1*90c8c64dSAndroid Build Coastguard Worker# 2*90c8c64dSAndroid Build Coastguard Worker# Copyright (C) 2021 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker# 4*90c8c64dSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker# 8*90c8c64dSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker# 10*90c8c64dSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker# limitations under the License. 15*90c8c64dSAndroid Build Coastguard Worker# 16*90c8c64dSAndroid Build Coastguard Worker"""APIs for interacting with Soong.""" 17*90c8c64dSAndroid Build Coastguard Workerimport logging 18*90c8c64dSAndroid Build Coastguard Workerimport os 19*90c8c64dSAndroid Build Coastguard Workerfrom pathlib import Path 20*90c8c64dSAndroid Build Coastguard Workerimport shlex 21*90c8c64dSAndroid Build Coastguard Workerimport shutil 22*90c8c64dSAndroid Build Coastguard Workerimport subprocess 23*90c8c64dSAndroid Build Coastguard Worker 24*90c8c64dSAndroid Build Coastguard Worker 25*90c8c64dSAndroid Build Coastguard Workerdef logger() -> logging.Logger: 26*90c8c64dSAndroid Build Coastguard Worker """Returns the module level logger.""" 27*90c8c64dSAndroid Build Coastguard Worker return logging.getLogger(__name__) 28*90c8c64dSAndroid Build Coastguard Worker 29*90c8c64dSAndroid Build Coastguard Worker 30*90c8c64dSAndroid Build Coastguard Workerclass Soong: 31*90c8c64dSAndroid Build Coastguard Worker """Interface for interacting with Soong.""" 32*90c8c64dSAndroid Build Coastguard Worker 33*90c8c64dSAndroid Build Coastguard Worker def __init__(self, build_top: Path, out_dir: Path) -> None: 34*90c8c64dSAndroid Build Coastguard Worker self.out_dir = out_dir 35*90c8c64dSAndroid Build Coastguard Worker self.soong_ui_path = build_top / "build/soong/soong_ui.bash" 36*90c8c64dSAndroid Build Coastguard Worker 37*90c8c64dSAndroid Build Coastguard Worker def soong_ui( 38*90c8c64dSAndroid Build Coastguard Worker self, 39*90c8c64dSAndroid Build Coastguard Worker args: list[str], 40*90c8c64dSAndroid Build Coastguard Worker env: dict[str, str] | None = None, 41*90c8c64dSAndroid Build Coastguard Worker capture_output: bool = False, 42*90c8c64dSAndroid Build Coastguard Worker ) -> str: 43*90c8c64dSAndroid Build Coastguard Worker """Executes soong_ui.bash and returns the output on success. 44*90c8c64dSAndroid Build Coastguard Worker 45*90c8c64dSAndroid Build Coastguard Worker Args: 46*90c8c64dSAndroid Build Coastguard Worker args: List of string arguments to pass to soong_ui.bash. 47*90c8c64dSAndroid Build Coastguard Worker env: Additional environment variables to set when running soong_ui. 48*90c8c64dSAndroid Build Coastguard Worker capture_output: True if the output of the command should be captured and 49*90c8c64dSAndroid Build Coastguard Worker returned. If not, the output will be printed to stdout/stderr and an 50*90c8c64dSAndroid Build Coastguard Worker empty string will be returned. 51*90c8c64dSAndroid Build Coastguard Worker 52*90c8c64dSAndroid Build Coastguard Worker Raises: 53*90c8c64dSAndroid Build Coastguard Worker subprocess.CalledProcessError: The subprocess failure if the soong command 54*90c8c64dSAndroid Build Coastguard Worker failed. 55*90c8c64dSAndroid Build Coastguard Worker 56*90c8c64dSAndroid Build Coastguard Worker Returns: 57*90c8c64dSAndroid Build Coastguard Worker The interleaved contents of stdout/stderr if capture_output is True, else an 58*90c8c64dSAndroid Build Coastguard Worker empty string. 59*90c8c64dSAndroid Build Coastguard Worker """ 60*90c8c64dSAndroid Build Coastguard Worker if env is None: 61*90c8c64dSAndroid Build Coastguard Worker env = {} 62*90c8c64dSAndroid Build Coastguard Worker 63*90c8c64dSAndroid Build Coastguard Worker # Use a (mostly) clean environment to avoid the caller's lunch 64*90c8c64dSAndroid Build Coastguard Worker # environment affecting the build. 65*90c8c64dSAndroid Build Coastguard Worker exec_env = { 66*90c8c64dSAndroid Build Coastguard Worker # Newer versions of golang require the go cache, which defaults to somewhere 67*90c8c64dSAndroid Build Coastguard Worker # in HOME if not set. 68*90c8c64dSAndroid Build Coastguard Worker "HOME": os.environ["HOME"], 69*90c8c64dSAndroid Build Coastguard Worker "OUT_DIR": str(self.out_dir.resolve()), 70*90c8c64dSAndroid Build Coastguard Worker "PATH": os.environ["PATH"], 71*90c8c64dSAndroid Build Coastguard Worker } 72*90c8c64dSAndroid Build Coastguard Worker exec_env.update(env) 73*90c8c64dSAndroid Build Coastguard Worker env_prefix = " ".join(f"{k}={v}" for k, v in exec_env.items()) 74*90c8c64dSAndroid Build Coastguard Worker cmd = [str(self.soong_ui_path)] + args 75*90c8c64dSAndroid Build Coastguard Worker logger().debug(f"running in {os.getcwd()}: {env_prefix} {shlex.join(cmd)}") 76*90c8c64dSAndroid Build Coastguard Worker result = subprocess.run( 77*90c8c64dSAndroid Build Coastguard Worker cmd, 78*90c8c64dSAndroid Build Coastguard Worker check=True, 79*90c8c64dSAndroid Build Coastguard Worker capture_output=capture_output, 80*90c8c64dSAndroid Build Coastguard Worker encoding="utf-8", 81*90c8c64dSAndroid Build Coastguard Worker env=exec_env, 82*90c8c64dSAndroid Build Coastguard Worker ) 83*90c8c64dSAndroid Build Coastguard Worker return result.stdout 84*90c8c64dSAndroid Build Coastguard Worker 85*90c8c64dSAndroid Build Coastguard Worker def get_make_var(self, name: str) -> str: 86*90c8c64dSAndroid Build Coastguard Worker """Queries the build system for the value of a make variable. 87*90c8c64dSAndroid Build Coastguard Worker 88*90c8c64dSAndroid Build Coastguard Worker Args: 89*90c8c64dSAndroid Build Coastguard Worker name: The name of the build variable to query. 90*90c8c64dSAndroid Build Coastguard Worker 91*90c8c64dSAndroid Build Coastguard Worker Returns: 92*90c8c64dSAndroid Build Coastguard Worker The value of the build variable in string form. 93*90c8c64dSAndroid Build Coastguard Worker """ 94*90c8c64dSAndroid Build Coastguard Worker return self.soong_ui(["--dumpvar-mode", name], capture_output=True).rstrip("\n") 95*90c8c64dSAndroid Build Coastguard Worker 96*90c8c64dSAndroid Build Coastguard Worker def clean(self) -> None: 97*90c8c64dSAndroid Build Coastguard Worker """Removes the output directory, if it exists.""" 98*90c8c64dSAndroid Build Coastguard Worker if self.out_dir.exists(): 99*90c8c64dSAndroid Build Coastguard Worker shutil.rmtree(self.out_dir) 100*90c8c64dSAndroid Build Coastguard Worker 101*90c8c64dSAndroid Build Coastguard Worker def build(self, targets: list[str], env: dict[str, str] | None = None) -> None: 102*90c8c64dSAndroid Build Coastguard Worker """Builds the given targets. 103*90c8c64dSAndroid Build Coastguard Worker 104*90c8c64dSAndroid Build Coastguard Worker The out directory will be created if it does not already exist. Existing 105*90c8c64dSAndroid Build Coastguard Worker contents will not be removed, but affected outputs will be modified. 106*90c8c64dSAndroid Build Coastguard Worker 107*90c8c64dSAndroid Build Coastguard Worker Args: 108*90c8c64dSAndroid Build Coastguard Worker targets: A list of target names to build. 109*90c8c64dSAndroid Build Coastguard Worker env: Additional environment variables to set when running the build. 110*90c8c64dSAndroid Build Coastguard Worker """ 111*90c8c64dSAndroid Build Coastguard Worker self.out_dir.mkdir(parents=True, exist_ok=True) 112*90c8c64dSAndroid Build Coastguard Worker self.soong_ui(["--make-mode", "--soong-only"] + targets, env=env) 113