| #!/usr/bin/env python3 |
| # Copyright 2019 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Build script that builds a binary from a bundle.""" |
| |
| import argparse |
| import os.path |
| import re |
| import subprocess |
| import sys |
| |
| |
| def parse_args(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument( |
| "--config", |
| required=True, |
| choices=["cros.hardened", "cros.nonhardened", "cros.host", "android"], |
| ) |
| parser.add_argument( |
| "--use_ccache", required=True, choices=["true", "false"] |
| ) |
| parser.add_argument( |
| "--use_llvm_next", required=True, choices=["true", "false"] |
| ) |
| parser.add_argument("--output_file", required=True, type=str) |
| parser.add_argument( |
| "--static", |
| choices=["true", "false"], |
| help="If true, produce a static wrapper. Autodetects a good value if " |
| "unspecified.", |
| ) |
| |
| version_args = parser.add_mutually_exclusive_group() |
| version_args.add_argument( |
| "--version", |
| help=""" |
| A string to pass to `go` that instructs the compiler wrapper about what |
| version to print. Automatically selects the current git commit SHA if |
| this is left unspecified. |
| """, |
| ) |
| parser.add_argument( |
| "--version_suffix", |
| help=""" |
| A string appended to the **computed** version of the wrapper. This is |
| appended directly without any delimiter. Incompatible with |
| `--version`. |
| """, |
| ) |
| args = parser.parse_args() |
| |
| if args.static is None: |
| args.static = "cros" not in args.config |
| else: |
| args.static = args.static == "true" |
| |
| return args |
| |
| |
| def calc_go_args(args, version, build_dir, output_file): |
| # These seem unnecessary, and might lead to breakages with Go's ldflag |
| # parsing. Don't allow them. |
| if "'" in version: |
| raise ValueError("`version` should not contain single quotes") |
| |
| ldFlags = [ |
| "-X", |
| "main.ConfigName=" + args.config, |
| "-X", |
| "main.UseCCache=" + args.use_ccache, |
| "-X", |
| "main.UseLlvmNext=" + args.use_llvm_next, |
| "-X", |
| # Quote this, as `version` may have spaces in it. |
| "'main.Version=" + version + "'", |
| ] |
| |
| # If the wrapper is intended for ChromeOS, we need to use libc's exec. |
| extra_args = [] |
| if not args.static: |
| extra_args += ["-tags", "libc_exec"] |
| |
| if args.config == "android": |
| # If android_llvm_next_flags.go DNE, we'll get an obscure "no |
| # llvmNextFlags" build error; complaining here is clearer. |
| if not os.path.exists( |
| os.path.join(build_dir, "android_llvm_next_flags.go") |
| ): |
| sys.exit( |
| "In order to build the Android wrapper, you must have a local " |
| "android_llvm_next_flags.go file; please see " |
| "cros_llvm_next_flags.go." |
| ) |
| extra_args += ["-tags", "android_llvm_next_flags"] |
| |
| return [ |
| "go", |
| "build", |
| "-o", |
| output_file, |
| "-ldflags", |
| " ".join(ldFlags), |
| ] + extra_args |
| |
| |
| def read_version(build_dir): |
| version_path = os.path.join(build_dir, "VERSION") |
| if os.path.exists(version_path): |
| with open(version_path, "r", encoding="utf-8") as r: |
| return r.read() |
| |
| last_commit_msg = subprocess.check_output( |
| ["git", "-C", build_dir, "log", "-1", "--pretty=%B"], encoding="utf-8" |
| ) |
| # Use last found change id to support reverts as well. |
| change_ids = re.findall(r"Change-Id: (\w+)", last_commit_msg) |
| if not change_ids: |
| sys.exit("Couldn't find Change-Id in last commit message.") |
| return change_ids[-1] |
| |
| |
| def main(): |
| args = parse_args() |
| build_dir = os.path.dirname(__file__) |
| |
| if args.version: |
| version = args.version |
| else: |
| version = read_version(build_dir) |
| if args.version_suffix: |
| version += args.version_suffix |
| |
| # Note: Go does not support using absolute package names. |
| # So we run go inside the directory of the the build file. |
| output_file = os.path.abspath(args.output_file) |
| subprocess.check_call( |
| calc_go_args(args, version, build_dir, output_file), cwd=build_dir |
| ) |
| |
| # b/203821449: we're occasionally seeing very small (and non-functional) |
| # compiler-wrapper binaries on SDK builds. To help narrow down why, add a |
| # size check here. Locally, the wrapper is 1.9MB, so warning on <1MB |
| # shouldn't flag false-positives. |
| size = os.path.getsize(output_file) |
| min_size_bytes = 1024 * 1024 |
| if size < min_size_bytes: |
| raise ValueError( |
| f"Compiler wrapper is {size:,} bytes; expected at " |
| f"least {min_size_bytes:,}" |
| ) |
| |
| |
| if __name__ == "__main__": |
| main() |