#!/usr/bin/env python3
#
# Copyright 2023 Google LLC
#
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# This script runs bazel build [target] [additional_arg] [additional_arg]
# and then copies each of the specified outputs from the bazel-bin output
# folder under the current working directory. This allows GN to shell out
# to Bazel to generate or compile files, but still have them show up where
# GN expects (in the output folder).
#
# The first argument passed in to this script is the Bazel target.
# subsequent arguments are treated either as Bazel flags (if they start with
# --) or output files to copy (if they do not).
#
# Output files may contain an equals sign (=), which means the first part is
# the Bazel file to copy and the second part is where to copy it. If omitted,
# the file will go immediately under the GN output directory.

import hashlib
import os
import shutil
import subprocess
import sys

# Separate targets (starting with // or @) from outputs and flags
targets = []
outputs = {}
bazel_args = []
for arg in sys.argv[1:]:
    if arg.startswith("//") or arg.startswith("@"):
        targets.append(arg)
    elif arg.startswith("--"):
        bazel_args.append(arg)
    else:
        if "=" in arg:
            # "../../bazel-bin/src/ports/ffi.h=src/core/ffi.h" means to
            # copy ../../bazel-bin/src/ports/ffi.h to
            # $GN_OUTPUT/src/core/ffi.h
            bazel_file, output_path = arg.split("=")
            outputs[bazel_file] = output_path
        else:
            # "../../bazel-bin/src/ports/libstuff.a" means to copy
            # ../../bazel-bin/src/ports/libstuff.a to
            # $GN_OUTPUT/libstuff.a
            outputs[arg] = os.path.basename(arg)

print("Invoking bazelisk from ", os.getcwd())
# Build all targets in a single invocation
subprocess.run(["bazelisk", "build"] + targets + bazel_args, check=True)

for bazel_file, output_path in outputs.items():
    # GN expects files to be created underneath the output directory from which
    # this script is invoked.
    expected_output = os.path.join(os.getcwd(), output_path)
    if not os.path.exists(expected_output):
        shutil.copyfile(bazel_file, expected_output)
        os.chmod(expected_output, 0o755)
    else:
        # GN uses timestamps to determine if it should re-build a file. If the
        # two files match (via hash) we should not re-copy the output, which would
        # re-trigger subsequent rebuilds.
        created_hash = hashlib.sha256(open(bazel_file, 'rb').read()).hexdigest()
        existing_hash = hashlib.sha256(open(expected_output, 'rb').read()).hexdigest()
        if created_hash != existing_hash:
            os.remove(expected_output)
            shutil.copyfile(bazel_file, expected_output)
            os.chmod(expected_output, 0o755)
