kiwix-build/kiwixbuild/builder.py

210 lines
8.4 KiB
Python

import sys
from collections import OrderedDict
from .buildenv import *
from .platforms import PlatformInfo
from .utils import remove_duplicates, StopBuild
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(('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 pltName in PlatformInfo.all_running_platforms:
plt = PlatformInfo.all_platforms[pltName]
for tlcName in plt.toolchain_names:
tlc = Dependency.all_deps[tlcName]
src_plt_step = ('source', tlcName)
add_target_step(src_plt_step, self._targets[src_plt_step])
blt_plt_step = ('neutral' if tlc.neutral else pltName, tlcName)
add_target_step(blt_plt_step, self._targets[blt_plt_step])
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("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("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 list -1 | grep -q {}'
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("NEEDED")
packages_to_install.append(package)
else:
print("SKIP")
if packages_to_install:
command = package_installer.format(" ".join(packages_to_install))
print(command)
subprocess.check_call(command, shell=True)
else:
print("SKIP, No package to install.")
def run(self):
try:
print("[INSTALL PACKAGES]")
if option('dont_install_packages'):
print("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("SKIP")
except StopBuild:
sys.exit("Stopping build due to errors")