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