| """Build step class for building x86 cast_shell.""" |
| import logging |
| import os |
| import sys |
| |
| from helpers import branch_utils |
| from helpers import cast_shell_utils |
| from helpers import gn_utils |
| from helpers import ninja_utils |
| from slave import base_step |
| from slave.step import gn_check_step |
| |
| _UNIT_TEST_HELPER_MODULE = 'unit_test.unit_test_helper' |
| |
| BUILD_SCRIPT_PROJECT = 'chromecast/internal' |
| BUILD_SCRIPT_REL_PATH = 'build/build_chromecast.py' |
| CAST_SHELL_EXE = 'cast_shell' |
| |
| |
| def _InjectPythonPath(path): |
| if path not in sys.path: |
| sys.path.append(path) |
| |
| |
| class NonOtaBuildBaseStep(base_step.BaseStep): |
| """Base step for building and running a non-ota build. |
| |
| This is for any non-ota build that uses the build_chromecast.py build flow |
| instead of the OTA `make` build flow. |
| """ |
| |
| def __init__(self, name, branding, build_type, product, build_flavor, |
| sanitizer=None, build_args_product=None, |
| build_args_official=None, **kwargs): |
| """Creates a UnittestBaseStep instance. |
| |
| Args: |
| name: The name of the step. |
| branding: Build branding (chromium or chrome). |
| build_type: Type of system to build for ('x86', 'clang', or 'arm'). |
| product: product to build for (ie. 'chromecast', 'audio'). |
| build_flavor: Flavor of build ('Debug', 'Eng', 'Release'). |
| build_args_official: Whether to do an official build (disables debugging). |
| sanitizer: Clang saniziter to run with ('asan', 'msan', 'tsan', 'ubsan'). |
| build_args_product: The build/args/product/*.gn product to build for. |
| build_args_official: Whether the build is an official build. MUST be set |
| for any build that will ship to users. Disables debugging; do not |
| set for development builds. |
| **kwargs: Any additional args to pass to BaseStep. |
| """ |
| super().__init__(name=name, **kwargs) |
| |
| # TODO(b/73231972) remove branding, build_type, product, build_flavor |
| # after migration to new builds |
| assert branding in ['chromium', 'chrome', None] |
| assert build_type in ['x86', 'clang', 'arm', 'arm64', None] |
| assert product in [ |
| 'chromecast', 'audio', 'android', 'atv', 'display', None] |
| assert build_flavor in ['Debug', 'Eng', 'Release', None] |
| assert sanitizer in ['asan', 'msan', 'tsan', 'ubsan', None] |
| assert build_args_product |
| assert build_args_official is not None |
| |
| self._branding = branding |
| self._build_type = build_type |
| self._product = product |
| self._build_flavor = build_flavor |
| self._build_args_official = build_args_official |
| self._enable_gn_check = gn_utils.is_gn_check_enabled(self.manifest_branch, |
| product) |
| self._sanitizer = sanitizer |
| self._build_args_product = build_args_product |
| |
| self._build_target = kwargs.get('build_target') |
| |
| # Re-calculate the out directory. This mimics the calculation done in |
| # build_chromecast.py because we don't want to just import it and we can't |
| # refactor it because we have old branches we need to be able to build. |
| chrome_root = self.get_project_path('chromium/src') |
| self._outdir = os.path.join(chrome_root, 'out_' + build_args_product) |
| if self._build_flavor is None: |
| return |
| build_out_parts = ['out', build_type, product, 'gn'] |
| if sanitizer: |
| build_out_parts.append(sanitizer) |
| self._build_out_dir = os.path.join(chrome_root, '_'.join(build_out_parts)) |
| flavor_part = 'Release' if build_flavor == 'Eng' else build_flavor |
| self._outdir = os.path.join(self._build_out_dir, flavor_part) |
| |
| @property |
| def build_type_dir(self): |
| # TODO(b/73231972) remove build_type based logic once deprecated |
| if self._build_type is None: |
| if '_arm_' in self._build_args_product: |
| return 'arm' |
| elif '_arm64_' in self._build_args_product: |
| return 'arm64' |
| elif '_x86_' in self._build_args_product: |
| return 'x86' |
| return 'clang' |
| return self._build_type |
| |
| @property |
| def build_flavor_dir(self): |
| # TODO(b/73231972) remove build_flavor based logic once deprecated |
| # castshell builds had build_flavor == Debug despite non debug suffix |
| if self._build_flavor is None: |
| return 'Debug' if ('debug' in self._build_args_product or |
| 'castshell' in self._build_args_product |
| ) else 'Release' |
| return 'Release' if self._build_flavor == 'Eng' else self._build_flavor |
| |
| @property |
| def build_product_dir(self): |
| # TODO(b/73231972) remove product based logic once deprecated |
| if self._product is None: |
| return 'chromecast' |
| return self._product |
| |
| def _get_minimum_required_argv_for_outdir(self): |
| """Returns the arguments needed to get the outdir from build_chromecast.py. |
| |
| The chromecast/internal/build/build_chromecast.py script takes many command |
| line arguments, but only a select few are needed to be able to determine |
| the out directory that a build will happen in. |
| |
| This helper method assembles the minimum set of arguments required to |
| pass to build_chromecast.py to be generate the correct out directory. |
| |
| Returns: |
| A list of arguments |
| """ |
| # Set up the argv as if running the build_chromecast.py script. |
| argv = [ |
| 'build_chromecast.py', |
| '--archive_dir', self.get_gcs_dir(), |
| '--build_args_product', self._build_args_product, |
| '--build_number', self.build_number, |
| '--chrome_root', self.get_project_path('chromium/src')] |
| |
| # TODO(b/73231972) remove unused flags once migrated to new builds |
| if self._build_flavor: |
| argv += ['--build_flavor', self._build_flavor] |
| if self._build_type: |
| argv += ['--build_type', self._build_type] |
| if self._branding: |
| argv += ['--chromecast_branding', self._branding] |
| if self._product: |
| argv += ['--product_name', self._product] |
| |
| if self._sanitizer: |
| argv += ['--sanitizer', self._sanitizer] |
| |
| if self._build_target: |
| argv += ['--build_target', self._build_target] |
| |
| return argv |
| |
| def run(self): |
| raise NotImplementedError |
| |
| |
| class CastShellStep(NonOtaBuildBaseStep, gn_check_step.GnCheckStepBase): |
| """Build step class for building cast_shell.""" |
| |
| def __init__(self, branding, build_type, product, build_flavor, |
| enable_assistant, sanitizer=None, archive_to_tmp=False, |
| skip_create_zip=False, strip_binaries=False, **kwargs): |
| """Creates a CastShellStep instance. |
| |
| Args: |
| branding: Build branding ('chromium' or 'chrome'). |
| build_type: Type of system to build for ('x86', 'clang', or 'arm'). |
| product: product to build for (ie. 'chromecast', 'audio'). |
| build_flavor: Flavor of build ('Debug', 'Eng', 'Release'). |
| enable_assistant: enable Assistant functionality. Audio products only. |
| sanitizer: Clang saniziter to run with ('asan', 'msan', 'tsan', 'ubsan'). |
| **kwargs: Any additional args to pass to BaseStep. |
| """ |
| self._enable_code_coverage = kwargs.pop('code_coverage', False) |
| gn_check_step.GnCheckStepBase.__init__(self, **kwargs) |
| NonOtaBuildBaseStep.__init__(self, 'build cast_shell', branding, |
| build_type, product, build_flavor, |
| sanitizer=sanitizer, **kwargs) |
| self._enable_assistant = enable_assistant |
| self._archive_to_tmp = archive_to_tmp |
| self._skip_create_zip = skip_create_zip |
| self._strip_binaries = strip_binaries |
| self._unit_test_helper = None |
| |
| @property |
| def unit_test_helper(self): |
| """Use the unit_test_helper module to be consistent with Catabuilder.""" |
| if not self._unit_test_helper: |
| _InjectPythonPath(self.get_project_path('test')) |
| self._unit_test_helper = __import__( |
| _UNIT_TEST_HELPER_MODULE, fromlist=['']) |
| return self._unit_test_helper |
| |
| @property |
| def out_dir(self): |
| return self._outdir |
| |
| def BuildCastShell(self): |
| self.archive_dir = \ |
| self.get_tmp_dir() if self._archive_to_tmp else self.get_gcs_dir() |
| |
| base_path = self.get_project_path(BUILD_SCRIPT_PROJECT) |
| build_script = os.path.join(base_path, BUILD_SCRIPT_REL_PATH) |
| command = ['python3', build_script, |
| '--archive_dir', self.archive_dir, |
| '--build_args_product', self._build_args_product, |
| '--build_number', self.build_number, |
| '--chrome_root', self.get_project_path('chromium/src')] |
| command += self.build_accelerator.build_chromecast_flags |
| |
| # TODO(b/73231972) remove unused flags once migrated to new builds |
| if self._build_flavor: |
| command += ['--build_flavor', self._build_flavor] |
| if self._build_type: |
| command += ['--build_type', self._build_type] |
| if self._branding: |
| command += ['--chromecast_branding', self._branding] |
| if self._product: |
| command += ['--product_name', self._product] |
| if self._build_args_official: |
| command += ['--official'] |
| |
| if self._enable_assistant: |
| command += ['--enable_assistant'] |
| |
| if self._enable_gn_check: |
| command += ['--enable_gn_check'] |
| |
| if self._sanitizer: |
| command += ['--sanitizer', self._sanitizer] |
| |
| if self._build_target: |
| command += ['--build_target', self._build_target] |
| |
| if self._enable_code_coverage: |
| command += ['--enable_code_coverage'] |
| |
| if self._skip_create_zip: |
| command += ['--skip_create_zip'] |
| |
| returncode, stdout, _ = self.exec_subprocess(command) |
| |
| if returncode != 0: |
| comments = ninja_utils.ninja_failure_to_comments(stdout, max_errors=3) |
| for comment in comments: |
| self.add_review({'message': comment}) |
| |
| if self._enable_gn_check: |
| self.gn_check_to_review(stdout) |
| |
| return returncode |
| |
| |
| def StripBinaries(self): |
| """Strips binaries and *.so files built.""" |
| # out_dir: "chromium/src/out_audioassistant_grte_eng" |
| # build_out_dirname = "out_audioassistant_grte_eng" |
| # buildout_dir_path = "/tmp/Xvsgfvcfg/out_audioassistant_grte_eng" |
| # build_chromecast.py copies files from build_out_dir to buildout_dir_path |
| build_out_dirname = os.path.basename(self._outdir) |
| buildout_dir_path = os.path.join(self.archive_dir, build_out_dirname) |
| binaries_to_strip = [] |
| |
| if not os.path.exists(buildout_dir_path): |
| print( |
| 'Directory %s not found. Skipping strip binaries.' |
| % buildout_dir_path) |
| return |
| |
| # Filter out directories and non *.so files from build outputs |
| for path in os.listdir(buildout_dir_path): |
| if os.path.isfile(os.path.join(buildout_dir_path, path)) \ |
| and ('.so' in path): |
| binaries_to_strip.append(os.path.join(buildout_dir_path, path)) |
| |
| CAST_BINARIES_LIST = [ |
| 'cast_shell', |
| 'aurora', |
| 'ml_framework', |
| 'exo_main', |
| 'hello_world' |
| ] |
| |
| # Add cast binaries to binaries_to_strip |
| for path in os.listdir(buildout_dir_path): |
| if os.path.isfile(os.path.join(buildout_dir_path, path)) \ |
| and (path in CAST_BINARIES_LIST): |
| binaries_to_strip.append(os.path.join(buildout_dir_path, path)) |
| |
| # Strip binaries to same path's |
| for binary_path in binaries_to_strip: |
| print('Stripping %s' % binary_path) |
| try: |
| self.unit_test_helper.StripBinary(binary_path) |
| except Exception as e: |
| logging.error( |
| 'Error while stripping binary %s %s' % (binary_path, str(e))) |
| |
| def run(self): |
| """Builds cast_shell. |
| |
| Returns: |
| True iff there were no errors. |
| """ |
| returncode = self.BuildCastShell() |
| |
| if self._strip_binaries: |
| self.StripBinaries() |
| |
| cast_shell_exe_path = os.path.join(self._outdir, CAST_SHELL_EXE) |
| self.add_step_data(cast_shell_utils.CAST_SHELL_EXE_PATH, |
| cast_shell_exe_path) |
| print("Cast shell exe path: ", cast_shell_exe_path) |
| |
| return returncode == 0 |