import sys from collections import OrderedDict from .buildenv import * from .platforms import PlatformInfo from .utils import remove_duplicates, StopBuild, colorize from .dependencies import Dependency from .packages import PACKAGE_NAME_MAPPERS from ._global import ( neutralEnv, option, add_target_step, get_target_step, target_steps, backend) from . import _global class Builder: def __init__(self): self._targets = {} PlatformInfo.get_platform('neutral', self._targets) target_platform = option('target_platform') platform = PlatformInfo.get_platform(target_platform, self._targets) if neutralEnv('distname') not in platform.compatible_hosts: print((colorize('ERROR')+': The target platform {} cannot be build on host {}.\n' 'Select another target platform or change your host system.' ).format(platform.name, neutralEnv('distname'))) self.targetDefs = platform.add_targets(option('target'), self._targets) def finalize_target_steps(self): steps = [] for targetDef in self.targetDefs: steps += self.order_steps(targetDef) steps = list(remove_duplicates(steps)) if option('build_nodeps'): # add all platform steps for dep in steps: stepClass = Dependency.all_deps[dep[1]] if stepClass.dont_skip: add_target_step(dep, self._targets[dep]) src_targetDef = ('source', targetDef[1]) add_target_step(src_targetDef, self._targets[src_targetDef]) add_target_step(targetDef, self._targets[targetDef]) else: for dep in steps: if option('build_deps_only') and dep[1] == targetDef[1]: continue add_target_step(dep, self._targets[dep]) self.instanciate_steps() def order_steps(self, targetDef): for pltName in PlatformInfo.all_running_platforms: plt = PlatformInfo.all_platforms[pltName] for tlcName in plt.toolchain_names: tlc = Dependency.all_deps[tlcName] yield('source', tlcName) yield('neutral' if tlc.neutral else pltName, tlcName) _targets =dict(self._targets) yield from self.order_dependencies(targetDef, _targets) def order_dependencies(self, targetDef, targets): targetPlatformName, targetName = targetDef if targetPlatformName == 'source': # Do not try to order sources, they will be added as dep by the # build step two lines later. return try: target = targets.pop(targetDef) except KeyError: return targetPlatform = PlatformInfo.get_platform(targetPlatformName) for dep in target.get_dependencies(targetPlatform, True): depPlatform, depName = targetPlatform.get_fully_qualified_dep(dep) if (depPlatform, depName) in targets: yield from self.order_dependencies((depPlatform, depName), targets) yield ('source', targetName) yield targetDef def instanciate_steps(self): for stepDef in list(target_steps()): stepPlatform, stepName = stepDef stepClass = Dependency.all_deps[stepName] if stepPlatform == 'source': source = get_target_step(stepDef)(stepClass) add_target_step(stepDef, source) else: source = get_target_step(stepName, 'source') env = PlatformInfo.get_platform(stepPlatform).buildEnv builder = get_target_step(stepDef)(stepClass, source, env) add_target_step(stepDef, builder) def prepare_sources(self): if option('skip_source_prepare'): print(colorize("SKIP")) return sourceDefs = remove_duplicates(tDef for tDef in target_steps() if tDef[0]=='source') for sourceDef in sourceDefs: print("prepare sources {} :".format(sourceDef[1])) source = get_target_step(sourceDef) source.prepare() def build(self): builderDefs = (tDef for tDef in target_steps() if tDef[0] != 'source') for builderDef in builderDefs: builder = get_target_step(builderDef) if option('make_dist') and builderDef[1] == option('target'): print("make dist {} ({}):".format(builder.name, builderDef[0])) builder.make_dist() continue print("build {} ({}):".format(builder.name, builderDef[0])) add_target_step(builderDef, builder) builder.build() def _get_packages(self): packages_list = [] for platform in PlatformInfo.all_running_platforms.values(): mapper_name = "{host}_{target}".format( host=neutralEnv('distname'), target=platform) package_name_mapper = PACKAGE_NAME_MAPPERS.get(mapper_name, {}) packages_list += package_name_mapper.get('COMMON', []) to_drop = [] for builderDef in self._targets: platformName, builderName = builderDef mapper_name = "{host}_{target}".format( host=neutralEnv('distname'), target=platformName) package_name_mapper = PACKAGE_NAME_MAPPERS.get(mapper_name, {}) packages = package_name_mapper.get(builderName) if packages: to_drop.append(builderDef) if packages is not True: # True means "assume the dependency is install but do not try to install anything for it" packages_list += packages for dep in to_drop: del self._targets[dep] return packages_list def install_packages(self): packages_to_have = self._get_packages() packages_to_have = remove_duplicates(packages_to_have) if option('assume_packages_installed'): print(colorize("SKIP") + ", Assume package installed") return distname = neutralEnv('distname') if distname in ('fedora', 'redhat', 'centos'): package_installer = 'sudo dnf install {}' package_checker = 'rpm -q --quiet {}' elif distname in ('debian', 'Ubuntu'): package_installer = 'sudo apt-get install {}' package_checker = 'LANG=C dpkg -s {} 2>&1 | grep Status | grep "ok installed" 1>/dev/null 2>&1' elif distname == 'Darwin': package_installer = 'brew install {}' package_checker = 'brew ls --version {} > /dev/null' packages_to_install = [] for package in packages_to_have: print(" - {} : ".format(package), end="") command = package_checker.format(package) try: subprocess.check_call(command, shell=True) except subprocess.CalledProcessError: print(colorize("NEEDED")) packages_to_install.append(package) else: print(colorize("SKIP")) if packages_to_install: command = package_installer.format(" ".join(packages_to_install)) print(command) subprocess.check_call(command, shell=True) else: print(colorize("SKIP")+ ", No package to install.") def run(self): try: print("[INSTALL PACKAGES]") if option('dont_install_packages'): print(colorize("SKIP")) else: self.install_packages() self.finalize_target_steps() print("[SETUP PLATFORMS]") for platform in PlatformInfo.all_running_platforms.values(): platform.finalize_setup() print("[PREPARE]") self.prepare_sources() print("[BUILD]") self.build() # No error, clean intermediate file at end of build if needed. print("[CLEAN]") if option('clean_at_end'): for platform in PlatformInfo.all_running_platforms.values(): platform.clean_intermediate_directories() else: print(colorize("SKIP")) except StopBuild as e: print(e) sys.exit("Stopping build due to errors")