summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbootstrap.py243
1 files changed, 243 insertions, 0 deletions
diff --git a/bootstrap.py b/bootstrap.py
new file mode 100755
index 0000000000..c7c04bfd75
--- /dev/null
+++ b/bootstrap.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python3
+
+# Copyright 2021 Google, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+""" Bootstrap script to help set up Linux build. """
+
+import argparse
+import os
+import subprocess
+
+PLATFORM2_GIT = 'https://chromium.googlesource.com/chromiumos/platform2'
+RUST_CRATES_GIT = 'https://chromium.googlesource.com/chromiumos/third_party/rust_crates'
+PROTO_LOGGING_GIT = 'https://android.googlesource.com/platform/frameworks/proto_logging'
+
+# List of packages required for linux build
+REQUIRED_APT_PACKAGES = [
+ 'bison',
+ 'build-essential',
+ 'curl',
+ 'flatbuffers-compiler',
+ 'flex',
+ 'g++-multilib',
+ 'gcc-multilib',
+ 'generate-ninja',
+ 'gnupg',
+ 'gperf',
+ 'libc++-dev',
+ 'libdbus-1-dev',
+ 'libevent-dev',
+ 'libevent-dev',
+ 'libflatbuffers-dev',
+ 'libflatbuffers1',
+ 'libgl1-mesa-dev',
+ 'libglib2.0-dev',
+ 'liblz4-tool',
+ 'libncurses5',
+ 'libnss3-dev',
+ 'libprotobuf-dev',
+ 'libre2-9',
+ 'libssl-dev',
+ 'libtinyxml2-dev',
+ 'libx11-dev',
+ 'libxml2-utils',
+ 'ninja-build',
+ 'openssl',
+ 'protobuf-compiler',
+ 'unzip',
+ 'x11proto-core-dev',
+ 'xsltproc',
+ 'zip',
+ 'zlib1g-dev',
+]
+
+# List of cargo packages required for linux build
+REQUIRED_CARGO_PACKAGES = ['cxxbridge-cmd']
+
+APT_PKG_LIST = ['apt', '-qq', 'list']
+CARGO_PKG_LIST = ['cargo', 'install', '--list']
+
+
+class Bootstrap():
+
+ def __init__(self, base_dir, bt_dir):
+ """ Construct bootstrapper.
+
+ Args:
+ base_dir: Where to stage everything.
+ bt_dir: Where bluetooth source is kept (will be symlinked)
+ """
+ self.base_dir = os.path.abspath(base_dir)
+ self.bt_dir = os.path.abspath(bt_dir)
+
+ if not os.path.isdir(self.base_dir):
+ raise Exception('{} is not a valid directory'.format(self.base_dir))
+
+ if not os.path.isdir(self.bt_dir):
+ raise Exception('{} is not a valid directory'.format(self.bt_dir))
+
+ self.git_dir = os.path.join(self.base_dir, 'repos')
+ self.staging_dir = os.path.join(self.base_dir, 'staging')
+ self.output_dir = os.path.join(self.base_dir, 'output')
+ self.external_dir = os.path.join(self.base_dir, 'staging', 'external')
+
+ self.dir_setup_complete = os.path.join(self.base_dir, '.setup-complete')
+
+ def _setup_platform2(self):
+ """ Set up platform2.
+
+ This will check out all the git repos and symlink everything correctly.
+ """
+
+ # If already set up, exit early
+ if os.path.isfile(self.dir_setup_complete):
+ print('{} is already set-up'.format(self.base_dir))
+ return
+
+ # Create all directories we will need to use
+ for dirpath in [self.git_dir, self.staging_dir, self.output_dir, self.external_dir]:
+ os.makedirs(dirpath)
+
+ # Check out all repos in git directory
+ for repo in [PLATFORM2_GIT, RUST_CRATES_GIT, PROTO_LOGGING_GIT]:
+ subprocess.check_call(['git', 'clone', repo], cwd=self.git_dir)
+
+ # Symlink things
+ symlinks = [
+ (os.path.join(self.git_dir, 'platform2', 'common-mk'), os.path.join(self.staging_dir, 'common-mk')),
+ (os.path.join(self.git_dir, 'platform2', '.gn'), os.path.join(self.staging_dir, '.gn')),
+ (os.path.join(self.bt_dir), os.path.join(self.staging_dir, 'bt')),
+ (os.path.join(self.git_dir, 'rust_crates'), os.path.join(self.external_dir, 'rust')),
+ (os.path.join(self.git_dir, 'proto_logging'), os.path.join(self.external_dir, 'proto_logging')),
+ ]
+
+ # Create symlinks
+ for pairs in symlinks:
+ (src, dst) = pairs
+ os.symlink(src, dst)
+
+ # Write to setup complete file so we don't repeat this step
+ with open(self.dir_setup_complete, 'w') as f:
+ f.write('Setup complete.')
+
+ def _pretty_print_install(self, install_cmd, packages, line_limit=80):
+ """ Pretty print an install command.
+
+ Args:
+ install_cmd: Prefixed install command.
+ packages: Enumerate packages and append them to install command.
+ line_limit: Number of characters per line.
+
+ Return:
+ Array of lines to join and print.
+ """
+ install = [install_cmd]
+ line = ' '
+ # Remainder needed = space + len(pkg) + space + \
+ # Assuming 80 character lines, that's 80 - 3 = 77
+ line_limit = line_limit - 3
+ for pkg in packages:
+ if len(line) + len(pkg) < line_limit:
+ line = '{}{} '.format(line, pkg)
+ else:
+ install.append(line)
+ line = ' {} '.format(pkg)
+
+ if len(line) > 0:
+ install.append(line)
+
+ return install
+
+ def _check_package_installed(self, package, cmd, predicate):
+ """Check that the given package is installed.
+
+ Args:
+ package: Check that this package is installed.
+ cmd: Command prefix to check if installed (package appended to end)
+ predicate: Function/lambda to check if package is installed based
+ on output. Takes string output and returns boolean.
+
+ Return:
+ True if package is installed.
+ """
+ try:
+ output = subprocess.check_output(cmd + [package], stderr=subprocess.STDOUT)
+ is_installed = predicate(output.decode('utf-8'))
+ print(' {} is {}'.format(package, 'installed' if is_installed else 'missing'))
+
+ return is_installed
+ except Exception as e:
+ print(e)
+ return False
+
+ def _print_missing_packages(self):
+ """Print any missing packages found via apt.
+
+ This will find any missing packages necessary for build using apt and
+ print it out as an apt-get install printf.
+ """
+ print('Checking for any missing packages...')
+ need_packages = []
+ for pkg in REQUIRED_APT_PACKAGES:
+ if not self._check_package_installed(pkg, APT_PKG_LIST, lambda output: 'installed' in output):
+ need_packages.append(pkg)
+
+ # No packages need to be installed
+ if len(need_packages) == 0:
+ print('All required packages are installed')
+ return
+
+ install = self._pretty_print_install('sudo apt-get install', need_packages)
+
+ # Print all lines so they can be run in cmdline
+ print('Missing system packages. Run the following command: ')
+ print(' \\\n'.join(install))
+
+ def _print_missing_rust_packages(self):
+ """Print any missing packages found via cargo.
+
+ This will find any missing packages necessary for build using cargo and
+ print it out as a cargo-install printf.
+ """
+ print('Checking for any missing cargo packages...')
+ need_packages = []
+
+ for pkg in REQUIRED_CARGO_PACKAGES:
+ if not self._check_package_installed(pkg, CARGO_PKG_LIST, lambda output: pkg in output):
+ need_packages.append(pkg)
+
+ # No packages to be installed
+ if len(need_packages) == 0:
+ print('All required cargo packages are installed')
+ return
+
+ install = self._pretty_print_install('cargo install', need_packages)
+ print('Missing cargo packages. Run the following command: ')
+ print(' \\\n'.join(install))
+
+ def bootstrap(self):
+ """ Bootstrap the Linux build."""
+ self._setup_platform2()
+ self._print_missing_packages()
+ self._print_missing_rust_packages()
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Bootstrap Linux build')
+ parser.add_argument('--base-dir', help='Where to create build directories.', required=True)
+ parser.add_argument('--bt-dir', help='Path to packages/modules/Bluetooth/system', required=True)
+
+ args = parser.parse_args()
+ bootstrap = Bootstrap(args.base_dir, args.bt_dir)
+ bootstrap.bootstrap()