|  | #!/usr/bin/env python3 | 
|  | # Copyright (C) 2020 The Android Open Source Project | 
|  | # | 
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | # you may not use this file except in compliance with the License. | 
|  | # You may obtain a copy of the License at | 
|  | # | 
|  | #      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | # | 
|  | # Unless required by applicable law or agreed to in writing, software | 
|  | # distributed under the License is distributed on an "AS IS" BASIS, | 
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | # See the License for the specific language governing permissions and | 
|  | # limitations under the License. | 
|  | """ | 
|  | Writes the perfetto_version{.gen.h, .ts} files. | 
|  |  | 
|  | This tool is run as part of a genrule from GN, SoonG and Bazel builds. It | 
|  | generates a source header (or in the case of --ts_out a TypeScript file) that | 
|  | contains: | 
|  | - The version number (e.g. v9.0) obtained parsing the CHANGELOG file. | 
|  | - The git HEAD's commit-ish (e.g. 6b330b772b0e973f79c70ba2e9bb2b0110c6715d) | 
|  | - The number of CLs from the release tag to HEAD. | 
|  |  | 
|  | The latter is concatenated to the version number to distinguish builds made | 
|  | fully from release tags (e.g., v9.0.0) vs builds made from the main branch which | 
|  | are N cls ahead of the latest monthly release (e.g., v9.0.42). | 
|  | """ | 
|  |  | 
|  | import argparse | 
|  | import os | 
|  | import re | 
|  | import sys | 
|  | import subprocess | 
|  |  | 
|  | # Note: PROJECT_ROOT is not accurate in bazel builds, where this script is | 
|  | # executed in the bazel sandbox. | 
|  | PROJECT_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) | 
|  | SCM_REV_NOT_AVAILABLE = 'N/A' | 
|  |  | 
|  |  | 
|  | def get_latest_release(changelog_path): | 
|  | """Returns a string like 'v9.0'. | 
|  |  | 
|  | It does so by searching the latest version mentioned in the CHANGELOG.""" | 
|  | if not changelog_path: | 
|  | if os.path.exists('CHANGELOG'): | 
|  | changelog_path = 'CHANGELOG' | 
|  | else: | 
|  | changelog_path = os.path.join(PROJECT_ROOT, 'CHANGELOG') | 
|  | with open(changelog_path) as f: | 
|  | for line in f.readlines(): | 
|  | m = re.match('^(v\d+[.]\d+)\s.*$', line) | 
|  | if m is not None: | 
|  | return m.group(1) | 
|  | raise Exception('Failed to fetch Perfetto version from %s' % changelog_path) | 
|  |  | 
|  |  | 
|  | def get_git_info(last_release_tag): | 
|  | """Returns a tuple ('deadbeef', '1234'). | 
|  |  | 
|  | The first value is the SHA1 of the HEAD. The second is the number of CLs from | 
|  | the passed |last_release_tag| to HEAD.""" | 
|  | commit_sha1 = SCM_REV_NOT_AVAILABLE | 
|  | commits_since_release = '' | 
|  | git_dir = os.path.join(PROJECT_ROOT, '.git') | 
|  | if os.path.exists(git_dir): | 
|  | try: | 
|  | commit_sha1 = subprocess.check_output(['git', 'rev-parse', 'HEAD'], | 
|  | cwd=PROJECT_ROOT).strip().decode() | 
|  | with open(os.devnull, 'wb') as devnull: | 
|  | commits_since_release = subprocess.check_output( | 
|  | [ | 
|  | 'git', 'rev-list', '--count', | 
|  | 'refs/tags/%s..HEAD' % last_release_tag | 
|  | ], | 
|  | cwd=PROJECT_ROOT, | 
|  | stderr=devnull).strip().decode() | 
|  | except subprocess.CalledProcessError: | 
|  | pass | 
|  |  | 
|  | return (commit_sha1, commits_since_release) | 
|  |  | 
|  |  | 
|  | def write_if_unchanged(path, content): | 
|  | prev_content = None | 
|  | if os.path.exists(path): | 
|  | with open(path, 'r') as fprev: | 
|  | prev_content = fprev.read() | 
|  | if prev_content == content: | 
|  | return 0 | 
|  | with open(path, 'w') as fout: | 
|  | fout.write(content) | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | parser = argparse.ArgumentParser() | 
|  | parser.add_argument( | 
|  | '--no_git', | 
|  | action='store_true', | 
|  | help='Skips running git rev-parse, emits only the version from CHANGELOG') | 
|  | parser.add_argument('--cpp_out', help='Path of the generated .h file.') | 
|  | parser.add_argument('--ts_out', help='Path of the generated .ts file.') | 
|  | parser.add_argument('--stdout', help='Write to stdout', action='store_true') | 
|  | parser.add_argument('--changelog', help='Path to CHANGELOG.') | 
|  | args = parser.parse_args() | 
|  |  | 
|  | release = get_latest_release(args.changelog) | 
|  | if args.no_git: | 
|  | git_sha1, commits_since_release = (SCM_REV_NOT_AVAILABLE, '') | 
|  | else: | 
|  | git_sha1, commits_since_release = get_git_info(release) | 
|  |  | 
|  | # Try to compute the number of commits since the last release. This can fail | 
|  | # in some environments (e.g. in android builds) because the bots pull only | 
|  | # the main branch and don't pull the whole list of tags. | 
|  | if commits_since_release: | 
|  | version = '%s.%s' % (release, commits_since_release)  # e.g., 'v9.0.42'. | 
|  | else: | 
|  | version = release  # e.g., 'v9.0'. | 
|  |  | 
|  | if args.cpp_out: | 
|  | guard = '%s_' % args.cpp_out.upper() | 
|  | guard = re.sub(r'[^\w]', '_', guard) | 
|  | lines = [] | 
|  | lines.append('// Generated by %s' % os.path.basename(__file__)) | 
|  | lines.append('') | 
|  | lines.append('#ifndef %s' % guard) | 
|  | lines.append('#define %s' % guard) | 
|  | lines.append('') | 
|  | lines.append('#define PERFETTO_VERSION_STRING() "%s"' % version) | 
|  | lines.append('#define PERFETTO_VERSION_SCM_REVISION() "%s"' % git_sha1) | 
|  | lines.append('') | 
|  | lines.append('#endif  // %s' % guard) | 
|  | lines.append('') | 
|  | content = '\n'.join(lines) | 
|  | write_if_unchanged(args.cpp_out, content) | 
|  |  | 
|  | if args.ts_out: | 
|  | lines = [] | 
|  | lines.append('export const VERSION = "%s";' % version) | 
|  | lines.append('export const SCM_REVISION = "%s";' % git_sha1) | 
|  | content = '\n'.join(lines) | 
|  | write_if_unchanged(args.ts_out, content) | 
|  |  | 
|  | if args.stdout: | 
|  | print(version) | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | sys.exit(main()) |