blob: c7ce8cc1f07b746aa958db31ac383e9c95472303 [file] [log] [blame]
# StagedBuilder.py
#
# The factory provides a staged builder configuration to build the LLVM project unified tree.
from buildbot.plugins import steps, util
from zorg.buildbot.commands.LitTestCommand import LitTestCommand
from zorg.buildbot.process.factory import LLVMBuildFactory, _all_runtimes as LLVMRuntimes
import zorg.buildbot.builders.Util as builders_util
def getCmakeBuildFactory(
llvm_srcdir = None, # A custom LLVM src directory within %(prop:builddir)s of the builder.
generator = "Ninja", # CMake generator.
clean = False, # Do clean build flag.
stages = None, # A list of dict() or LLVMBuildFactory() objects that represent the build stages.
extra_git_args = None, # Extra parameters for steps.Git step (such as 'config', 'workdir' & etc.)
jobs = None, # Restrict a degree of parallelism.
env = None, # Environmental variables for all stages.
):
""" Create and configure a simple multi-staged builder factory to build the LLVM projects from the unified source tree.
This is CMake configurable build that uses Ninja generator by default. Using the other CMake generators
also possible.
Every builder stage includes a pair of cmake configuration and build steps by default.
The optional steps are the checks (local and remote/custom) and the installation steps.
Also the builder factory accepts the LLVMBuildFactory object prepared by any other build factories
as own build stage.
Property Parameters
-------------------
clean : boolean
Clean up the source and the build folders.
clean_obj : boolean
Clean up the build folders.
Parameters
----------
llvm_srcdir : str, optional
A custom LLVM src directory within %(prop:builddir)s of the builder (default is "llvm-project").
(see LLVMBuildFactory for more details).
generator : str, required
The CMake generator (default is 'Ninja').
See CMake documentation for more details.
clean : boolean
Alsways do a clean build (default is False).
stages : list, required
A list of dict() or LLVMBuildFactory() objects with the stage descriptions.
Each object represents a single CMake configurable (dict()) or custom (LLVMBuildFactory()) build configuration.
See [Stage Parameters] section for more details.
extra_git_args : dict, optional
Provide extra arguments for the Git step (default is None).
Sometimes is necessary to pass some additional parameters into the git step, such as
'config', 'workdir' & etc.
jobs : int, optional
Restrict a degree of parallelism (default is None).
env : dict, optional
Common environmental variables for all stages (default is None).
Stage Parameters
----------------
The parameters for the stage object, passed to the factory.
NOTE: All stages get their own 'obj' directories in format %(prop:builddir)s/build/<stage_name>. It cannot be changed.
depends_on_projects : list, optional
A list of the depended projects (default is None).
These project names will be used to set up the proper schedulers. Also these names will be used
to prepare the factory's 'enable_projects' and 'enable_runtimes' lists.
If this parameter is not None and contains the non-runtime project names, they will go to
LLVM_ENABLE_PROJECTS CMake configuration parameter.
enable_runtimes : list, optional
A list of the runtime project names for the build (default is 'auto'). This list goes into
the factory's 'enable_runtimes' attribute and LLVM_ENABLE_RUNTIMES CMake configuration parameter.
If "auto" is specified, the runtime projects will be extracted from 'depends_on_projects' parameter.
If None is specified, LLVM_ENABLE_RUNTIMES will avoided for the CMake configuration step.
(see LLVMBuildFactory for more details).
cmake_definitions : dict, optional
A dictionary of the CMake definitions (default is None).
cmake_options : list, optional
A list of the CMake options (default is None).
targets : list, optional
A list of targets to build (default is ["."]).
Pass '.' in a list to build the default target. Each target gets built with own step.
No build steps will be generated for the build if 'None' was provided with this argument.
install_targets : list, optional
A list of the installation targets (default is ["install"]).
Each target gets installed with own step.
NOTE: the 'install_dir' argument also must be provided to add the installation steps.
checks : list, optional
A list of the check targets (default is ["check-all"]).
Each check target gets executed with own step. The check step uses LitTestCommand
to parse and display the test result logs.
No check steps will be generated for the build if 'None' was provided with this argument.
checks_on_target : list of tuples, optional
A list of commands to run the tests on the remote dev boards (default is None).
This argument expects a list of tuples, where is a name of the test (to display) and
the test command to execute:
```
checks_on_target=[
("libunwind",
["python", "bin/llvm-lit.py", "-v", "-vv", "--threads=32",
"runtimes/runtimes-armv7-unknown-linux-gnueabihf-bins/libunwind/test"]
),
...
]
```
vs : str, optional
Set up Visual Studio environment for the build (default is None).
Possible values are "autodetect", "manual" or None.
vs_arch : str, optional
Provide a build host arch for the Visual Studio tools (default is None)
Possible values are None, "amd64", "x64" & etc.
src_to_build_dir : str, optional
A specific root project directory within the LLVM source code to configure and build (default is "llvm").
Provide a relative path to sub-project within the llvm-project directory to start configuring with instead of
default llvm folder.
As example: pass "flang/runtime" to configure and build the Flang runtime sub-project.
(see LLVMBuildFactory for more details).
install_dir : str, optional
The installation folder (default is None).
Provide the installation folder and the install targets to add the installation steps to the final build.
env : dict, optional
The stage specific environmental variables (default is None).
Returns
-------
Returns the factory object with prepared build steps.
Properties
----------
The factory sets some properties to provide more flexible configuration for the builders:
srcdir
Relative path to the source code root directory ("llvm-project" by default).
"%(prop:builddir)s/%(prop:srcdir)s" is a full path to the source code dir.
srcdir_relative
Relative path from the obj dir to the source code root dir ("../llvm-project" by default).
objrootdir
Relative path to the build directory.
"%(prop:builddir)s/%(prop:objrootdir)s/<stage-name>" is a full path to the stage's obj dir.
depends_on_projects
A list of the depended projects for the build.
enable_projects
A list of enabled projects for all stages.
enable_runtimes
A list of enabled runtimes for all stages.
"""
assert generator, "The CMake generator must be specified."
assert stages, "At least one stage must be specified."
assert isinstance(stages, list), "The 'stages' argument must be a list of dict() or LLVMBuildFactory()."
obj_root_dir = "build"
env = env or {}
# Do not everride TERM just in case.
if not "TERM" in env:
# Be cautious and disable color output from all tools.
env.update({ 'TERM' : 'dumb' })
if not "NINJA_STATUS" in env and generator.upper() == "NINJA":
env.update({ 'NINJA_STATUS' : "%e [%u/%r/%f] " })
# Default root factory. We will collect all steps for all stages here.
f = LLVMBuildFactory(
llvm_srcdir = llvm_srcdir,
obj_dir = obj_root_dir
)
f.addSteps([
# Remove the source code for a clean checkout if requested by property.
steps.RemoveDirectory(
name = 'clean-src-dir',
dir = f.monorepo_dir,
haltOnFailure = False,
flunkOnFailure = False,
doStepIf = util.Property("clean", False) == True,
),
# This is an incremental build, unless otherwise has been requested.
# Remove obj dirs for a clean build.
steps.RemoveDirectory(
name = 'clean-obj-dir',
dir = util.Interpolate(obj_root_dir),
haltOnFailure = False,
flunkOnFailure = False,
doStepIf = lambda step, clean = clean: clean or step.getProperty("clean_obj") == True
),
])
# Get the source code steps at first. We share it between all stages.
# Add the Git step.
extra_git_args = extra_git_args or {}
f.addGetSourcecodeSteps(**extra_git_args)
# Walk over all stages.
stage_factories = []
# Set up consolidated projects/runtimes/dependencies for the result factory.
def normalize_factory(result_factory, stage_factory):
# Add extra depended projects to trigger the schedulers.
if stage_factory.depends_on_projects:
result_factory.depends_on_projects = set(result_factory.depends_on_projects.union(stage_factory.depends_on_projects))
if stage_factory.enable_runtimes:
result_factory.enable_runtimes = frozenset(result_factory.enable_runtimes.union(stage_factory.enable_runtimes))
result_factory.depends_on_projects = set(result_factory.depends_on_projects.union(stage_factory.enable_runtimes))
# Update a list of enabled projects for the result factory.
# LLVMRuntimes => factory._all_runtimes
result_factory.enable_projects = result_factory.depends_on_projects.difference(LLVMRuntimes)
for stage in stages:
# If we got already prepared factory as a build stage, just store it for later processing.
if isinstance(stage, LLVMBuildFactory):
normalize_factory(f, stage)
stage_factories.append(stage)
continue
assert isinstance(stage, dict), "The stage object must be dict() or LLVMBuildFactory() " \
"in StagedBuilder.getCmakeBuildFactory(stages) argument."
stage_name = stage.get("name")
assert stage_name, "A stage name must be specified."
stage_obj_dir = f"{obj_root_dir}/{stage_name}"
depends_on_projects = stage.get("depends_on_projects") # None or a list of the projects.
enable_runtimes = stage.get("enable_runtimes") # None, "auto" or a list of the projects.
cmake_definitions = stage.get("cmake_definitions", {})
cmake_options = stage.get("cmake_options", [])
targets = stage.get("targets", ["."]) # None (default target) or a list of the targets.
checks = stage.get("checks", []) # None (no check) or a list of the check targets.
checks_on_target = stage.get("checks_on_target", []) # None (no target checks) or a list of the target check commands.
src_to_build_dir = stage.get("src_to_build_dir") # None (default: llvm) or a path the project's root inside of the llvm-project directory.
install_dir = stage.get("install_dir")
install_targets = stage.get("install_targets", ["install"])
stage_env = stage.get("env", {})
# VS tools environment variable if using MSVC.
vs = stage.get("vs") # None, "autodetect", "manual"
vs_arch = stage.get("vs_arch") # None, "amd64", "x64" & etc.
# The stage factory. This factory will be merged into the root factory.
stage_f = LLVMBuildFactory(
depends_on_projects = depends_on_projects,
enable_runtimes = enable_runtimes,
llvm_srcdir = llvm_srcdir,
src_to_build_dir = src_to_build_dir,
obj_dir = stage_obj_dir,
install_dir = install_dir
)
stage_env.update(env)
# Configure MSVC environment at first if requested.
if vs:
stage_f.addStep(
steps.SetPropertyFromCommand(
name = "set-pros.vs_env",
command = builders_util.getVisualStudioEnvironment(vs, vs_arch),
extract_fn = builders_util.extractVSEnvironment,
env = stage_env
))
stage_env = util.Property('vs_env')
# CMake command.
if not "LLVM_ENABLE_PROJECTS" in cmake_definitions and stage_f.enable_projects:
cmake_definitions.update({ "LLVM_ENABLE_PROJECTS" : ";".join(stage_f.enable_projects) })
if not "LLVM_ENABLE_RUNTIMES" in cmake_definitions and stage_f.enable_runtimes:
cmake_definitions.update({ "LLVM_ENABLE_RUNTIMES" : ";".join(stage_f.enable_runtimes) })
if not "CMAKE_INSTALL_PREFIX" in cmake_definitions and stage_f.install_dir:
cmake_definitions.update({ "CMAKE_INSTALL_PREFIX" : LLVMBuildFactory.pathRelativeTo(
stage_f.install_dir,
stage_f.obj_dir) })
# Remove all install directories for the stage.
stage_f.addSteps([
steps.RemoveDirectory(
name = f"clean-install-dir-{stage_name:.30}",
dir = util.Interpolate(stage_f.install_dir),
haltOnFailure = False,
flunkOnFailure = False,
doStepIf = lambda step, clean = clean: clean or step.getProperty("clean_obj") == True
),
])
stage_f.addStep(
steps.CMake(
name = f"cmake-configure-{stage_name:.32}",
path = LLVMBuildFactory.pathRelativeTo(stage_f.llvm_srcdir, stage_f.obj_dir),
generator = generator,
definitions = cmake_definitions,
options = cmake_options,
description = ["CMake configure", stage_name],
haltOnFailure = True,
env = stage_env,
workdir = stage_f.obj_dir
))
# Build Commands.
for target in (targets or []):
cmake_build_options = ["--build", "."]
if target != ".":
cmake_build_options.extend(["--target", target])
if jobs:
cmake_build_options.extend(["--", "-j", jobs])
target_title = "default" if target == "." else target
stage_f.addStep(
steps.CMake(
name = f"build-{stage_name}-{target_title}"[:48],
options = cmake_build_options,
description = ["Build", stage_name, "target", target_title],
haltOnFailure = True,
env = stage_env,
workdir = stage_f.obj_dir
))
# Check Commands.
for target in (checks or []):
stage_f.addStep(
LitTestCommand(
name = f"test-{stage_name}-{target}"[:48],
command = [steps.CMake.DEFAULT_CMAKE, "--build", ".", "--target", target],
description = ["Test just built components for", stage_name, ":", target],
descriptionDone = ["Test just built components for", stage_name, ":", target, "completed"],
haltOnFailure = False, # We want to test as much as we could.
env = stage_env,
workdir = stage_f.obj_dir
))
# Target Check Commands.
for check, cmd in checks_on_target:
stage_f.addStep(
LitTestCommand(
name = f"test-{stage_name}-{check}"[:48],
command = cmd,
description = ["Test just built components for", stage_name, ":", check],
descriptionDone = ["Test just built components for", stage_name, ":", check, "completed"],
haltOnFailure = False, # We want to test as much as we could.
env = stage_env,
workdir = stage_f.obj_dir
))
# Install
if stage_f.install_dir:
for target in (install_targets or []):
stage_f.addStep(
steps.CMake(
name = f"install-{stage_name}-{target}"[:48],
options = ["--build", ".", "--target", target],
description = ["Install just built components for", stage_name, ":", target],
haltOnFailure = True,
env = stage_env,
workdir = stage_f.obj_dir
))
# Normalize root factory in according of the current stage recult factory.
normalize_factory(f, stage_f)
# Store the stage factory. We will process them a little bit later.
stage_factories.append(stage_f)
# Finalize the result factory.
f.addSteps([
# Set up some properties, which could be used to configure the builders.
steps.SetProperties(
name = 'set-props',
properties = {
"depends_on_projects" : ";".join(f.depends_on_projects),
"enable_projects" : ";".join(f.enable_projects),
"enable_runtimes" : ";".join(f.enable_runtimes),
"srcdir" : util.Interpolate(f.monorepo_dir),
"srcdir_relative" : util.Interpolate(LLVMBuildFactory.pathRelativeTo(f.monorepo_dir, f"{obj_root_dir}/stage")),
"objrootdir" : util.Interpolate(obj_root_dir),
}
),
])
# Done with all steps for the stage. Now we need to merge these steps into the root factory.
for stage_f in stage_factories:
f.addSteps(stage_f.steps)
return f