| #!/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()) |