| #!/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 latter is concatenated to the version number to disambiguate builds made | |
| from release tags vs builds made from the main branch vs UI builds made from the | |
| ui-canary/ui-stable branch. | |
| """ | |
| 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_sha1(commitish): | |
| """Returns the SHA1 of the provided commit-ish""" | |
| commit_sha1 = SCM_REV_NOT_AVAILABLE | |
| git_dir = os.path.join(PROJECT_ROOT, '.git') | |
| if os.path.exists(git_dir): | |
| try: | |
| commit_sha1 = subprocess.check_output(['git', 'rev-parse', commitish], | |
| cwd=PROJECT_ROOT).strip().decode() | |
| except subprocess.CalledProcessError: | |
| pass | |
| return commit_sha1 | |
| 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('--check_git', action='store_true') | |
| 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() | |
| if args.check_git: | |
| has_git = os.path.exists(os.path.join(PROJECT_ROOT, '.git', 'HEAD')) | |
| print('1' if has_git else '0') | |
| return 0 | |
| release = get_latest_release(args.changelog) | |
| if args.no_git: | |
| head_sha1 = SCM_REV_NOT_AVAILABLE | |
| else: | |
| head_sha1 = get_git_sha1('HEAD') # SCM_REV_NOT_AVAILABLE on failure. | |
| if head_sha1 == SCM_REV_NOT_AVAILABLE: | |
| version = release # e.g., 'v9.0'. | |
| else: | |
| sha1_abbrev = head_sha1[:9] | |
| version = f'{release}-{sha1_abbrev}' # e.g., 'v9.0-adeadbeef'. | |
| 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"' % head_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";' % head_sha1) | |
| content = '\n'.join(lines) | |
| write_if_unchanged(args.ts_out, content) | |
| if args.stdout: | |
| print(version) | |
| if __name__ == '__main__': | |
| sys.exit(main()) |