def _run_step(self, step, part, part_names): common.reset_env() prereqs = self.parts_config.get_prereqs(part.name) unstaged_prereqs = {p for p in prereqs if 'stage' not in self._steps_run[p]} if unstaged_prereqs and not unstaged_prereqs.issubset(part_names): missing_parts = [part_name for part_name in self.config.part_names if part_name in unstaged_prereqs] if missing_parts: raise RuntimeError( 'Requested {!r} of {!r} but there are unsatisfied ' 'prerequisites: {!r}'.format( step, part.name, ' '.join(missing_parts))) elif unstaged_prereqs: # prerequisites need to build all the way to the staging # step to be able to share the common assets that make them # a dependency. logger.info( '{!r} has prerequisites that need to be staged: ' '{}'.format(part.name, ' '.join(unstaged_prereqs))) self.run('stage', unstaged_prereqs) # Run the preparation function for this step (if implemented) with contextlib.suppress(AttributeError): getattr(part, 'prepare_{}'.format(step))() common.env = self.parts_config.build_env_for_part(part) common.env.extend(self.config.project_env()) part = _replace_in_part(part) getattr(part, step)()
def _prepare_step(self, *, step: steps.Step, part: pluginhandler.PluginHandler): common.reset_env() all_dependencies = self.parts_config.get_dependencies(part.name) # Filter dependencies down to only those that need to run the # prerequisite step prerequisite_step = steps.get_dependency_prerequisite_step(step) dependencies = { p for p in all_dependencies if self._cache.should_step_run(p, prerequisite_step) } if dependencies: dependency_names = {p.name for p in dependencies} # Dependencies need to go all the way to the prerequisite step to # be able to share the common assets that make them a dependency logger.info("{!r} has dependencies that need to be {}d: {}".format( part.name, prerequisite_step.name, " ".join(dependency_names))) self.run(prerequisite_step, dependency_names) # Run the preparation function for this step (if implemented) preparation_function = getattr(part, "prepare_{}".format(step.name), None) if preparation_function: notify_part_progress(part, "Preparing to {}".format(step.name), debug=True) preparation_function() common.env = self.parts_config.build_env_for_part(part) common.env.extend(self.config.project_env()) part = _replace_in_part(part)
def _assemble_runtime_environment(self) -> str: # Classic confinement or building on a host that does not match the target base # means we cannot setup an environment that will work. if self._config_data["confinement"] == "classic": # Temporary workaround for snapd bug not expanding PATH: # We generate an empty runner which addresses the issue. # https://bugs.launchpad.net/snapd/+bug/1860369 return "" env = list() if self._project_config.project._snap_meta.base in ("core", "core16", "core18"): common.env = self._project_config.snap_env() assembled_env = common.assemble_env() common.reset_env() assembled_env = assembled_env.replace(self._prime_dir, "$SNAP") env.append(self._install_path_pattern.sub("$SNAP", assembled_env)) else: # TODO use something local to the meta package and # only add paths for directory items that actually exist. runtime_env = project_loader.runtime_env( self._prime_dir, self._project_config.project.arch_triplet ) for e in runtime_env: env.append(re.sub(self._prime_dir, "$SNAP", e)) env.append("export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH") return "\n".join(env)
def _run_step(self, *, step: steps.Step, part, progress, hint='', prerequisite_step: steps.Step=steps.STAGE): common.reset_env() prereqs = self.parts_config.get_prereqs(part.name) step_prereqs = {p for p in prereqs if prerequisite_step not in self._steps_run[p]} if step_prereqs: # prerequisites need to go all the way to the prerequisite step to # be able to share the common assets that make them a dependency. logger.info( '{!r} has prerequisites that need to be {}d: {}'.format( part.name, prerequisite_step.name, ' '.join(step_prereqs))) self.run(prerequisite_step, step_prereqs) # Run the preparation function for this step (if implemented) preparation_function = getattr( part, 'prepare_{}'.format(step.name), None) if preparation_function: notify_part_progress( part, 'Preparing to {}'.format(step.name), debug=True) preparation_function() common.env = self.parts_config.build_env_for_part(part) common.env.extend(self.config.project_env()) part = _replace_in_part(part) notify_part_progress(part, progress, hint) getattr(part, step.name)() self._steps_run[part.name].add(step) self.steps_were_run = True
def _run_step(self, step, part, part_names, dirty, recursed): common.reset_env() prereqs = self.config.part_prereqs(part.name) if recursed: prereqs = prereqs & dirty if prereqs and not prereqs.issubset(part_names): for prereq in self.config.all_parts: if prereq.name in prereqs and prereq.should_step_run('stage'): raise RuntimeError( 'Requested {!r} of {!r} but there are unsatisfied ' 'prerequisites: {!r}'.format( step, part.name, ' '.join(prereqs))) elif prereqs: # prerequisites need to build all the way to the staging # step to be able to share the common assets that make them # a dependency. logger.info( '{!r} has prerequisites that need to be staged: ' '{}'.format(part.name, ' '.join(prereqs))) self.run('stage', prereqs, recursed=True) if part.is_dirty(step): self._handle_dirty(part, step) if not part.should_step_run(step): part.notify_part_progress('Skipping {}'.format(step), '(already ran)') return # Run the preparation function for this step (if implemented) with contextlib.suppress(AttributeError): getattr(part, 'prepare_{}'.format(step))() common.env = self.config.build_env_for_part(part) getattr(part, step)()
def _run_step(self, step, part, part_names, dirty, recursed): common.reset_env() prereqs = self.parts_config.get_prereqs(part.name) if recursed: prereqs = prereqs & dirty if prereqs and not prereqs.issubset(part_names): for prereq in self.config.all_parts: if prereq.name in prereqs and prereq.should_step_run('stage'): raise RuntimeError( 'Requested {!r} of {!r} but there are unsatisfied ' 'prerequisites: {!r}'.format(step, part.name, ' '.join(prereqs))) elif prereqs: # prerequisites need to build all the way to the staging # step to be able to share the common assets that make them # a dependency. logger.info('{!r} has prerequisites that need to be staged: ' '{}'.format(part.name, ' '.join(prereqs))) self.run('stage', prereqs, recursed=True) if part.is_dirty(step): self._handle_dirty(part, step) if not part.should_step_run(step): part.notify_part_progress('Skipping {}'.format(step), '(already ran)') return # Run the preparation function for this step (if implemented) with contextlib.suppress(AttributeError): getattr(part, 'prepare_{}'.format(step))() common.env = self.parts_config.build_env_for_part(part) getattr(part, step)()
def _generate_snapcraft_runner(self) -> Optional[str]: """Create runner if required. Return path relative to prime directory, if created.""" # Classic confinement or building on a host that does not match the target base # means we cannot setup an environment that will work. if (self._config_data["confinement"] == "classic" or not self._is_host_compatible_with_base or not self._snap_meta.apps): return None meta_runner = os.path.join(self._prime_dir, "snap", "command-chain", "snapcraft-runner") common.env = self._project_config.snap_env() assembled_env = common.assemble_env() assembled_env = assembled_env.replace(self._prime_dir, "$SNAP") assembled_env = self._install_path_pattern.sub("$SNAP", assembled_env) if assembled_env: os.makedirs(os.path.dirname(meta_runner), exist_ok=True) with open(meta_runner, "w") as f: print("#!/bin/sh", file=f) print(assembled_env, file=f) print( "export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH", file=f) print('exec "$@"', file=f) os.chmod(meta_runner, 0o755) common.reset_env() return os.path.relpath(meta_runner, self._prime_dir)
def _prepare_step(self, *, step: steps.Step, part: pluginhandler.PluginHandler): common.reset_env() all_dependencies = self.parts_config.get_dependencies(part.name) # Filter dependencies down to only those that need to run the # prerequisite step prerequisite_step = steps.get_dependency_prerequisite_step(step) dependencies = { p for p in all_dependencies if self._cache.should_step_run(p, prerequisite_step) } if dependencies: dependency_names = {p.name for p in dependencies} # Dependencies need to go all the way to the prerequisite step to # be able to share the common assets that make them a dependency logger.info( "{!r} has dependencies that need to be {}d: {}".format( part.name, prerequisite_step.name, " ".join(dependency_names) ) ) self.run(prerequisite_step, dependency_names) # Run the preparation function for this step (if implemented) preparation_function = getattr(part, "prepare_{}".format(step.name), None) if preparation_function: notify_part_progress(part, "Preparing to {}".format(step.name), debug=True) preparation_function() common.env = self.parts_config.build_env_for_part(part) common.env.extend(self.config.project_env()) part = _replace_in_part(part)
def _run_step(self, step, part, part_names): common.reset_env() prereqs = self.parts_config.get_prereqs(part.name) # Dependencies need to be primed to have paths to. if step == 'prime': required_step = 'prime' # Or staged to be built with. else: required_step = 'stage' step_prereqs = { p for p in prereqs if required_step not in self._steps_run[p] } if step_prereqs: # prerequisites need to build all the way to the staging # step to be able to share the common assets that make them # a dependency. logger.info('{!r} has prerequisites that need to be {}d: ' '{}'.format(part.name, required_step, ' '.join(step_prereqs))) self.run(required_step, step_prereqs) # Run the preparation function for this step (if implemented) with contextlib.suppress(AttributeError): getattr(part, 'prepare_{}'.format(step))() common.env = self.parts_config.build_env_for_part(part) common.env.extend(self.config.project_env()) part = _replace_in_part(part) getattr(part, step)()
def write_snap_yaml(self) -> str: common.env = self._project_config.snap_env() try: package_snap_path = os.path.join(self.meta_dir, "snap.yaml") snap_yaml = self._compose_snap_yaml() with open(package_snap_path, "w") as f: yaml_utils.dump(snap_yaml, stream=f) return snap_yaml finally: common.reset_env()
def _prepare_step(self, *, step: steps.Step, part: pluginhandler.PluginHandler): common.reset_env() self._handle_part_dependencies(step=step, part=part) # Run the preparation function for this step (if implemented) preparation_function = getattr(part, "prepare_{}".format(step.name), None) if preparation_function: notify_part_progress(part, "Preparing to {}".format(step.name), debug=True) preparation_function() if isinstance(part.plugin, plugins.v1.PluginV1): common.env = self.parts_config.build_env_for_part(part) common.env.extend(self.config.project_env()) part = _replace_in_part(part)
def write_snap_yaml(self) -> str: common.env = self._project_config.snap_env() try: # Only generate the meta command chain if there are apps that need it if self._config_data.get("apps", None): self._generate_command_chain() package_snap_path = os.path.join(self.meta_dir, "snap.yaml") snap_yaml = self._compose_snap_yaml() with open(package_snap_path, "w") as f: yaml_utils.dump(snap_yaml, stream=f) return snap_yaml finally: common.reset_env()
def _assemble_runtime_environment(self) -> str: # Classic confinement or building on a host that does not match the target base # means we cannot setup an environment that will work. if self._config_data["confinement"] == "classic": # Temporary workaround for snapd bug not expanding PATH: # We generate an empty runner which addresses the issue. # https://bugs.launchpad.net/snapd/+bug/1860369 return "" common.env = self._project_config.snap_env() assembled_env = common.assemble_env() common.reset_env() assembled_env = assembled_env.replace(self._prime_dir, "$SNAP") assembled_env = self._install_path_pattern.sub("$SNAP", assembled_env) ld_library_env = "export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH" return "\n".join([assembled_env, ld_library_env])
def _assemble_runtime_environment(self) -> str: # Classic confinement or building on a host that does not match the target base # means we cannot setup an environment that will work. if self._config_data["confinement"] == "classic": # Temporary workaround for snapd bug not expanding PATH: # We generate an empty runner which addresses the issue. # https://bugs.launchpad.net/snapd/+bug/1860369 return "" env = list() if self._project_config.project._snap_meta.base == "core18": common.env = self._project_config.snap_env() assembled_env = common.assemble_env() common.reset_env() assembled_env = assembled_env.replace(self._prime_dir, "$SNAP") env.append(self._install_path_pattern.sub("$SNAP", assembled_env)) else: # TODO use something local to the meta package and # only add paths for directory items that actually exist. runtime_env = project_loader.runtime_env( self._prime_dir, self._project_config.project.arch_triplet ) for e in runtime_env: env.append(re.sub(self._prime_dir, "$SNAP", e)) if all( [ part._build_attributes.enable_patchelf() for part in self._project_config.all_parts ] ): # All ELF files have had rpath and interpreter patched. Strip all LD_LIBRARY_PATH variables env = [e for e in env if not e.startswith("export LD_LIBRARY_PATH=")] else: env.append( 'export LD_LIBRARY_PATH="$SNAP_LIBRARY_PATH${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"' ) return "\n".join(env)