1*760c253cSXin Li#!/usr/bin/env python3 2*760c253cSXin Li# Copyright 2019 The ChromiumOS Authors 3*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be 4*760c253cSXin Li# found in the LICENSE file. 5*760c253cSXin Li 6*760c253cSXin Li"""Build script that builds a binary from a bundle.""" 7*760c253cSXin Li 8*760c253cSXin Liimport argparse 9*760c253cSXin Liimport os.path 10*760c253cSXin Liimport re 11*760c253cSXin Liimport subprocess 12*760c253cSXin Liimport sys 13*760c253cSXin Li 14*760c253cSXin Li 15*760c253cSXin Lidef parse_args(): 16*760c253cSXin Li parser = argparse.ArgumentParser() 17*760c253cSXin Li parser.add_argument( 18*760c253cSXin Li "--config", 19*760c253cSXin Li required=True, 20*760c253cSXin Li choices=["cros.hardened", "cros.nonhardened", "cros.host", "android"], 21*760c253cSXin Li ) 22*760c253cSXin Li parser.add_argument( 23*760c253cSXin Li "--use_ccache", required=True, choices=["true", "false"] 24*760c253cSXin Li ) 25*760c253cSXin Li parser.add_argument( 26*760c253cSXin Li "--use_llvm_next", required=True, choices=["true", "false"] 27*760c253cSXin Li ) 28*760c253cSXin Li parser.add_argument("--output_file", required=True, type=str) 29*760c253cSXin Li parser.add_argument( 30*760c253cSXin Li "--static", 31*760c253cSXin Li choices=["true", "false"], 32*760c253cSXin Li help="If true, produce a static wrapper. Autodetects a good value if " 33*760c253cSXin Li "unspecified.", 34*760c253cSXin Li ) 35*760c253cSXin Li 36*760c253cSXin Li version_args = parser.add_mutually_exclusive_group() 37*760c253cSXin Li version_args.add_argument( 38*760c253cSXin Li "--version", 39*760c253cSXin Li help=""" 40*760c253cSXin Li A string to pass to `go` that instructs the compiler wrapper about what 41*760c253cSXin Li version to print. Automatically selects the current git commit SHA if 42*760c253cSXin Li this is left unspecified. 43*760c253cSXin Li """, 44*760c253cSXin Li ) 45*760c253cSXin Li parser.add_argument( 46*760c253cSXin Li "--version_suffix", 47*760c253cSXin Li help=""" 48*760c253cSXin Li A string appended to the **computed** version of the wrapper. This is 49*760c253cSXin Li appended directly without any delimiter. Incompatible with 50*760c253cSXin Li `--version`. 51*760c253cSXin Li """, 52*760c253cSXin Li ) 53*760c253cSXin Li args = parser.parse_args() 54*760c253cSXin Li 55*760c253cSXin Li if args.static is None: 56*760c253cSXin Li args.static = "cros" not in args.config 57*760c253cSXin Li else: 58*760c253cSXin Li args.static = args.static == "true" 59*760c253cSXin Li 60*760c253cSXin Li return args 61*760c253cSXin Li 62*760c253cSXin Li 63*760c253cSXin Lidef calc_go_args(args, version, build_dir, output_file): 64*760c253cSXin Li # These seem unnecessary, and might lead to breakages with Go's ldflag 65*760c253cSXin Li # parsing. Don't allow them. 66*760c253cSXin Li if "'" in version: 67*760c253cSXin Li raise ValueError("`version` should not contain single quotes") 68*760c253cSXin Li 69*760c253cSXin Li ldFlags = [ 70*760c253cSXin Li "-X", 71*760c253cSXin Li "main.ConfigName=" + args.config, 72*760c253cSXin Li "-X", 73*760c253cSXin Li "main.UseCCache=" + args.use_ccache, 74*760c253cSXin Li "-X", 75*760c253cSXin Li "main.UseLlvmNext=" + args.use_llvm_next, 76*760c253cSXin Li "-X", 77*760c253cSXin Li # Quote this, as `version` may have spaces in it. 78*760c253cSXin Li "'main.Version=" + version + "'", 79*760c253cSXin Li ] 80*760c253cSXin Li 81*760c253cSXin Li # If the wrapper is intended for ChromeOS, we need to use libc's exec. 82*760c253cSXin Li extra_args = [] 83*760c253cSXin Li if not args.static: 84*760c253cSXin Li extra_args += ["-tags", "libc_exec"] 85*760c253cSXin Li 86*760c253cSXin Li if args.config == "android": 87*760c253cSXin Li # If android_llvm_next_flags.go DNE, we'll get an obscure "no 88*760c253cSXin Li # llvmNextFlags" build error; complaining here is clearer. 89*760c253cSXin Li if not os.path.exists( 90*760c253cSXin Li os.path.join(build_dir, "android_llvm_next_flags.go") 91*760c253cSXin Li ): 92*760c253cSXin Li sys.exit( 93*760c253cSXin Li "In order to build the Android wrapper, you must have a local " 94*760c253cSXin Li "android_llvm_next_flags.go file; please see " 95*760c253cSXin Li "cros_llvm_next_flags.go." 96*760c253cSXin Li ) 97*760c253cSXin Li extra_args += ["-tags", "android_llvm_next_flags"] 98*760c253cSXin Li 99*760c253cSXin Li return [ 100*760c253cSXin Li "go", 101*760c253cSXin Li "build", 102*760c253cSXin Li "-o", 103*760c253cSXin Li output_file, 104*760c253cSXin Li "-ldflags", 105*760c253cSXin Li " ".join(ldFlags), 106*760c253cSXin Li ] + extra_args 107*760c253cSXin Li 108*760c253cSXin Li 109*760c253cSXin Lidef read_version(build_dir): 110*760c253cSXin Li version_path = os.path.join(build_dir, "VERSION") 111*760c253cSXin Li if os.path.exists(version_path): 112*760c253cSXin Li with open(version_path, "r", encoding="utf-8") as r: 113*760c253cSXin Li return r.read() 114*760c253cSXin Li 115*760c253cSXin Li last_commit_msg = subprocess.check_output( 116*760c253cSXin Li ["git", "-C", build_dir, "log", "-1", "--pretty=%B"], encoding="utf-8" 117*760c253cSXin Li ) 118*760c253cSXin Li # Use last found change id to support reverts as well. 119*760c253cSXin Li change_ids = re.findall(r"Change-Id: (\w+)", last_commit_msg) 120*760c253cSXin Li if not change_ids: 121*760c253cSXin Li sys.exit("Couldn't find Change-Id in last commit message.") 122*760c253cSXin Li return change_ids[-1] 123*760c253cSXin Li 124*760c253cSXin Li 125*760c253cSXin Lidef main(): 126*760c253cSXin Li args = parse_args() 127*760c253cSXin Li build_dir = os.path.dirname(__file__) 128*760c253cSXin Li 129*760c253cSXin Li if args.version: 130*760c253cSXin Li version = args.version 131*760c253cSXin Li else: 132*760c253cSXin Li version = read_version(build_dir) 133*760c253cSXin Li if args.version_suffix: 134*760c253cSXin Li version += args.version_suffix 135*760c253cSXin Li 136*760c253cSXin Li # Note: Go does not support using absolute package names. 137*760c253cSXin Li # So we run go inside the directory of the the build file. 138*760c253cSXin Li output_file = os.path.abspath(args.output_file) 139*760c253cSXin Li subprocess.check_call( 140*760c253cSXin Li calc_go_args(args, version, build_dir, output_file), cwd=build_dir 141*760c253cSXin Li ) 142*760c253cSXin Li 143*760c253cSXin Li # b/203821449: we're occasionally seeing very small (and non-functional) 144*760c253cSXin Li # compiler-wrapper binaries on SDK builds. To help narrow down why, add a 145*760c253cSXin Li # size check here. Locally, the wrapper is 1.9MB, so warning on <1MB 146*760c253cSXin Li # shouldn't flag false-positives. 147*760c253cSXin Li size = os.path.getsize(output_file) 148*760c253cSXin Li min_size_bytes = 1024 * 1024 149*760c253cSXin Li if size < min_size_bytes: 150*760c253cSXin Li raise ValueError( 151*760c253cSXin Li f"Compiler wrapper is {size:,} bytes; expected at " 152*760c253cSXin Li f"least {min_size_bytes:,}" 153*760c253cSXin Li ) 154*760c253cSXin Li 155*760c253cSXin Li 156*760c253cSXin Liif __name__ == "__main__": 157*760c253cSXin Li main() 158