blob: 3fcb2fb14a196342e0687f188819c061a64cec50 [file] [log] [blame]
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +00001#!/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
18For building, you will first have to stage a platform directory that has the
19following structure:
20|-common-mk
21|-bt
22|-external
23|-|-rust
24|-|-|-vendor
25
26The simplest way to do this is to check out platform2 to another directory (that
27is not a subdir of this bt directory), symlink bt there and symlink the rust
28vendor repository as well.
29"""
30import argparse
31import multiprocessing
32import os
33import shutil
34import six
35import subprocess
36import sys
Chris Mantone7ad6332021-09-30 22:55:39 -070037import time
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +000038
39# Use flags required by common-mk (find -type f | grep -nE 'use[.]' {})
40COMMON_MK_USES = [
41 'asan',
42 'coverage',
43 'cros_host',
44 'fuzzer',
45 'fuzzer',
46 'msan',
47 'profiling',
48 'tcmalloc',
49 'test',
50 'ubsan',
51]
52
53# Default use flags.
54USE_DEFAULTS = {
55 'android': False,
56 'bt_nonstandard_codecs': False,
57 'test': False,
58}
59
60VALID_TARGETS = [
61 'prepare', # Prepare the output directory (gn gen + rust setup)
62 'tools', # Build the host tools (i.e. packetgen)
63 'rust', # Build only the rust components + copy artifacts to output dir
64 'main', # Build the main C++ codebase
Abhishek Pandit-Subedid801b122021-04-10 00:41:07 +000065 'test', # Run the unit tests
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +000066 'clean', # Clean up output directory
67 'all', # All targets except test and clean
68]
69
Abhishek Pandit-Subedi70e43042021-06-10 21:16:52 +000070# TODO(b/190750167) - Host tests are disabled until we are full bazel build
Abhishek Pandit-Subedid801b122021-04-10 00:41:07 +000071HOST_TESTS = [
Abhishek Pandit-Subedi70e43042021-06-10 21:16:52 +000072 # 'bluetooth_test_common',
73 # 'bluetoothtbd_test',
74 # 'net_test_avrcp',
75 # 'net_test_btcore',
76 # 'net_test_types',
77 # 'net_test_btm_iso',
78 # 'net_test_btpackets',
Abhishek Pandit-Subedid801b122021-04-10 00:41:07 +000079]
80
Abhishek Pandit-Subedi0db77012021-09-20 17:54:26 -070081BOOTSTRAP_GIT_REPOS = {
82 'platform2': 'https://chromium.googlesource.com/chromiumos/platform2',
83 'rust_crates': 'https://chromium.googlesource.com/chromiumos/third_party/rust_crates',
84 'proto_logging': 'https://android.googlesource.com/platform/frameworks/proto_logging'
85}
86
87# List of packages required for linux build
88REQUIRED_APT_PACKAGES = [
89 'bison',
90 'build-essential',
91 'curl',
92 'debmake',
93 'flatbuffers-compiler',
94 'flex',
95 'g++-multilib',
96 'gcc-multilib',
97 'generate-ninja',
98 'gnupg',
99 'gperf',
100 'libc++-dev',
101 'libdbus-1-dev',
102 'libevent-dev',
103 'libevent-dev',
104 'libflatbuffers-dev',
105 'libflatbuffers1',
106 'libgl1-mesa-dev',
107 'libglib2.0-dev',
108 'liblz4-tool',
109 'libncurses5',
110 'libnss3-dev',
111 'libprotobuf-dev',
112 'libre2-9',
113 'libssl-dev',
114 'libtinyxml2-dev',
115 'libx11-dev',
116 'libxml2-utils',
117 'ninja-build',
118 'openssl',
119 'protobuf-compiler',
120 'unzip',
121 'x11proto-core-dev',
122 'xsltproc',
123 'zip',
124 'zlib1g-dev',
125]
126
127# List of cargo packages required for linux build
128REQUIRED_CARGO_PACKAGES = ['cxxbridge-cmd']
129
130APT_PKG_LIST = ['apt', '-qq', 'list']
131CARGO_PKG_LIST = ['cargo', 'install', '--list']
132
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000133
134class UseFlags():
135
136 def __init__(self, use_flags):
137 """ Construct the use flags.
138
139 Args:
140 use_flags: List of use flags parsed from the command.
141 """
142 self.flags = {}
143
144 # Import use flags required by common-mk
145 for use in COMMON_MK_USES:
146 self.set_flag(use, False)
147
148 # Set our defaults
149 for use, value in USE_DEFAULTS.items():
150 self.set_flag(use, value)
151
152 # Set use flags - value is set to True unless the use starts with -
153 # All given use flags always override the defaults
154 for use in use_flags:
155 value = not use.startswith('-')
156 self.set_flag(use, value)
157
158 def set_flag(self, key, value=True):
159 setattr(self, key, value)
160 self.flags[key] = value
161
162
163class HostBuild():
164
165 def __init__(self, args):
166 """ Construct the builder.
167
168 Args:
169 args: Parsed arguments from ArgumentParser
170 """
171 self.args = args
172
173 # Set jobs to number of cpus unless explicitly set
174 self.jobs = self.args.jobs
175 if not self.jobs:
176 self.jobs = multiprocessing.cpu_count()
Abhishek Pandit-Subedid801b122021-04-10 00:41:07 +0000177 print("Number of jobs = {}".format(self.jobs))
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000178
Abhishek Pandit-Subedi0db77012021-09-20 17:54:26 -0700179 # Normalize bootstrap dir and make sure it exists
180 self.bootstrap_dir = os.path.abspath(self.args.bootstrap_dir)
181 os.makedirs(self.bootstrap_dir, exist_ok=True)
182
183 # Output and platform directories are based on bootstrap
184 self.output_dir = os.path.join(self.bootstrap_dir, 'output')
185 self.platform_dir = os.path.join(self.bootstrap_dir, 'staging')
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000186 self.sysroot = self.args.sysroot
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000187 self.libdir = self.args.libdir
188
189 # If default target isn't set, build everything
190 self.target = 'all'
191 if hasattr(self.args, 'target') and self.args.target:
192 self.target = self.args.target
193
Abhishek Pandit-Subedid801b122021-04-10 00:41:07 +0000194 target_use = self.args.use if self.args.use else []
195
196 # Unless set, always build test code
197 if not self.args.notest:
198 target_use.append('test')
199
200 self.use = UseFlags(target_use)
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000201
202 # Validate platform directory
203 assert os.path.isdir(self.platform_dir), 'Platform dir does not exist'
204 assert os.path.isfile(os.path.join(self.platform_dir, '.gn')), 'Platform dir does not have .gn at root'
205
206 # Make sure output directory exists (or create it)
207 os.makedirs(self.output_dir, exist_ok=True)
208
209 # Set some default attributes
210 self.libbase_ver = None
211
212 self.configure_environ()
213
Abhishek Pandit-Subedid801b122021-04-10 00:41:07 +0000214 def _generate_rustflags(self):
215 """ Rustflags to include for the build.
216 """
217 rust_flags = [
218 '-L',
Martin Brabham1c24fda2021-09-16 11:19:46 -0700219 '{}/out/Default'.format(self.output_dir),
Abhishek Pandit-Subedid801b122021-04-10 00:41:07 +0000220 '-C',
221 'link-arg=-Wl,--allow-multiple-definition',
222 ]
223
224 return ' '.join(rust_flags)
225
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000226 def configure_environ(self):
227 """ Configure environment variables for GN and Cargo.
228 """
229 self.env = os.environ.copy()
230
231 # Make sure cargo home dir exists and has a bin directory
232 cargo_home = os.path.join(self.output_dir, 'cargo_home')
233 os.makedirs(cargo_home, exist_ok=True)
234 os.makedirs(os.path.join(cargo_home, 'bin'), exist_ok=True)
235
236 # Configure Rust env variables
237 self.env['CARGO_TARGET_DIR'] = self.output_dir
238 self.env['CARGO_HOME'] = os.path.join(self.output_dir, 'cargo_home')
Abhishek Pandit-Subedid801b122021-04-10 00:41:07 +0000239 self.env['RUSTFLAGS'] = self._generate_rustflags()
Abhishek Pandit-Subedi1927afa2021-04-28 21:16:18 -0700240 self.env['CXX_ROOT_PATH'] = os.path.join(self.platform_dir, 'bt')
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000241
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000242 def run_command(self, target, args, cwd=None, env=None):
243 """ Run command and stream the output.
244 """
245 # Set some defaults
246 if not cwd:
247 cwd = self.platform_dir
248 if not env:
249 env = self.env
250
251 log_file = os.path.join(self.output_dir, '{}.log'.format(target))
252 with open(log_file, 'wb') as lf:
253 rc = 0
254 process = subprocess.Popen(args, cwd=cwd, env=env, stdout=subprocess.PIPE)
255 while True:
256 line = process.stdout.readline()
257 print(line.decode('utf-8'), end="")
258 lf.write(line)
259 if not line:
260 rc = process.poll()
261 if rc is not None:
262 break
263
264 time.sleep(0.1)
265
266 if rc != 0:
267 raise Exception("Return code is {}".format(rc))
268
269 def _get_basever(self):
270 if self.libbase_ver:
271 return self.libbase_ver
272
273 self.libbase_ver = os.environ.get('BASE_VER', '')
274 if not self.libbase_ver:
275 base_file = os.path.join(self.sysroot, 'usr/share/libchrome/BASE_VER')
276 try:
277 with open(base_file, 'r') as f:
278 self.libbase_ver = f.read().strip('\n')
279 except:
280 self.libbase_ver = 'NOT-INSTALLED'
281
282 return self.libbase_ver
283
284 def _gn_default_output(self):
285 return os.path.join(self.output_dir, 'out/Default')
286
287 def _gn_configure(self):
288 """ Configure all required parameters for platform2.
289
290 Mostly copied from //common-mk/platform2.py
291 """
Abhishek Pandit-Subedi0db77012021-09-20 17:54:26 -0700292 clang = not self.args.no_clang
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000293
294 def to_gn_string(s):
295 return '"%s"' % s.replace('"', '\\"')
296
297 def to_gn_list(strs):
298 return '[%s]' % ','.join([to_gn_string(s) for s in strs])
299
300 def to_gn_args_args(gn_args):
301 for k, v in gn_args.items():
302 if isinstance(v, bool):
303 v = str(v).lower()
304 elif isinstance(v, list):
305 v = to_gn_list(v)
306 elif isinstance(v, six.string_types):
307 v = to_gn_string(v)
308 else:
309 raise AssertionError('Unexpected %s, %r=%r' % (type(v), k, v))
310 yield '%s=%s' % (k.replace('-', '_'), v)
311
312 gn_args = {
313 'platform_subdir': 'bt',
314 'cc': 'clang' if clang else 'gcc',
315 'cxx': 'clang++' if clang else 'g++',
316 'ar': 'llvm-ar' if clang else 'ar',
317 'pkg-config': 'pkg-config',
318 'clang_cc': clang,
319 'clang_cxx': clang,
320 'OS': 'linux',
321 'sysroot': self.sysroot,
322 'libdir': os.path.join(self.sysroot, self.libdir),
323 'build_root': self.output_dir,
324 'platform2_root': self.platform_dir,
325 'libbase_ver': self._get_basever(),
326 'enable_exceptions': os.environ.get('CXXEXCEPTIONS', 0) == '1',
327 'external_cflags': [],
328 'external_cxxflags': [],
329 'enable_werror': False,
330 }
331
332 if clang:
333 # Make sure to mark the clang use flag as true
334 self.use.set_flag('clang', True)
335 gn_args['external_cxxflags'] += ['-I/usr/include/']
336
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000337 gn_args_args = list(to_gn_args_args(gn_args))
338 use_args = ['%s=%s' % (k, str(v).lower()) for k, v in self.use.flags.items()]
339 gn_args_args += ['use={%s}' % (' '.join(use_args))]
340
341 gn_args = [
342 'gn',
343 'gen',
344 ]
345
346 if self.args.verbose:
347 gn_args.append('-v')
348
349 gn_args += [
350 '--root=%s' % self.platform_dir,
351 '--args=%s' % ' '.join(gn_args_args),
352 self._gn_default_output(),
353 ]
354
Sonny Sasaka706ec3b2021-03-25 05:39:20 -0700355 if 'PKG_CONFIG_PATH' in self.env:
356 print('DEBUG: PKG_CONFIG_PATH is', self.env['PKG_CONFIG_PATH'])
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000357
358 self.run_command('configure', gn_args)
359
360 def _gn_build(self, target):
361 """ Generate the ninja command for the target and run it.
362 """
363 args = ['%s:%s' % ('bt', target)]
364 ninja_args = ['ninja', '-C', self._gn_default_output()]
365 if self.jobs:
366 ninja_args += ['-j', str(self.jobs)]
367 ninja_args += args
368
369 if self.args.verbose:
370 ninja_args.append('-v')
371
372 self.run_command('build', ninja_args)
373
374 def _rust_configure(self):
375 """ Generate config file at cargo_home so we use vendored crates.
376 """
377 template = """
378 [source.systembt]
379 directory = "{}/external/rust/vendor"
380
381 [source.crates-io]
382 replace-with = "systembt"
383 local-registry = "/nonexistent"
384 """
Sonny Sasakac1335a22021-03-25 07:10:47 -0700385
Abhishek Pandit-Subedi0db77012021-09-20 17:54:26 -0700386 if not self.args.no_vendored_rust:
Sonny Sasakac1335a22021-03-25 07:10:47 -0700387 contents = template.format(self.platform_dir)
388 with open(os.path.join(self.env['CARGO_HOME'], 'config'), 'w') as f:
389 f.write(contents)
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000390
391 def _rust_build(self):
392 """ Run `cargo build` from platform2/bt directory.
393 """
394 self.run_command('rust', ['cargo', 'build'], cwd=os.path.join(self.platform_dir, 'bt'), env=self.env)
395
396 def _target_prepare(self):
397 """ Target to prepare the output directory for building.
398
399 This runs gn gen to generate all rquired files and set up the Rust
400 config properly. This will be run
401 """
402 self._gn_configure()
403 self._rust_configure()
404
405 def _target_tools(self):
406 """ Build the tools target in an already prepared environment.
407 """
408 self._gn_build('tools')
409
410 # Also copy bluetooth_packetgen to CARGO_HOME so it's available
411 shutil.copy(
412 os.path.join(self._gn_default_output(), 'bluetooth_packetgen'), os.path.join(self.env['CARGO_HOME'], 'bin'))
413
414 def _target_rust(self):
415 """ Build rust artifacts in an already prepared environment.
416 """
417 self._rust_build()
Sonny Sasakac1335a22021-03-25 07:10:47 -0700418 rust_dir = os.path.join(self._gn_default_output(), 'rust')
419 if os.path.exists(rust_dir):
420 shutil.rmtree(rust_dir)
421 shutil.copytree(os.path.join(self.output_dir, 'debug'), rust_dir)
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000422
423 def _target_main(self):
424 """ Build the main GN artifacts in an already prepared environment.
425 """
426 self._gn_build('all')
427
428 def _target_test(self):
429 """ Runs the host tests.
430 """
Abhishek Pandit-Subedid801b122021-04-10 00:41:07 +0000431 # Rust tests first
432 self.run_command('test', ['cargo', 'test'], cwd=os.path.join(self.platform_dir, 'bt'), env=self.env)
433
434 # Host tests second based on host test list
435 for t in HOST_TESTS:
436 self.run_command(
437 'test', [os.path.join(self.output_dir, 'out/Default', t)],
438 cwd=os.path.join(self.output_dir),
439 env=self.env)
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000440
441 def _target_clean(self):
442 """ Delete the output directory entirely.
443 """
444 shutil.rmtree(self.output_dir)
Abhishek Pandit-Subedi0db77012021-09-20 17:54:26 -0700445 # Remove Cargo.lock that may have become generated
446 os.remove(os.path.join(self.platform_dir, 'bt', 'Cargo.lock'))
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000447
448 def _target_all(self):
449 """ Build all common targets (skipping test and clean).
450 """
451 self._target_prepare()
452 self._target_tools()
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000453 self._target_main()
Abhishek Pandit-Subedia7b57b72021-04-01 15:33:05 -0700454 self._target_rust()
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000455
456 def build(self):
457 """ Builds according to self.target
458 """
459 print('Building target ', self.target)
460
461 if self.target == 'prepare':
462 self._target_prepare()
463 elif self.target == 'tools':
464 self._target_tools()
465 elif self.target == 'rust':
466 self._target_rust()
467 elif self.target == 'main':
468 self._target_main()
469 elif self.target == 'test':
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000470 self._target_test()
471 elif self.target == 'clean':
472 self._target_clean()
473 elif self.target == 'all':
474 self._target_all()
475
476
Abhishek Pandit-Subedi0db77012021-09-20 17:54:26 -0700477class Bootstrap():
478
479 def __init__(self, base_dir, bt_dir):
480 """ Construct bootstrapper.
481
482 Args:
483 base_dir: Where to stage everything.
484 bt_dir: Where bluetooth source is kept (will be symlinked)
485 """
486 self.base_dir = os.path.abspath(base_dir)
487 self.bt_dir = os.path.abspath(bt_dir)
488
489 # Create base directory if it doesn't already exist
490 os.makedirs(self.base_dir, exist_ok=True)
491
492 if not os.path.isdir(self.bt_dir):
493 raise Exception('{} is not a valid directory'.format(self.bt_dir))
494
495 self.git_dir = os.path.join(self.base_dir, 'repos')
496 self.staging_dir = os.path.join(self.base_dir, 'staging')
497 self.output_dir = os.path.join(self.base_dir, 'output')
498 self.external_dir = os.path.join(self.base_dir, 'staging', 'external')
499
500 self.dir_setup_complete = os.path.join(self.base_dir, '.setup-complete')
501
502 def _update_platform2(self):
503 """Updates repositories used for build."""
504 for repo in BOOTSTRAP_GIT_REPOS.keys():
505 cwd = os.path.join(self.git_dir, repo)
506 subprocess.check_call(['git', 'pull'], cwd=cwd)
507
508 def _setup_platform2(self):
509 """ Set up platform2.
510
511 This will check out all the git repos and symlink everything correctly.
512 """
513
514 # If already set up, exit early
515 if os.path.isfile(self.dir_setup_complete):
516 print('{} already set-up. Updating instead.'.format(self.base_dir))
517 self._update_platform2()
518 return
519
520 # Create all directories we will need to use
521 for dirpath in [self.git_dir, self.staging_dir, self.output_dir, self.external_dir]:
522 os.makedirs(dirpath)
523
524 # Check out all repos in git directory
525 for repo in BOOTSTRAP_GIT_REPOS.values():
526 subprocess.check_call(['git', 'clone', repo], cwd=self.git_dir)
527
528 # Symlink things
529 symlinks = [
530 (os.path.join(self.git_dir, 'platform2', 'common-mk'), os.path.join(self.staging_dir, 'common-mk')),
531 (os.path.join(self.git_dir, 'platform2', '.gn'), os.path.join(self.staging_dir, '.gn')),
532 (os.path.join(self.bt_dir), os.path.join(self.staging_dir, 'bt')),
533 (os.path.join(self.git_dir, 'rust_crates'), os.path.join(self.external_dir, 'rust')),
534 (os.path.join(self.git_dir, 'proto_logging'), os.path.join(self.external_dir, 'proto_logging')),
535 ]
536
537 # Create symlinks
538 for pairs in symlinks:
539 (src, dst) = pairs
540 os.symlink(src, dst)
541
542 # Write to setup complete file so we don't repeat this step
543 with open(self.dir_setup_complete, 'w') as f:
544 f.write('Setup complete.')
545
546 def _pretty_print_install(self, install_cmd, packages, line_limit=80):
547 """ Pretty print an install command.
548
549 Args:
550 install_cmd: Prefixed install command.
551 packages: Enumerate packages and append them to install command.
552 line_limit: Number of characters per line.
553
554 Return:
555 Array of lines to join and print.
556 """
557 install = [install_cmd]
558 line = ' '
559 # Remainder needed = space + len(pkg) + space + \
560 # Assuming 80 character lines, that's 80 - 3 = 77
561 line_limit = line_limit - 3
562 for pkg in packages:
563 if len(line) + len(pkg) < line_limit:
564 line = '{}{} '.format(line, pkg)
565 else:
566 install.append(line)
567 line = ' {} '.format(pkg)
568
569 if len(line) > 0:
570 install.append(line)
571
572 return install
573
574 def _check_package_installed(self, package, cmd, predicate):
575 """Check that the given package is installed.
576
577 Args:
578 package: Check that this package is installed.
579 cmd: Command prefix to check if installed (package appended to end)
580 predicate: Function/lambda to check if package is installed based
581 on output. Takes string output and returns boolean.
582
583 Return:
584 True if package is installed.
585 """
586 try:
587 output = subprocess.check_output(cmd + [package], stderr=subprocess.STDOUT)
588 is_installed = predicate(output.decode('utf-8'))
589 print(' {} is {}'.format(package, 'installed' if is_installed else 'missing'))
590
591 return is_installed
592 except Exception as e:
593 print(e)
594 return False
595
596 def _get_command_output(self, cmd):
597 """Runs the command and gets the output.
598
599 Args:
600 cmd: Command to run.
601
602 Return:
603 Tuple (Success, Output). Success represents if the command ran ok.
604 """
605 try:
606 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
607 return (True, output.decode('utf-8').split('\n'))
608 except Exception as e:
609 print(e)
610 return (False, "")
611
612 def _print_missing_packages(self):
613 """Print any missing packages found via apt.
614
615 This will find any missing packages necessary for build using apt and
616 print it out as an apt-get install printf.
617 """
618 print('Checking for any missing packages...')
619
620 (success, output) = self._get_command_output(APT_PKG_LIST)
621 if not success:
622 raise Exception("Could not query apt for packages.")
623
624 packages_installed = {}
625 for line in output:
626 if 'installed' in line:
627 split = line.split('/', 2)
628 packages_installed[split[0]] = True
629
630 need_packages = []
631 for pkg in REQUIRED_APT_PACKAGES:
632 if pkg not in packages_installed:
633 need_packages.append(pkg)
634
635 # No packages need to be installed
636 if len(need_packages) == 0:
637 print('+ All required packages are installed')
638 return
639
640 install = self._pretty_print_install('sudo apt-get install', need_packages)
641
642 # Print all lines so they can be run in cmdline
643 print('Missing system packages. Run the following command: ')
644 print(' \\\n'.join(install))
645
646 def _print_missing_rust_packages(self):
647 """Print any missing packages found via cargo.
648
649 This will find any missing packages necessary for build using cargo and
650 print it out as a cargo-install printf.
651 """
652 print('Checking for any missing cargo packages...')
653
654 (success, output) = self._get_command_output(CARGO_PKG_LIST)
655 if not success:
656 raise Exception("Could not query cargo for packages.")
657
658 packages_installed = {}
659 for line in output:
660 # Cargo installed packages have this format
661 # <crate name> <version>:
662 # <binary name>
663 # We only care about the crates themselves
664 if ':' not in line:
665 continue
666
667 split = line.split(' ', 2)
668 packages_installed[split[0]] = True
669
670 need_packages = []
671 for pkg in REQUIRED_CARGO_PACKAGES:
672 if pkg not in packages_installed:
673 need_packages.append(pkg)
674
675 # No packages to be installed
676 if len(need_packages) == 0:
677 print('+ All required cargo packages are installed')
678 return
679
680 install = self._pretty_print_install('cargo install', need_packages)
681 print('Missing cargo packages. Run the following command: ')
682 print(' \\\n'.join(install))
683
684 def bootstrap(self):
685 """ Bootstrap the Linux build."""
686 self._setup_platform2()
687 self._print_missing_packages()
688 self._print_missing_rust_packages()
689
690
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000691if __name__ == '__main__':
692 parser = argparse.ArgumentParser(description='Simple build for host.')
Abhishek Pandit-Subedi0db77012021-09-20 17:54:26 -0700693 parser.add_argument(
694 '--bootstrap-dir', help='Directory to run bootstrap on (or was previously run on).', default="~/.floss")
695 parser.add_argument(
696 '--run-bootstrap',
697 help='Run bootstrap code to verify build env is ok to build.',
698 default=False,
699 action='store_true')
700 parser.add_argument('--no-clang', help='Use clang compiler.', default=False, action='store_true')
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000701 parser.add_argument('--use', help='Set a specific use flag.')
Abhishek Pandit-Subedid801b122021-04-10 00:41:07 +0000702 parser.add_argument('--notest', help="Don't compile test code.", default=False, action='store_true')
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000703 parser.add_argument('--target', help='Run specific build target')
704 parser.add_argument('--sysroot', help='Set a specific sysroot path', default='/')
Abhishek Pandit-Subedi0db77012021-09-20 17:54:26 -0700705 parser.add_argument('--libdir', help='Libdir - default = usr/lib', default='usr/lib')
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000706 parser.add_argument('--jobs', help='Number of jobs to run', default=0, type=int)
Abhishek Pandit-Subedi0db77012021-09-20 17:54:26 -0700707 parser.add_argument(
708 '--no-vendored-rust', help='Do not use vendored rust crates', default=False, action='store_true')
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000709 parser.add_argument('--verbose', help='Verbose logs for build.')
Abhishek Pandit-Subedif52cf662021-03-02 22:33:25 +0000710 args = parser.parse_args()
Abhishek Pandit-Subedi0db77012021-09-20 17:54:26 -0700711
712 # Make sure we get absolute path + expanded path for bootstrap directory
713 args.bootstrap_dir = os.path.abspath(os.path.expanduser(args.bootstrap_dir))
714
715 if args.run_bootstrap:
716 bootstrap = Bootstrap(args.bootstrap_dir, os.path.dirname(__file__))
717 bootstrap.bootstrap()
718 else:
719 build = HostBuild(args)
720 build.build()