Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | |
| 3 | # Copyright 2021 Google, Inc. |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at: |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | """ Build BT targets on the host system. |
| 17 | |
| 18 | For building, you will first have to stage a platform directory that has the |
| 19 | following structure: |
| 20 | |-common-mk |
| 21 | |-bt |
| 22 | |-external |
| 23 | |-|-rust |
| 24 | |-|-|-vendor |
| 25 | |
| 26 | The simplest way to do this is to check out platform2 to another directory (that |
| 27 | is not a subdir of this bt directory), symlink bt there and symlink the rust |
| 28 | vendor repository as well. |
| 29 | """ |
| 30 | import argparse |
| 31 | import multiprocessing |
| 32 | import os |
Andre Braga | aa11e7d | 2022-08-10 21:46:44 +0000 | [diff] [blame] | 33 | import platform |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 34 | import shutil |
| 35 | import six |
| 36 | import subprocess |
| 37 | import sys |
Abhishek Pandit-Subedi | a1a94d1 | 2021-11-18 13:14:04 -0800 | [diff] [blame] | 38 | import tarfile |
Chris Manton | e7ad633 | 2021-09-30 22:55:39 -0700 | [diff] [blame] | 39 | import time |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 40 | |
| 41 | # Use flags required by common-mk (find -type f | grep -nE 'use[.]' {}) |
| 42 | COMMON_MK_USES = [ |
| 43 | 'asan', |
| 44 | 'coverage', |
| 45 | 'cros_host', |
Abhishek Pandit-Subedi | 461eeab | 2023-01-09 10:20:17 -0800 | [diff] [blame] | 46 | 'cros_debug', |
JohnLai | 762f820 | 2023-09-18 18:53:16 +0800 | [diff] [blame] | 47 | 'floss_rootcanal', |
| 48 | 'function_elimination_experiment', |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 49 | 'fuzzer', |
| 50 | 'fuzzer', |
JohnLai | 762f820 | 2023-09-18 18:53:16 +0800 | [diff] [blame] | 51 | 'lto_experiment', |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 52 | 'msan', |
| 53 | 'profiling', |
JohnLai | 762f820 | 2023-09-18 18:53:16 +0800 | [diff] [blame] | 54 | 'proto_force_optimize_speed', |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 55 | 'tcmalloc', |
| 56 | 'test', |
| 57 | 'ubsan', |
| 58 | ] |
| 59 | |
Abhishek Pandit-Subedi | 461eeab | 2023-01-09 10:20:17 -0800 | [diff] [blame] | 60 | # Use a specific commit version for common-mk to avoid build surprises. |
JohnLai | 762f820 | 2023-09-18 18:53:16 +0800 | [diff] [blame] | 61 | COMMON_MK_COMMIT = "d014d561eaf5ece08166edd98b10c145ef81312d" |
Abhishek Pandit-Subedi | 461eeab | 2023-01-09 10:20:17 -0800 | [diff] [blame] | 62 | |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 63 | # Default use flags. |
| 64 | USE_DEFAULTS = { |
| 65 | 'android': False, |
| 66 | 'bt_nonstandard_codecs': False, |
| 67 | 'test': False, |
| 68 | } |
| 69 | |
| 70 | VALID_TARGETS = [ |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 71 | 'all', # All targets except test and clean |
Abhishek Pandit-Subedi | a04c74c | 2024-08-01 09:24:34 -0700 | [diff] [blame] | 72 | 'bloat', # Check bloat of crates |
Abhishek Pandit-Subedi | 9476414 | 2022-02-07 17:18:00 -0800 | [diff] [blame] | 73 | 'clean', # Clean up output directory |
| 74 | 'docs', # Build Rust docs |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 75 | 'hosttools', # Build the host tools (i.e. packetgen) |
Abhishek Pandit-Subedi | 9476414 | 2022-02-07 17:18:00 -0800 | [diff] [blame] | 76 | 'main', # Build the main C++ codebase |
| 77 | 'prepare', # Prepare the output directory (gn gen + rust setup) |
| 78 | 'rust', # Build only the rust components + copy artifacts to output dir |
| 79 | 'test', # Run the unit tests |
Hsin-chen Chuang | bcd7b63 | 2024-06-07 01:07:08 +0800 | [diff] [blame] | 80 | 'clippy', # Run cargo clippy |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 81 | 'utils', # Build Floss utils |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 82 | ] |
| 83 | |
Abhishek Pandit-Subedi | 70e4304 | 2021-06-10 21:16:52 +0000 | [diff] [blame] | 84 | # TODO(b/190750167) - Host tests are disabled until we are full bazel build |
Abhishek Pandit-Subedi | d801b12 | 2021-04-10 00:41:07 +0000 | [diff] [blame] | 85 | HOST_TESTS = [ |
Abhishek Pandit-Subedi | 70e4304 | 2021-06-10 21:16:52 +0000 | [diff] [blame] | 86 | # 'bluetooth_test_common', |
| 87 | # 'bluetoothtbd_test', |
| 88 | # 'net_test_avrcp', |
| 89 | # 'net_test_btcore', |
| 90 | # 'net_test_types', |
| 91 | # 'net_test_btm_iso', |
| 92 | # 'net_test_btpackets', |
Abhishek Pandit-Subedi | d801b12 | 2021-04-10 00:41:07 +0000 | [diff] [blame] | 93 | ] |
| 94 | |
Abhishek Pandit-Subedi | 461eeab | 2023-01-09 10:20:17 -0800 | [diff] [blame] | 95 | # Map of git repos to bootstrap and what commit to check them out at. None |
| 96 | # values will just checkout to HEAD. |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 97 | BOOTSTRAP_GIT_REPOS = { |
Abhishek Pandit-Subedi | 461eeab | 2023-01-09 10:20:17 -0800 | [diff] [blame] | 98 | 'platform2': ('https://chromium.googlesource.com/chromiumos/platform2', COMMON_MK_COMMIT), |
| 99 | 'rust_crates': ('https://chromium.googlesource.com/chromiumos/third_party/rust_crates', None), |
| 100 | 'proto_logging': ('https://android.googlesource.com/platform/frameworks/proto_logging', None), |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 101 | } |
| 102 | |
| 103 | # List of packages required for linux build |
| 104 | REQUIRED_APT_PACKAGES = [ |
| 105 | 'bison', |
| 106 | 'build-essential', |
| 107 | 'curl', |
| 108 | 'debmake', |
| 109 | 'flatbuffers-compiler', |
| 110 | 'flex', |
| 111 | 'g++-multilib', |
| 112 | 'gcc-multilib', |
| 113 | 'generate-ninja', |
| 114 | 'gnupg', |
| 115 | 'gperf', |
Yun-Hao Chung | 69db9ce | 2023-09-08 11:50:50 +0000 | [diff] [blame] | 116 | 'libabsl-dev', |
Martin Brabham | ba22adf | 2022-02-04 19:51:21 +0000 | [diff] [blame] | 117 | 'libc++abi-dev', |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 118 | 'libc++-dev', |
| 119 | 'libdbus-1-dev', |
Martin Brabham | 996f150 | 2022-02-14 17:39:23 +0000 | [diff] [blame] | 120 | 'libdouble-conversion-dev', |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 121 | 'libevent-dev', |
| 122 | 'libevent-dev', |
| 123 | 'libflatbuffers-dev', |
David Duarte | f11fe88 | 2023-02-22 21:27:34 +0000 | [diff] [blame] | 124 | 'libfmt-dev', |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 125 | 'libgl1-mesa-dev', |
| 126 | 'libglib2.0-dev', |
Martin Brabham | 996f150 | 2022-02-14 17:39:23 +0000 | [diff] [blame] | 127 | 'libgtest-dev', |
| 128 | 'libgmock-dev', |
Yun-Hao Chung | 69db9ce | 2023-09-08 11:50:50 +0000 | [diff] [blame] | 129 | 'liblc3-dev', |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 130 | 'liblz4-tool', |
| 131 | 'libncurses5', |
| 132 | 'libnss3-dev', |
Henri Chataing | e08cbed | 2023-10-20 09:06:51 -0700 | [diff] [blame] | 133 | 'libfmt-dev', |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 134 | 'libprotobuf-dev', |
| 135 | 'libre2-9', |
Martin Brabham | 996f150 | 2022-02-14 17:39:23 +0000 | [diff] [blame] | 136 | 'libre2-dev', |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 137 | 'libssl-dev', |
| 138 | 'libtinyxml2-dev', |
| 139 | 'libx11-dev', |
| 140 | 'libxml2-utils', |
| 141 | 'ninja-build', |
| 142 | 'openssl', |
| 143 | 'protobuf-compiler', |
| 144 | 'unzip', |
| 145 | 'x11proto-core-dev', |
| 146 | 'xsltproc', |
| 147 | 'zip', |
| 148 | 'zlib1g-dev', |
| 149 | ] |
| 150 | |
| 151 | # List of cargo packages required for linux build |
Abhishek Pandit-Subedi | a04c74c | 2024-08-01 09:24:34 -0700 | [diff] [blame] | 152 | REQUIRED_CARGO_PACKAGES = ['cxxbridge-cmd', 'pdl-compiler', 'grpcio-compiler', 'cargo-bloat'] |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 153 | |
| 154 | APT_PKG_LIST = ['apt', '-qq', 'list'] |
| 155 | CARGO_PKG_LIST = ['cargo', 'install', '--list'] |
| 156 | |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 157 | |
| 158 | class UseFlags(): |
| 159 | |
| 160 | def __init__(self, use_flags): |
| 161 | """ Construct the use flags. |
| 162 | |
| 163 | Args: |
| 164 | use_flags: List of use flags parsed from the command. |
| 165 | """ |
| 166 | self.flags = {} |
| 167 | |
| 168 | # Import use flags required by common-mk |
| 169 | for use in COMMON_MK_USES: |
| 170 | self.set_flag(use, False) |
| 171 | |
| 172 | # Set our defaults |
| 173 | for use, value in USE_DEFAULTS.items(): |
| 174 | self.set_flag(use, value) |
| 175 | |
| 176 | # Set use flags - value is set to True unless the use starts with - |
| 177 | # All given use flags always override the defaults |
| 178 | for use in use_flags: |
| 179 | value = not use.startswith('-') |
| 180 | self.set_flag(use, value) |
| 181 | |
| 182 | def set_flag(self, key, value=True): |
| 183 | setattr(self, key, value) |
| 184 | self.flags[key] = value |
| 185 | |
| 186 | |
| 187 | class HostBuild(): |
| 188 | |
| 189 | def __init__(self, args): |
| 190 | """ Construct the builder. |
| 191 | |
| 192 | Args: |
| 193 | args: Parsed arguments from ArgumentParser |
| 194 | """ |
| 195 | self.args = args |
| 196 | |
| 197 | # Set jobs to number of cpus unless explicitly set |
| 198 | self.jobs = self.args.jobs |
| 199 | if not self.jobs: |
| 200 | self.jobs = multiprocessing.cpu_count() |
Sonny Sasaka | 5b7e6f2 | 2022-09-06 11:31:53 -0700 | [diff] [blame] | 201 | sys.stderr.write("Number of jobs = {}\n".format(self.jobs)) |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 202 | |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 203 | # Normalize bootstrap dir and make sure it exists |
| 204 | self.bootstrap_dir = os.path.abspath(self.args.bootstrap_dir) |
| 205 | os.makedirs(self.bootstrap_dir, exist_ok=True) |
| 206 | |
| 207 | # Output and platform directories are based on bootstrap |
| 208 | self.output_dir = os.path.join(self.bootstrap_dir, 'output') |
| 209 | self.platform_dir = os.path.join(self.bootstrap_dir, 'staging') |
Michael Sun | 4940f2b | 2022-09-15 16:08:24 -0700 | [diff] [blame] | 210 | self.bt_dir = os.path.join(self.platform_dir, 'bt') |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 211 | self.sysroot = self.args.sysroot |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 212 | self.libdir = self.args.libdir |
Abhishek Pandit-Subedi | a1a94d1 | 2021-11-18 13:14:04 -0800 | [diff] [blame] | 213 | self.install_dir = os.path.join(self.output_dir, 'install') |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 214 | |
Michael Sun | 4940f2b | 2022-09-15 16:08:24 -0700 | [diff] [blame] | 215 | assert os.path.samefile(self.bt_dir, |
| 216 | os.path.dirname(__file__)), "Please rerun bootstrap for the current project!" |
| 217 | |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 218 | # If default target isn't set, build everything |
| 219 | self.target = 'all' |
| 220 | if hasattr(self.args, 'target') and self.args.target: |
| 221 | self.target = self.args.target |
| 222 | |
Abhishek Pandit-Subedi | d801b12 | 2021-04-10 00:41:07 +0000 | [diff] [blame] | 223 | target_use = self.args.use if self.args.use else [] |
| 224 | |
| 225 | # Unless set, always build test code |
| 226 | if not self.args.notest: |
| 227 | target_use.append('test') |
| 228 | |
| 229 | self.use = UseFlags(target_use) |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 230 | |
| 231 | # Validate platform directory |
| 232 | assert os.path.isdir(self.platform_dir), 'Platform dir does not exist' |
| 233 | assert os.path.isfile(os.path.join(self.platform_dir, '.gn')), 'Platform dir does not have .gn at root' |
| 234 | |
| 235 | # Make sure output directory exists (or create it) |
| 236 | os.makedirs(self.output_dir, exist_ok=True) |
| 237 | |
| 238 | # Set some default attributes |
| 239 | self.libbase_ver = None |
| 240 | |
| 241 | self.configure_environ() |
| 242 | |
Abhishek Pandit-Subedi | d801b12 | 2021-04-10 00:41:07 +0000 | [diff] [blame] | 243 | def _generate_rustflags(self): |
| 244 | """ Rustflags to include for the build. |
| 245 | """ |
| 246 | rust_flags = [ |
| 247 | '-L', |
Martin Brabham | 1c24fda | 2021-09-16 11:19:46 -0700 | [diff] [blame] | 248 | '{}/out/Default'.format(self.output_dir), |
Abhishek Pandit-Subedi | d801b12 | 2021-04-10 00:41:07 +0000 | [diff] [blame] | 249 | '-C', |
| 250 | 'link-arg=-Wl,--allow-multiple-definition', |
Sonny Sasaka | 87bacb6 | 2022-04-29 10:34:29 -0700 | [diff] [blame] | 251 | # exclude uninteresting warnings |
| 252 | '-A improper_ctypes_definitions -A improper_ctypes -A unknown_lints', |
Abhishek Pandit-Subedi | a04c74c | 2024-08-01 09:24:34 -0700 | [diff] [blame] | 253 | '-Cstrip=debuginfo', |
| 254 | '-Copt-level=z', |
Abhishek Pandit-Subedi | d801b12 | 2021-04-10 00:41:07 +0000 | [diff] [blame] | 255 | ] |
| 256 | |
| 257 | return ' '.join(rust_flags) |
| 258 | |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 259 | def configure_environ(self): |
| 260 | """ Configure environment variables for GN and Cargo. |
| 261 | """ |
| 262 | self.env = os.environ.copy() |
| 263 | |
| 264 | # Make sure cargo home dir exists and has a bin directory |
| 265 | cargo_home = os.path.join(self.output_dir, 'cargo_home') |
| 266 | os.makedirs(cargo_home, exist_ok=True) |
| 267 | os.makedirs(os.path.join(cargo_home, 'bin'), exist_ok=True) |
| 268 | |
| 269 | # Configure Rust env variables |
Sonny Sasaka | 5b7e6f2 | 2022-09-06 11:31:53 -0700 | [diff] [blame] | 270 | self.custom_env = {} |
| 271 | self.custom_env['CARGO_TARGET_DIR'] = self.output_dir |
| 272 | self.custom_env['CARGO_HOME'] = os.path.join(self.output_dir, 'cargo_home') |
| 273 | self.custom_env['RUSTFLAGS'] = self._generate_rustflags() |
| 274 | self.custom_env['CXX_ROOT_PATH'] = os.path.join(self.platform_dir, 'bt') |
| 275 | self.custom_env['CROS_SYSTEM_API_ROOT'] = os.path.join(self.platform_dir, 'system_api') |
| 276 | self.custom_env['CXX_OUTDIR'] = self._gn_default_output() |
Ludovic Barman | 6cd8236 | 2023-11-08 09:12:07 +0000 | [diff] [blame] | 277 | |
| 278 | # On ChromeOS, this is /usr/bin/grpc_rust_plugin |
| 279 | # In the container, this is /root/.cargo/bin/grpc_rust_plugin |
| 280 | self.custom_env['GRPC_RUST_PLUGIN_PATH'] = shutil.which('grpc_rust_plugin') |
Sonny Sasaka | 5b7e6f2 | 2022-09-06 11:31:53 -0700 | [diff] [blame] | 281 | self.env.update(self.custom_env) |
| 282 | |
| 283 | def print_env(self): |
| 284 | """ Print the custom environment variables that are used in build. |
| 285 | |
| 286 | Useful so that external tools can mimic the environment to be the same |
| 287 | as build.py, e.g. rust-analyzer. |
| 288 | """ |
| 289 | for k, v in self.custom_env.items(): |
| 290 | print("export {}='{}'".format(k, v)) |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 291 | |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 292 | def run_command(self, target, args, cwd=None, env=None): |
| 293 | """ Run command and stream the output. |
| 294 | """ |
| 295 | # Set some defaults |
| 296 | if not cwd: |
| 297 | cwd = self.platform_dir |
| 298 | if not env: |
| 299 | env = self.env |
| 300 | |
Abhishek Pandit-Subedi | 2b09507 | 2024-08-01 09:34:03 -0700 | [diff] [blame] | 301 | for k, v in env.items(): |
| 302 | if env[k] is None: |
| 303 | env[k] = "" |
| 304 | |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 305 | log_file = os.path.join(self.output_dir, '{}.log'.format(target)) |
| 306 | with open(log_file, 'wb') as lf: |
| 307 | rc = 0 |
| 308 | process = subprocess.Popen(args, cwd=cwd, env=env, stdout=subprocess.PIPE) |
| 309 | while True: |
| 310 | line = process.stdout.readline() |
| 311 | print(line.decode('utf-8'), end="") |
| 312 | lf.write(line) |
| 313 | if not line: |
| 314 | rc = process.poll() |
| 315 | if rc is not None: |
| 316 | break |
| 317 | |
| 318 | time.sleep(0.1) |
| 319 | |
| 320 | if rc != 0: |
| 321 | raise Exception("Return code is {}".format(rc)) |
| 322 | |
| 323 | def _get_basever(self): |
| 324 | if self.libbase_ver: |
| 325 | return self.libbase_ver |
| 326 | |
| 327 | self.libbase_ver = os.environ.get('BASE_VER', '') |
| 328 | if not self.libbase_ver: |
| 329 | base_file = os.path.join(self.sysroot, 'usr/share/libchrome/BASE_VER') |
| 330 | try: |
| 331 | with open(base_file, 'r') as f: |
| 332 | self.libbase_ver = f.read().strip('\n') |
| 333 | except: |
| 334 | self.libbase_ver = 'NOT-INSTALLED' |
| 335 | |
| 336 | return self.libbase_ver |
| 337 | |
| 338 | def _gn_default_output(self): |
| 339 | return os.path.join(self.output_dir, 'out/Default') |
| 340 | |
| 341 | def _gn_configure(self): |
| 342 | """ Configure all required parameters for platform2. |
| 343 | |
| 344 | Mostly copied from //common-mk/platform2.py |
| 345 | """ |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 346 | clang = not self.args.no_clang |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 347 | |
| 348 | def to_gn_string(s): |
| 349 | return '"%s"' % s.replace('"', '\\"') |
| 350 | |
| 351 | def to_gn_list(strs): |
| 352 | return '[%s]' % ','.join([to_gn_string(s) for s in strs]) |
| 353 | |
| 354 | def to_gn_args_args(gn_args): |
| 355 | for k, v in gn_args.items(): |
| 356 | if isinstance(v, bool): |
| 357 | v = str(v).lower() |
| 358 | elif isinstance(v, list): |
| 359 | v = to_gn_list(v) |
| 360 | elif isinstance(v, six.string_types): |
| 361 | v = to_gn_string(v) |
| 362 | else: |
| 363 | raise AssertionError('Unexpected %s, %r=%r' % (type(v), k, v)) |
| 364 | yield '%s=%s' % (k.replace('-', '_'), v) |
| 365 | |
| 366 | gn_args = { |
| 367 | 'platform_subdir': 'bt', |
| 368 | 'cc': 'clang' if clang else 'gcc', |
| 369 | 'cxx': 'clang++' if clang else 'g++', |
| 370 | 'ar': 'llvm-ar' if clang else 'ar', |
| 371 | 'pkg-config': 'pkg-config', |
| 372 | 'clang_cc': clang, |
| 373 | 'clang_cxx': clang, |
| 374 | 'OS': 'linux', |
| 375 | 'sysroot': self.sysroot, |
| 376 | 'libdir': os.path.join(self.sysroot, self.libdir), |
| 377 | 'build_root': self.output_dir, |
| 378 | 'platform2_root': self.platform_dir, |
| 379 | 'libbase_ver': self._get_basever(), |
| 380 | 'enable_exceptions': os.environ.get('CXXEXCEPTIONS', 0) == '1', |
| 381 | 'external_cflags': [], |
Abhishek Pandit-Subedi | 852dc3a | 2022-02-14 15:12:36 -0800 | [diff] [blame] | 382 | 'external_cxxflags': ["-DNDEBUG"], |
Hsin-chen Chuang | e31b416 | 2024-08-23 11:45:28 +0800 | [diff] [blame] | 383 | 'enable_werror': True, |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 384 | } |
| 385 | |
| 386 | if clang: |
| 387 | # Make sure to mark the clang use flag as true |
| 388 | self.use.set_flag('clang', True) |
| 389 | gn_args['external_cxxflags'] += ['-I/usr/include/'] |
| 390 | |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 391 | gn_args_args = list(to_gn_args_args(gn_args)) |
| 392 | use_args = ['%s=%s' % (k, str(v).lower()) for k, v in self.use.flags.items()] |
| 393 | gn_args_args += ['use={%s}' % (' '.join(use_args))] |
| 394 | |
| 395 | gn_args = [ |
| 396 | 'gn', |
| 397 | 'gen', |
| 398 | ] |
| 399 | |
| 400 | if self.args.verbose: |
| 401 | gn_args.append('-v') |
| 402 | |
| 403 | gn_args += [ |
| 404 | '--root=%s' % self.platform_dir, |
| 405 | '--args=%s' % ' '.join(gn_args_args), |
| 406 | self._gn_default_output(), |
| 407 | ] |
| 408 | |
Sonny Sasaka | 706ec3b | 2021-03-25 05:39:20 -0700 | [diff] [blame] | 409 | if 'PKG_CONFIG_PATH' in self.env: |
| 410 | print('DEBUG: PKG_CONFIG_PATH is', self.env['PKG_CONFIG_PATH']) |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 411 | |
| 412 | self.run_command('configure', gn_args) |
| 413 | |
| 414 | def _gn_build(self, target): |
| 415 | """ Generate the ninja command for the target and run it. |
| 416 | """ |
| 417 | args = ['%s:%s' % ('bt', target)] |
| 418 | ninja_args = ['ninja', '-C', self._gn_default_output()] |
| 419 | if self.jobs: |
| 420 | ninja_args += ['-j', str(self.jobs)] |
| 421 | ninja_args += args |
| 422 | |
| 423 | if self.args.verbose: |
| 424 | ninja_args.append('-v') |
| 425 | |
| 426 | self.run_command('build', ninja_args) |
| 427 | |
| 428 | def _rust_configure(self): |
| 429 | """ Generate config file at cargo_home so we use vendored crates. |
| 430 | """ |
| 431 | template = """ |
| 432 | [source.systembt] |
| 433 | directory = "{}/external/rust/vendor" |
| 434 | |
| 435 | [source.crates-io] |
| 436 | replace-with = "systembt" |
| 437 | local-registry = "/nonexistent" |
| 438 | """ |
Sonny Sasaka | c1335a2 | 2021-03-25 07:10:47 -0700 | [diff] [blame] | 439 | |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 440 | if not self.args.no_vendored_rust: |
Sonny Sasaka | c1335a2 | 2021-03-25 07:10:47 -0700 | [diff] [blame] | 441 | contents = template.format(self.platform_dir) |
| 442 | with open(os.path.join(self.env['CARGO_HOME'], 'config'), 'w') as f: |
| 443 | f.write(contents) |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 444 | |
| 445 | def _rust_build(self): |
| 446 | """ Run `cargo build` from platform2/bt directory. |
| 447 | """ |
Abhishek Pandit-Subedi | 30e5730 | 2022-10-17 13:41:09 -0700 | [diff] [blame] | 448 | cmd = ['cargo', 'build'] |
| 449 | if not self.args.rust_debug: |
| 450 | cmd.append('--release') |
| 451 | |
| 452 | self.run_command('rust', cmd, cwd=os.path.join(self.platform_dir, 'bt'), env=self.env) |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 453 | |
| 454 | def _target_prepare(self): |
| 455 | """ Target to prepare the output directory for building. |
| 456 | |
| 457 | This runs gn gen to generate all rquired files and set up the Rust |
| 458 | config properly. This will be run |
| 459 | """ |
| 460 | self._gn_configure() |
| 461 | self._rust_configure() |
| 462 | |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 463 | def _target_hosttools(self): |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 464 | """ Build the tools target in an already prepared environment. |
| 465 | """ |
| 466 | self._gn_build('tools') |
| 467 | |
| 468 | # Also copy bluetooth_packetgen to CARGO_HOME so it's available |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 469 | shutil.copy(os.path.join(self._gn_default_output(), 'bluetooth_packetgen'), |
| 470 | os.path.join(self.env['CARGO_HOME'], 'bin')) |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 471 | |
Abhishek Pandit-Subedi | 9476414 | 2022-02-07 17:18:00 -0800 | [diff] [blame] | 472 | def _target_docs(self): |
| 473 | """Build the Rust docs.""" |
| 474 | self.run_command('docs', ['cargo', 'doc'], cwd=os.path.join(self.platform_dir, 'bt'), env=self.env) |
| 475 | |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 476 | def _target_rust(self): |
| 477 | """ Build rust artifacts in an already prepared environment. |
| 478 | """ |
| 479 | self._rust_build() |
| 480 | |
| 481 | def _target_main(self): |
| 482 | """ Build the main GN artifacts in an already prepared environment. |
| 483 | """ |
| 484 | self._gn_build('all') |
| 485 | |
| 486 | def _target_test(self): |
| 487 | """ Runs the host tests. |
| 488 | """ |
Abhishek Pandit-Subedi | d801b12 | 2021-04-10 00:41:07 +0000 | [diff] [blame] | 489 | # Rust tests first |
Abhishek Pandit-Subedi | 9476414 | 2022-02-07 17:18:00 -0800 | [diff] [blame] | 490 | rust_test_cmd = ['cargo', 'test'] |
Abhishek Pandit-Subedi | 30e5730 | 2022-10-17 13:41:09 -0700 | [diff] [blame] | 491 | if not self.args.rust_debug: |
| 492 | rust_test_cmd.append('--release') |
| 493 | |
Abhishek Pandit-Subedi | 9476414 | 2022-02-07 17:18:00 -0800 | [diff] [blame] | 494 | if self.args.test_name: |
Abhishek Pandit-Subedi | b04e6a9 | 2022-09-08 19:19:40 -0700 | [diff] [blame] | 495 | rust_test_cmd = rust_test_cmd + [self.args.test_name, "--", "--test-threads=1", "--nocapture"] |
Abhishek Pandit-Subedi | 9476414 | 2022-02-07 17:18:00 -0800 | [diff] [blame] | 496 | |
| 497 | self.run_command('test', rust_test_cmd, cwd=os.path.join(self.platform_dir, 'bt'), env=self.env) |
Abhishek Pandit-Subedi | d801b12 | 2021-04-10 00:41:07 +0000 | [diff] [blame] | 498 | |
| 499 | # Host tests second based on host test list |
| 500 | for t in HOST_TESTS: |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 501 | self.run_command('test', [os.path.join(self.output_dir, 'out/Default', t)], |
| 502 | cwd=os.path.join(self.output_dir), |
| 503 | env=self.env) |
| 504 | |
Hsin-chen Chuang | bcd7b63 | 2024-06-07 01:07:08 +0800 | [diff] [blame] | 505 | def _target_clippy(self): |
| 506 | """ Runs cargo clippy, a collection of lints to catch common mistakes. |
| 507 | """ |
| 508 | cmd = ['cargo', 'clippy'] |
| 509 | self.run_command('rust', cmd, cwd=os.path.join(self.platform_dir, 'bt'), env=self.env) |
| 510 | |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 511 | def _target_utils(self): |
| 512 | """ Builds the utility applications. |
| 513 | """ |
| 514 | rust_targets = ['hcidoc'] |
| 515 | |
| 516 | # Build targets |
| 517 | for target in rust_targets: |
| 518 | self.run_command('utils', ['cargo', 'build', '-p', target], |
| 519 | cwd=os.path.join(self.platform_dir, 'bt'), |
| 520 | env=self.env) |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 521 | |
Abhishek Pandit-Subedi | a1a94d1 | 2021-11-18 13:14:04 -0800 | [diff] [blame] | 522 | def _target_install(self): |
| 523 | """ Installs files required to run Floss to install directory. |
| 524 | """ |
| 525 | # First make the install directory |
| 526 | prefix = self.install_dir |
| 527 | os.makedirs(prefix, exist_ok=True) |
| 528 | |
| 529 | # Next save the cwd and change to install directory |
| 530 | last_cwd = os.getcwd() |
| 531 | os.chdir(prefix) |
| 532 | |
| 533 | bindir = os.path.join(self.output_dir, 'debug') |
| 534 | srcdir = os.path.dirname(__file__) |
| 535 | |
| 536 | install_map = [ |
| 537 | { |
| 538 | 'src': os.path.join(bindir, 'btadapterd'), |
| 539 | 'dst': 'usr/libexec/bluetooth/btadapterd', |
| 540 | 'strip': True |
| 541 | }, |
| 542 | { |
| 543 | 'src': os.path.join(bindir, 'btmanagerd'), |
| 544 | 'dst': 'usr/libexec/bluetooth/btmanagerd', |
| 545 | 'strip': True |
| 546 | }, |
| 547 | { |
| 548 | 'src': os.path.join(bindir, 'btclient'), |
| 549 | 'dst': 'usr/local/bin/btclient', |
| 550 | 'strip': True |
| 551 | }, |
| 552 | ] |
| 553 | |
| 554 | for v in install_map: |
| 555 | src, partial_dst, strip = (v['src'], v['dst'], v['strip']) |
| 556 | dst = os.path.join(prefix, partial_dst) |
| 557 | |
| 558 | # Create dst directory first and copy file there |
| 559 | os.makedirs(os.path.dirname(dst), exist_ok=True) |
| 560 | print('Installing {}'.format(dst)) |
| 561 | shutil.copy(src, dst) |
| 562 | |
| 563 | # Binary should be marked for strip and no-strip option shouldn't be |
| 564 | # set. No-strip is useful while debugging. |
| 565 | if strip and not self.args.no_strip: |
| 566 | self.run_command('install', ['llvm-strip', dst]) |
| 567 | |
| 568 | # Put all files into a tar.gz for easier installation |
| 569 | tar_location = os.path.join(prefix, 'floss.tar.gz') |
| 570 | with tarfile.open(tar_location, 'w:gz') as tar: |
| 571 | for v in install_map: |
| 572 | tar.add(v['dst']) |
| 573 | |
| 574 | print('Tarball created at {}'.format(tar_location)) |
| 575 | |
Abhishek Pandit-Subedi | a04c74c | 2024-08-01 09:24:34 -0700 | [diff] [blame] | 576 | def _target_bloat(self): |
| 577 | """Run cargo bloat on workspace. |
| 578 | """ |
| 579 | crate_paths = [ |
| 580 | os.path.join(self.platform_dir, 'bt', 'system', 'gd', 'rust', 'linux', 'mgmt'), |
| 581 | os.path.join(self.platform_dir, 'bt', 'system', 'gd', 'rust', 'linux', 'service'), |
| 582 | os.path.join(self.platform_dir, 'bt', 'system', 'gd', 'rust', 'linux', 'client') |
| 583 | ] |
| 584 | for crate in crate_paths: |
| 585 | self.run_command('bloat', ['cargo', 'bloat', '--release', '--crates', '--wide'], cwd=crate, env=self.env) |
| 586 | |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 587 | def _target_clean(self): |
| 588 | """ Delete the output directory entirely. |
| 589 | """ |
| 590 | shutil.rmtree(self.output_dir) |
Abhishek Pandit-Subedi | a1a94d1 | 2021-11-18 13:14:04 -0800 | [diff] [blame] | 591 | |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 592 | # Remove Cargo.lock that may have become generated |
Abhishek Pandit-Subedi | 1661728 | 2022-09-28 10:11:10 -0700 | [diff] [blame] | 593 | cargo_lock_files = [ |
| 594 | os.path.join(self.platform_dir, 'bt', 'Cargo.lock'), |
Abhishek Pandit-Subedi | 1661728 | 2022-09-28 10:11:10 -0700 | [diff] [blame] | 595 | ] |
| 596 | for lock_file in cargo_lock_files: |
| 597 | try: |
| 598 | os.remove(lock_file) |
| 599 | print('Removed {}'.format(lock_file)) |
| 600 | except FileNotFoundError: |
| 601 | pass |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 602 | |
| 603 | def _target_all(self): |
Abhishek Pandit-Subedi | 9476414 | 2022-02-07 17:18:00 -0800 | [diff] [blame] | 604 | """ Build all common targets (skipping doc, test, and clean). |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 605 | """ |
| 606 | self._target_prepare() |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 607 | self._target_hosttools() |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 608 | self._target_main() |
Abhishek Pandit-Subedi | a7b57b7 | 2021-04-01 15:33:05 -0700 | [diff] [blame] | 609 | self._target_rust() |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 610 | |
| 611 | def build(self): |
| 612 | """ Builds according to self.target |
| 613 | """ |
| 614 | print('Building target ', self.target) |
| 615 | |
Abhishek Pandit-Subedi | 9476414 | 2022-02-07 17:18:00 -0800 | [diff] [blame] | 616 | # Validate that the target is valid |
| 617 | if self.target not in VALID_TARGETS: |
howardchung | 63187b6 | 2022-08-16 17:06:17 +0800 | [diff] [blame] | 618 | print('Target {} is not valid. Must be in {}'.format(self.target, VALID_TARGETS)) |
Abhishek Pandit-Subedi | 9476414 | 2022-02-07 17:18:00 -0800 | [diff] [blame] | 619 | return |
| 620 | |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 621 | if self.target == 'prepare': |
| 622 | self._target_prepare() |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 623 | elif self.target == 'hosttools': |
| 624 | self._target_hosttools() |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 625 | elif self.target == 'rust': |
| 626 | self._target_rust() |
Abhishek Pandit-Subedi | 9476414 | 2022-02-07 17:18:00 -0800 | [diff] [blame] | 627 | elif self.target == 'docs': |
| 628 | self._target_docs() |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 629 | elif self.target == 'main': |
| 630 | self._target_main() |
| 631 | elif self.target == 'test': |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 632 | self._target_test() |
Hsin-chen Chuang | bcd7b63 | 2024-06-07 01:07:08 +0800 | [diff] [blame] | 633 | elif self.target == 'clippy': |
| 634 | self._target_clippy() |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 635 | elif self.target == 'clean': |
| 636 | self._target_clean() |
Abhishek Pandit-Subedi | a1a94d1 | 2021-11-18 13:14:04 -0800 | [diff] [blame] | 637 | elif self.target == 'install': |
| 638 | self._target_install() |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 639 | elif self.target == 'utils': |
| 640 | self._target_utils() |
Abhishek Pandit-Subedi | a04c74c | 2024-08-01 09:24:34 -0700 | [diff] [blame] | 641 | elif self.target == 'bloat': |
| 642 | self._target_bloat() |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 643 | elif self.target == 'all': |
| 644 | self._target_all() |
| 645 | |
| 646 | |
Abhishek Pandit-Subedi | c1342e8 | 2024-04-22 17:27:57 -0700 | [diff] [blame] | 647 | # Default to 10 min timeouts on all git operations. |
| 648 | GIT_TIMEOUT_SEC = 600 |
| 649 | |
| 650 | |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 651 | class Bootstrap(): |
| 652 | |
Abhishek Pandit-Subedi | c1342e8 | 2024-04-22 17:27:57 -0700 | [diff] [blame] | 653 | def __init__(self, base_dir, bt_dir, partial_staging, clone_timeout): |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 654 | """ Construct bootstrapper. |
| 655 | |
| 656 | Args: |
| 657 | base_dir: Where to stage everything. |
| 658 | bt_dir: Where bluetooth source is kept (will be symlinked) |
Abhishek Pandit-Subedi | 4a56c04 | 2023-01-23 13:52:52 -0800 | [diff] [blame] | 659 | partial_staging: Whether to do a partial clone for staging. |
Abhishek Pandit-Subedi | c1342e8 | 2024-04-22 17:27:57 -0700 | [diff] [blame] | 660 | clone_timeout: Timeout for clone operations. |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 661 | """ |
| 662 | self.base_dir = os.path.abspath(base_dir) |
| 663 | self.bt_dir = os.path.abspath(bt_dir) |
Abhishek Pandit-Subedi | 4a56c04 | 2023-01-23 13:52:52 -0800 | [diff] [blame] | 664 | self.partial_staging = partial_staging |
Abhishek Pandit-Subedi | c1342e8 | 2024-04-22 17:27:57 -0700 | [diff] [blame] | 665 | self.clone_timeout = clone_timeout |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 666 | |
| 667 | # Create base directory if it doesn't already exist |
| 668 | os.makedirs(self.base_dir, exist_ok=True) |
| 669 | |
| 670 | if not os.path.isdir(self.bt_dir): |
| 671 | raise Exception('{} is not a valid directory'.format(self.bt_dir)) |
| 672 | |
| 673 | self.git_dir = os.path.join(self.base_dir, 'repos') |
| 674 | self.staging_dir = os.path.join(self.base_dir, 'staging') |
| 675 | self.output_dir = os.path.join(self.base_dir, 'output') |
| 676 | self.external_dir = os.path.join(self.base_dir, 'staging', 'external') |
| 677 | |
| 678 | self.dir_setup_complete = os.path.join(self.base_dir, '.setup-complete') |
| 679 | |
Abhishek Pandit-Subedi | c1342e8 | 2024-04-22 17:27:57 -0700 | [diff] [blame] | 680 | def _run_with_timeout(self, cmd, cwd, timeout=None): |
| 681 | """Runs a command using subprocess.check_output. """ |
| 682 | print('Running command: {} [at cwd={}]'.format(' '.join(cmd), cwd)) |
| 683 | with subprocess.Popen(cmd, cwd=cwd) as proc: |
| 684 | try: |
| 685 | outs, errs = proc.communicate(timeout=timeout) |
| 686 | except subprocess.TimeoutExpired: |
| 687 | proc.kill() |
| 688 | outs, errs = proc.communicate() |
| 689 | print('Timeout on {}'.format(' '.join(cmd)), file=sys.stderr) |
| 690 | raise |
| 691 | |
| 692 | if proc.returncode != 0: |
| 693 | raise Exception('Cmd {} had return code {}'.format(' '.join(cmd), proc.returncode)) |
| 694 | |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 695 | def _update_platform2(self): |
| 696 | """Updates repositories used for build.""" |
Abhishek Pandit-Subedi | 461eeab | 2023-01-09 10:20:17 -0800 | [diff] [blame] | 697 | for project in BOOTSTRAP_GIT_REPOS.keys(): |
| 698 | cwd = os.path.join(self.git_dir, project) |
| 699 | (repo, commit) = BOOTSTRAP_GIT_REPOS[project] |
| 700 | |
| 701 | # Update to required commit when necessary or pull the latest code. |
Abhishek Pandit-Subedi | c1342e8 | 2024-04-22 17:27:57 -0700 | [diff] [blame] | 702 | if commit is not None: |
Abhishek Pandit-Subedi | 461eeab | 2023-01-09 10:20:17 -0800 | [diff] [blame] | 703 | head = subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=cwd).strip() |
| 704 | if head != commit: |
| 705 | subprocess.check_call(['git', 'fetch'], cwd=cwd) |
| 706 | subprocess.check_call(['git', 'checkout', commit], cwd=cwd) |
| 707 | else: |
| 708 | subprocess.check_call(['git', 'pull'], cwd=cwd) |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 709 | |
| 710 | def _setup_platform2(self): |
| 711 | """ Set up platform2. |
| 712 | |
| 713 | This will check out all the git repos and symlink everything correctly. |
| 714 | """ |
| 715 | |
Abhishek Pandit-Subedi | b987eb6 | 2021-11-17 17:27:31 -0800 | [diff] [blame] | 716 | # Create all directories we will need to use |
| 717 | for dirpath in [self.git_dir, self.staging_dir, self.output_dir, self.external_dir]: |
| 718 | os.makedirs(dirpath, exist_ok=True) |
| 719 | |
| 720 | # If already set up, only update platform2 |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 721 | if os.path.isfile(self.dir_setup_complete): |
| 722 | print('{} already set-up. Updating instead.'.format(self.base_dir)) |
| 723 | self._update_platform2() |
Abhishek Pandit-Subedi | b987eb6 | 2021-11-17 17:27:31 -0800 | [diff] [blame] | 724 | else: |
Abhishek Pandit-Subedi | 4a56c04 | 2023-01-23 13:52:52 -0800 | [diff] [blame] | 725 | clone_options = [] |
| 726 | # When doing a partial staging, we use a treeless clone which allows |
| 727 | # us to access all commits but downloads things on demand. This |
| 728 | # helps speed up the initial git clone during builds but isn't good |
| 729 | # for long-term development. |
| 730 | if self.partial_staging: |
| 731 | clone_options = ['--filter=tree:0'] |
Abhishek Pandit-Subedi | b987eb6 | 2021-11-17 17:27:31 -0800 | [diff] [blame] | 732 | # Check out all repos in git directory |
Abhishek Pandit-Subedi | 461eeab | 2023-01-09 10:20:17 -0800 | [diff] [blame] | 733 | for project in BOOTSTRAP_GIT_REPOS.keys(): |
| 734 | (repo, commit) = BOOTSTRAP_GIT_REPOS[project] |
Abhishek Pandit-Subedi | c1342e8 | 2024-04-22 17:27:57 -0700 | [diff] [blame] | 735 | |
| 736 | # Try repo clone several times. |
| 737 | # Currently, we set timeout on this operation after |
| 738 | # |self.clone_timeout|. If it fails, try to recover. |
| 739 | tries = 2 |
| 740 | for x in range(tries): |
| 741 | try: |
| 742 | self._run_with_timeout(['git', 'clone', repo, project] + clone_options, |
| 743 | cwd=self.git_dir, |
| 744 | timeout=self.clone_timeout) |
| 745 | except subprocess.TimeoutExpired: |
| 746 | shutil.rmtree(os.path.join(self.git_dir, project)) |
| 747 | if x == tries - 1: |
| 748 | raise |
| 749 | # All other exceptions should raise |
| 750 | except: |
| 751 | raise |
| 752 | # No exceptions/problems should not retry. |
| 753 | else: |
| 754 | break |
| 755 | |
Abhishek Pandit-Subedi | 461eeab | 2023-01-09 10:20:17 -0800 | [diff] [blame] | 756 | # Pin to commit. |
Abhishek Pandit-Subedi | c1342e8 | 2024-04-22 17:27:57 -0700 | [diff] [blame] | 757 | if commit is not None: |
Abhishek Pandit-Subedi | 461eeab | 2023-01-09 10:20:17 -0800 | [diff] [blame] | 758 | subprocess.check_call(['git', 'checkout', commit], cwd=os.path.join(self.git_dir, project)) |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 759 | |
| 760 | # Symlink things |
| 761 | symlinks = [ |
| 762 | (os.path.join(self.git_dir, 'platform2', 'common-mk'), os.path.join(self.staging_dir, 'common-mk')), |
Sonny Sasaka | ae9f652 | 2022-03-28 10:31:34 -0700 | [diff] [blame] | 763 | (os.path.join(self.git_dir, 'platform2', 'system_api'), os.path.join(self.staging_dir, 'system_api')), |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 764 | (os.path.join(self.git_dir, 'platform2', '.gn'), os.path.join(self.staging_dir, '.gn')), |
| 765 | (os.path.join(self.bt_dir), os.path.join(self.staging_dir, 'bt')), |
| 766 | (os.path.join(self.git_dir, 'rust_crates'), os.path.join(self.external_dir, 'rust')), |
| 767 | (os.path.join(self.git_dir, 'proto_logging'), os.path.join(self.external_dir, 'proto_logging')), |
| 768 | ] |
| 769 | |
| 770 | # Create symlinks |
| 771 | for pairs in symlinks: |
| 772 | (src, dst) = pairs |
Martin Brabham | 247d80b | 2022-02-04 19:42:49 +0000 | [diff] [blame] | 773 | try: |
| 774 | os.unlink(dst) |
| 775 | except Exception as e: |
| 776 | print(e) |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 777 | os.symlink(src, dst) |
| 778 | |
| 779 | # Write to setup complete file so we don't repeat this step |
| 780 | with open(self.dir_setup_complete, 'w') as f: |
| 781 | f.write('Setup complete.') |
| 782 | |
| 783 | def _pretty_print_install(self, install_cmd, packages, line_limit=80): |
| 784 | """ Pretty print an install command. |
| 785 | |
| 786 | Args: |
| 787 | install_cmd: Prefixed install command. |
| 788 | packages: Enumerate packages and append them to install command. |
| 789 | line_limit: Number of characters per line. |
| 790 | |
| 791 | Return: |
| 792 | Array of lines to join and print. |
| 793 | """ |
| 794 | install = [install_cmd] |
| 795 | line = ' ' |
| 796 | # Remainder needed = space + len(pkg) + space + \ |
| 797 | # Assuming 80 character lines, that's 80 - 3 = 77 |
| 798 | line_limit = line_limit - 3 |
| 799 | for pkg in packages: |
| 800 | if len(line) + len(pkg) < line_limit: |
| 801 | line = '{}{} '.format(line, pkg) |
| 802 | else: |
| 803 | install.append(line) |
| 804 | line = ' {} '.format(pkg) |
| 805 | |
| 806 | if len(line) > 0: |
| 807 | install.append(line) |
| 808 | |
| 809 | return install |
| 810 | |
| 811 | def _check_package_installed(self, package, cmd, predicate): |
| 812 | """Check that the given package is installed. |
| 813 | |
| 814 | Args: |
| 815 | package: Check that this package is installed. |
| 816 | cmd: Command prefix to check if installed (package appended to end) |
| 817 | predicate: Function/lambda to check if package is installed based |
| 818 | on output. Takes string output and returns boolean. |
| 819 | |
| 820 | Return: |
| 821 | True if package is installed. |
| 822 | """ |
| 823 | try: |
| 824 | output = subprocess.check_output(cmd + [package], stderr=subprocess.STDOUT) |
| 825 | is_installed = predicate(output.decode('utf-8')) |
| 826 | print(' {} is {}'.format(package, 'installed' if is_installed else 'missing')) |
| 827 | |
| 828 | return is_installed |
| 829 | except Exception as e: |
| 830 | print(e) |
| 831 | return False |
| 832 | |
| 833 | def _get_command_output(self, cmd): |
| 834 | """Runs the command and gets the output. |
| 835 | |
| 836 | Args: |
| 837 | cmd: Command to run. |
| 838 | |
| 839 | Return: |
| 840 | Tuple (Success, Output). Success represents if the command ran ok. |
| 841 | """ |
| 842 | try: |
| 843 | output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) |
| 844 | return (True, output.decode('utf-8').split('\n')) |
| 845 | except Exception as e: |
| 846 | print(e) |
| 847 | return (False, "") |
| 848 | |
| 849 | def _print_missing_packages(self): |
| 850 | """Print any missing packages found via apt. |
| 851 | |
| 852 | This will find any missing packages necessary for build using apt and |
| 853 | print it out as an apt-get install printf. |
| 854 | """ |
| 855 | print('Checking for any missing packages...') |
| 856 | |
| 857 | (success, output) = self._get_command_output(APT_PKG_LIST) |
| 858 | if not success: |
| 859 | raise Exception("Could not query apt for packages.") |
| 860 | |
| 861 | packages_installed = {} |
| 862 | for line in output: |
| 863 | if 'installed' in line: |
| 864 | split = line.split('/', 2) |
| 865 | packages_installed[split[0]] = True |
| 866 | |
| 867 | need_packages = [] |
| 868 | for pkg in REQUIRED_APT_PACKAGES: |
| 869 | if pkg not in packages_installed: |
| 870 | need_packages.append(pkg) |
| 871 | |
| 872 | # No packages need to be installed |
| 873 | if len(need_packages) == 0: |
| 874 | print('+ All required packages are installed') |
| 875 | return |
| 876 | |
| 877 | install = self._pretty_print_install('sudo apt-get install', need_packages) |
| 878 | |
| 879 | # Print all lines so they can be run in cmdline |
| 880 | print('Missing system packages. Run the following command: ') |
| 881 | print(' \\\n'.join(install)) |
| 882 | |
| 883 | def _print_missing_rust_packages(self): |
| 884 | """Print any missing packages found via cargo. |
| 885 | |
| 886 | This will find any missing packages necessary for build using cargo and |
| 887 | print it out as a cargo-install printf. |
| 888 | """ |
| 889 | print('Checking for any missing cargo packages...') |
| 890 | |
| 891 | (success, output) = self._get_command_output(CARGO_PKG_LIST) |
| 892 | if not success: |
| 893 | raise Exception("Could not query cargo for packages.") |
| 894 | |
| 895 | packages_installed = {} |
| 896 | for line in output: |
| 897 | # Cargo installed packages have this format |
| 898 | # <crate name> <version>: |
| 899 | # <binary name> |
| 900 | # We only care about the crates themselves |
| 901 | if ':' not in line: |
| 902 | continue |
| 903 | |
| 904 | split = line.split(' ', 2) |
| 905 | packages_installed[split[0]] = True |
| 906 | |
| 907 | need_packages = [] |
| 908 | for pkg in REQUIRED_CARGO_PACKAGES: |
| 909 | if pkg not in packages_installed: |
| 910 | need_packages.append(pkg) |
| 911 | |
| 912 | # No packages to be installed |
| 913 | if len(need_packages) == 0: |
| 914 | print('+ All required cargo packages are installed') |
| 915 | return |
| 916 | |
| 917 | install = self._pretty_print_install('cargo install', need_packages) |
| 918 | print('Missing cargo packages. Run the following command: ') |
| 919 | print(' \\\n'.join(install)) |
| 920 | |
| 921 | def bootstrap(self): |
| 922 | """ Bootstrap the Linux build.""" |
| 923 | self._setup_platform2() |
| 924 | self._print_missing_packages() |
| 925 | self._print_missing_rust_packages() |
| 926 | |
| 927 | |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 928 | if __name__ == '__main__': |
| 929 | parser = argparse.ArgumentParser(description='Simple build for host.') |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 930 | parser.add_argument('--bootstrap-dir', |
| 931 | help='Directory to run bootstrap on (or was previously run on).', |
| 932 | default="~/.floss") |
| 933 | parser.add_argument('--run-bootstrap', |
| 934 | help='Run bootstrap code to verify build env is ok to build.', |
| 935 | default=False, |
| 936 | action='store_true') |
| 937 | parser.add_argument('--print-env', |
| 938 | help='Print environment variables used for build.', |
| 939 | default=False, |
| 940 | action='store_true') |
Andre Braga | 17ff7bc | 2022-06-24 22:43:18 +0000 | [diff] [blame] | 941 | parser.add_argument('--no-clang', help='Don\'t use clang compiler.', default=False, action='store_true') |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 942 | parser.add_argument('--no-strip', |
| 943 | help='Skip stripping binaries during install.', |
| 944 | default=False, |
| 945 | action='store_true') |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 946 | parser.add_argument('--use', help='Set a specific use flag.') |
Abhishek Pandit-Subedi | 9476414 | 2022-02-07 17:18:00 -0800 | [diff] [blame] | 947 | parser.add_argument('--notest', help='Don\'t compile test code.', default=False, action='store_true') |
| 948 | parser.add_argument('--test-name', help='Run test with this string in the name.', default=None) |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 949 | parser.add_argument('--target', help='Run specific build target') |
| 950 | parser.add_argument('--sysroot', help='Set a specific sysroot path', default='/') |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 951 | parser.add_argument('--libdir', help='Libdir - default = usr/lib', default='usr/lib') |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 952 | parser.add_argument('--jobs', help='Number of jobs to run', default=0, type=int) |
Abhishek Pandit-Subedi | 2a9a916 | 2022-12-16 16:09:01 -0800 | [diff] [blame] | 953 | parser.add_argument('--no-vendored-rust', |
| 954 | help='Do not use vendored rust crates', |
| 955 | default=False, |
| 956 | action='store_true') |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 957 | parser.add_argument('--verbose', help='Verbose logs for build.') |
Abhishek Pandit-Subedi | 30e5730 | 2022-10-17 13:41:09 -0700 | [diff] [blame] | 958 | parser.add_argument('--rust-debug', help='Build Rust code as debug.', default=False, action='store_true') |
Abhishek Pandit-Subedi | 4a56c04 | 2023-01-23 13:52:52 -0800 | [diff] [blame] | 959 | parser.add_argument( |
| 960 | '--partial-staging', |
| 961 | help='Bootstrap git repositories with partial clones. Use to speed up initial git clone for automated builds.', |
| 962 | default=False, |
| 963 | action='store_true') |
Abhishek Pandit-Subedi | c1342e8 | 2024-04-22 17:27:57 -0700 | [diff] [blame] | 964 | parser.add_argument('--clone-timeout', |
| 965 | help='Timeout for repository cloning during bootstrap.', |
| 966 | default=GIT_TIMEOUT_SEC, |
| 967 | type=int) |
Abhishek Pandit-Subedi | f52cf66 | 2021-03-02 22:33:25 +0000 | [diff] [blame] | 968 | args = parser.parse_args() |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 969 | |
| 970 | # Make sure we get absolute path + expanded path for bootstrap directory |
| 971 | args.bootstrap_dir = os.path.abspath(os.path.expanduser(args.bootstrap_dir)) |
| 972 | |
Andre Braga | aa11e7d | 2022-08-10 21:46:44 +0000 | [diff] [blame] | 973 | # Possible values for machine() come from 'uname -m' |
| 974 | # Since this script only runs on Linux, x86_64 machines must have this value |
| 975 | if platform.machine() != 'x86_64': |
| 976 | raise Exception("Only x86_64 machines are currently supported by this build script.") |
| 977 | |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 978 | if args.run_bootstrap: |
Abhishek Pandit-Subedi | c1342e8 | 2024-04-22 17:27:57 -0700 | [diff] [blame] | 979 | bootstrap = Bootstrap(args.bootstrap_dir, os.path.dirname(__file__), args.partial_staging, args.clone_timeout) |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 980 | bootstrap.bootstrap() |
Sonny Sasaka | 5b7e6f2 | 2022-09-06 11:31:53 -0700 | [diff] [blame] | 981 | elif args.print_env: |
| 982 | build = HostBuild(args) |
| 983 | build.print_env() |
Abhishek Pandit-Subedi | 0db7701 | 2021-09-20 17:54:26 -0700 | [diff] [blame] | 984 | else: |
| 985 | build = HostBuild(args) |
| 986 | build.build() |