xref: /aosp_15_r20/external/skia/gn/bazel_build.py (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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