| #!/usr/bin/env python3 | |
| # Copyright 2018 The ANGLE Project Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| # Generate commit.h with git commit hash. | |
| # | |
| import pathlib | |
| import subprocess as sp | |
| import sys | |
| import os | |
| usage = """\ | |
| Usage: commit_id.py position - print commit position | |
| commit_id.py gen <output> [depfile] - generate commit.h""" | |
| def grab_output(command, cwd): | |
| return sp.Popen( | |
| command, stdout=sp.PIPE, shell=True, cwd=cwd).communicate()[0].strip().decode('utf-8') | |
| def get_git_dir(cwd): | |
| return grab_output('git rev-parse --git-dir', cwd) | |
| def get_git_common_dir(cwd): | |
| return grab_output('git rev-parse --git-common-dir', cwd) | |
| def get_commit_position(cwd): | |
| return grab_output('git rev-list HEAD --count', cwd) | |
| def does_git_dir_exist(cwd): | |
| ret = os.path.exists(os.path.join(cwd, '.git', 'HEAD')) | |
| # .git may be a file with a gitdir directive pointing elsewhere. | |
| if not ret and os.path.exists(os.path.join(cwd, '.git')): | |
| ret = 'true' == grab_output('git rev-parse --is-inside-work-tree', cwd) | |
| return ret | |
| def unpack_ref(ref_file, ref_file_full_path, packed_refs_full_path): | |
| with open(packed_refs_full_path) as fin: | |
| refs = fin.read().strip().split('\n') | |
| # Strip comments | |
| refs = [ref.split(' ') for ref in refs if ref.strip()[0] != '#'] | |
| # Parse lines (which are in the format <hash> <ref_file>) and find the input file | |
| refs = [git_hash for (git_hash, file_path) in refs if file_path == ref_file] | |
| assert (len(refs) == 1) | |
| git_hash = refs[0] | |
| with open(ref_file_full_path, 'w') as fout: | |
| fout.write(git_hash + '\n') | |
| # Get the files that GN action target angle_commit_id depends on. | |
| # If any of these files changed, the build system should rerun the commit_id.py | |
| # script to regenerate the angle_commit.h | |
| # Case 1: git config extensions.refStorage == file (or empty) | |
| # Case 1.1: .git/HEAD contains the hash. | |
| # Return .git/HEAD | |
| # Case 1.2: .git/HEAD contains non-hash: e.g. refs/heads/<branch_name> | |
| # Return .git/HEAD | |
| # Return .git/refs/heads/<branch_name> | |
| # Case 2: git config extensions.refStorage == reftable | |
| # In this case, HEAD will just store refs/heads/.invalid. If any reference is | |
| # updated, .git/reftable/table.list will be updated. | |
| # Return .git/reftable/table.list | |
| def get_git_inputs_and_maybe_unpack_ref(cwd): | |
| # check git extensions.refStorage type | |
| gitRefStorageType = grab_output('git config --get extensions.refStorage', cwd) | |
| isRefTableStorage = gitRefStorageType and gitRefStorageType == "reftable" | |
| git_dir = os.path.normpath(os.path.join(cwd, get_git_dir(cwd))) | |
| head_file = os.path.join(git_dir, 'HEAD') | |
| ret = [] | |
| # commit id should depend on angle's HEAD revision only if | |
| # extensions.refStorage = file. | |
| # If extensions.refStorage = reftable, .git/HEAD will always contains | |
| # "refs/heads/.invalid", and we can't rely on it as an indicator of whether | |
| # to rerun this script. | |
| if not isRefTableStorage: | |
| ret.append(head_file) | |
| git_common_dir = os.path.normpath(os.path.join(cwd, get_git_common_dir(cwd))) | |
| result = pathlib.Path(head_file).read_text().split() | |
| if result[0] == "ref:": | |
| # if the extensions.refStorage is reftable, add .git/reftable/tables.list as an input to gn action target | |
| if isRefTableStorage: | |
| ret.append(os.path.join(git_common_dir, 'reftable', 'tables.list')) | |
| else: | |
| # if the extensions.refStorage is file, add loose ref file pointed by HEAD | |
| ref_file = result[1] | |
| ref_file_full_path = os.path.join(git_common_dir, ref_file) | |
| if not os.path.exists(ref_file_full_path): | |
| packed_refs_full_path = os.path.join(git_common_dir, 'packed-refs') | |
| unpack_ref(ref_file, ref_file_full_path, packed_refs_full_path) | |
| ret.append(os.path.join(git_common_dir, ref_file)) | |
| return ret | |
| def main(): | |
| if len(sys.argv) < 2: | |
| sys.exit(usage) | |
| operation = sys.argv[1] | |
| # Set the root of ANGLE's repo as the working directory | |
| aosp_angle_path = os.path.join(os.path.dirname('.'), 'external', 'angle') | |
| aosp = os.path.exists(aosp_angle_path) | |
| cwd = aosp_angle_path if aosp else os.path.join( | |
| os.path.dirname(os.path.abspath(__file__)), '..') | |
| git_dir_exists = does_git_dir_exist(cwd) | |
| if operation == 'position': | |
| if git_dir_exists: | |
| print(get_commit_position(cwd)) | |
| else: | |
| print("0") | |
| sys.exit(0) | |
| if len(sys.argv) < 3 or operation != 'gen': | |
| sys.exit(usage) | |
| output_file = sys.argv[2] | |
| depfile = sys.argv[3] if len(sys.argv) == 4 else None | |
| commit_id_size = 12 | |
| commit_date = 'unknown date' | |
| commit_position = '0' | |
| # If the ANGLE_UPSTREAM_HASH environment variable is set, use it as | |
| # commit_id. commit_date will be 'unknown date' and commit_position will be 0 | |
| # in this case. See details in roll_aosp.sh where commit_id.py is invoked. | |
| commit_id = os.environ.get('ANGLE_UPSTREAM_HASH') | |
| # If ANGLE_UPSTREAM_HASH environment variable is not set, use the git command | |
| # to get the git hash, when .git is available | |
| if git_dir_exists and not commit_id: | |
| try: | |
| commit_id = grab_output('git rev-parse --short=%d HEAD' % commit_id_size, cwd) | |
| commit_date = grab_output('git show -s --format=%ci HEAD', cwd) or commit_date | |
| commit_position = get_commit_position(cwd) or commit_position | |
| except: | |
| pass | |
| with open(output_file, 'w') as hfile: | |
| hfile.write('#define ANGLE_COMMIT_HASH "%s"\n' % (commit_id or "unknown hash")) | |
| hfile.write('#define ANGLE_COMMIT_HASH_SIZE %d\n' % commit_id_size) | |
| hfile.write('#define ANGLE_COMMIT_DATE "%s"\n' % commit_date) | |
| hfile.write('#define ANGLE_COMMIT_POSITION %s\n' % commit_position) | |
| if depfile: | |
| inputs = [] | |
| if git_dir_exists: | |
| inputs = get_git_inputs_and_maybe_unpack_ref(cwd) | |
| with open(depfile, 'w') as f: | |
| f.write(output_file) | |
| f.write(': ') | |
| f.write(' '.join(os.path.relpath(p) for p in inputs)) | |
| f.write('\n') | |
| if __name__ == '__main__': | |
| main() |