1#!/usr/bin/env python3 2# 3# Copyright 2023 Google LLC 4# 5# Use of this source code is governed by a BSD-style license that can be 6# found in the LICENSE file. 7# 8# This script runs bazel build [target] [additional_arg] [additional_arg] 9# and then copies each of the specified outputs from the bazel-bin output 10# folder under the current working directory. This allows GN to shell out 11# to Bazel to generate or compile files, but still have them show up where 12# GN expects (in the output folder). 13# 14# The first argument passed in to this script is the Bazel target. 15# subsequent arguments are treated either as Bazel flags (if they start with 16# --) or output files to copy (if they do not). 17# 18# Output files may contain an equals sign (=), which means the first part is 19# the Bazel file to copy and the second part is where to copy it. If omitted, 20# the file will go immediately under the GN output directory. 21 22import hashlib 23import os 24import shutil 25import subprocess 26import sys 27 28target = sys.argv[1] 29outputs = {} 30bazel_args = [] 31for arg in sys.argv[2:]: 32 if arg.startswith("--"): 33 bazel_args.append(arg) 34 else: 35 if "=" in arg: 36 # "../../bazel-bin/src/ports/ffi.h=src/core/ffi.h" means to 37 # copy ../../bazel-bin/src/ports/ffi.h to 38 # $GN_OUTPUT/src/core/ffi.h 39 bazel_file, output_path = arg.split("=") 40 outputs[bazel_file] = output_path 41 else: 42 # "../../bazel-bin/src/ports/libstuff.a" means to copy 43 # ../../bazel-bin/src/ports/libstuff.a to 44 # $GN_OUTPUT/libstuff.a 45 outputs[arg] = os.path.basename(arg) 46 47print("Invoking bazelisk from ", os.getcwd()) 48# Forward the remaining args to the bazel invocation 49subprocess.run(["bazelisk", "build", target ] + bazel_args, check=True) 50 51for bazel_file, output_path in outputs.items(): 52 # GN expects files to be created underneath the output directory from which 53 # this script is invoked. 54 expected_output = os.path.join(os.getcwd(), output_path) 55 if not os.path.exists(expected_output): 56 shutil.copyfile(bazel_file, expected_output) 57 os.chmod(expected_output, 0o755) 58 else: 59 # GN uses timestamps to determine if it should re-build a file. If the 60 # two files match (via hash) we should not re-copy the output, which would 61 # re-trigger subsequent rebuilds. 62 created_hash = hashlib.sha256(open(bazel_file, 'rb').read()).hexdigest() 63 existing_hash = hashlib.sha256(open(expected_output, 'rb').read()).hexdigest() 64 if created_hash != existing_hash: 65 os.remove(expected_output) 66 shutil.copyfile(bazel_file, expected_output) 67 os.chmod(expected_output, 0o755) 68