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