-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[build-script] Compute targets in build-script
#2880
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c03943c
ca2d64a
bf75c8f
6df6ae7
f407cdf
bb9fc67
d515078
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,7 +50,7 @@ from swift_build_support.cmake import CMake # noqa (E402) | |
import swift_build_support.workspace # noqa (E402) | ||
|
||
|
||
def call_without_sleeping(command, dry_run=False): | ||
def call_without_sleeping(command, env=None, dry_run=False): | ||
""" | ||
Execute a command during which system sleep is disabled. | ||
|
||
|
@@ -62,7 +62,119 @@ def call_without_sleeping(command, dry_run=False): | |
# Don't mutate the caller's copy of the arguments. | ||
command = ["caffeinate"] + list(command) | ||
|
||
shell.call(command, dry_run=dry_run, echo=False) | ||
shell.call(command, env=env, dry_run=dry_run) | ||
|
||
|
||
class HostSpecificConfiguration(object): | ||
"""Configuration information for an individual host.""" | ||
|
||
def __init__(self, host_target, invocation): | ||
"""Initialize for the given `host_target`.""" | ||
|
||
# Compute the set of deployment targets to configure/build. | ||
args = invocation.args | ||
if host_target == args.host_target: | ||
# This host is the user's desired product, so honor the requested | ||
# set of targets to configure/build. | ||
stdlib_targets_to_configure = args.stdlib_deployment_targets | ||
if "all" in args.build_stdlib_deployment_targets: | ||
stdlib_targets_to_build = set(stdlib_targets_to_configure) | ||
else: | ||
stdlib_targets_to_build = set( | ||
args.build_stdlib_deployment_targets).intersect( | ||
set(args.stdlib_deployment_targets)) | ||
else: | ||
# Otherwise, this is a host we are building as part of | ||
# cross-compiling, so we only need the target itself. | ||
stdlib_targets_to_configure = [host_target] | ||
stdlib_targets_to_build = set(stdlib_targets_to_configure) | ||
|
||
# Compute the lists of **CMake** targets for each use case (configure | ||
# vs. build vs. run) and the SDKs to configure with. | ||
self.sdks_to_configure = set() | ||
self.swift_stdlib_build_targets = [] | ||
self.swift_test_run_targets = [] | ||
self.swift_benchmark_build_targets = [] | ||
self.swift_benchmark_run_targets = [] | ||
for deployment_target_name in stdlib_targets_to_configure: | ||
# Get the target object. | ||
deployment_target = StdlibDeploymentTarget.get_target_for_name( | ||
deployment_target_name) | ||
if deployment_target is None: | ||
diagnostics.fatal("unknown target: %r" % ( | ||
deployment_target_name,)) | ||
|
||
# Add the SDK to use. | ||
deployment_platform = deployment_target.platform | ||
self.sdks_to_configure.add(deployment_platform.sdk_name) | ||
|
||
# If we aren't actually building this target (only configuring | ||
# it), do nothing else. | ||
if deployment_target_name not in stdlib_targets_to_build: | ||
continue | ||
|
||
# Compute which actions are desired. | ||
build = ( | ||
deployment_platform not in invocation.platforms_to_skip_build) | ||
test = ( | ||
deployment_platform not in invocation.platforms_to_skip_test) | ||
test_host_only = None | ||
build_benchmark = build and deployment_target.supports_benchmark | ||
# FIXME: Note, `build-script-impl` computed a property here | ||
# w.r.t. testing, but it was actually unused. | ||
|
||
# For platforms which normally require a connected device to | ||
# test, the default behavior is to run tests that only require | ||
# the host (i.e., they do not attempt to execute). | ||
if deployment_platform.is_darwin and \ | ||
deployment_platform.is_embedded and \ | ||
not deployment_platform.is_simulator: | ||
if deployment_platform not in \ | ||
invocation.platforms_to_skip_test_host: | ||
test_host_only = True | ||
test = True | ||
else: | ||
test = False | ||
|
||
name = deployment_target.name | ||
if build: | ||
# Validation and long tests require building the full standard | ||
# library, whereas the other targets can build a slightly | ||
# smaller subset which is faster to build. | ||
if args.build_swift_stdlib_unittest_extra or \ | ||
args.validation_test or args.long_test: | ||
self.swift_stdlib_build_targets.append( | ||
"swift-stdlib-" + name) | ||
else: | ||
self.swift_stdlib_build_targets.append( | ||
"swift-test-stdlib-" + name) | ||
if build_benchmark: | ||
self.swift_benchmark_build_targets.append( | ||
"swift-benchmark-" + name) | ||
# FIXME: This probably should respect `args.benchmark`, but | ||
# a typo in build-script-impl meant we always would do this. | ||
self.swift_benchmark_run_targets.append( | ||
"check-swift-benchmark-" + name) | ||
if test: | ||
if test_host_only: | ||
suffix = "-non-executable" | ||
else: | ||
suffix = "" | ||
subset_suffix = "" | ||
if args.validation_test and args.long_test: | ||
subset_suffix = "-all" | ||
elif args.validation_test: | ||
subset_suffix = "-validation" | ||
elif args.long_test: | ||
subset_suffix = "-only_long" | ||
else: | ||
subset_suffix = "" | ||
self.swift_test_run_targets.append("check-swift{}{}-{}".format( | ||
subset_suffix, suffix, name)) | ||
if args.test_optimized and not test_host_only: | ||
self.swift_test_run_targets.append( | ||
"check-swift{}-optimize-{}".format( | ||
subset_suffix, name)) | ||
|
||
|
||
class BuildScriptInvocation(object): | ||
|
@@ -291,6 +403,79 @@ class BuildScriptInvocation(object): | |
source_root=SWIFT_SOURCE_ROOT, | ||
build_root=os.path.join(SWIFT_BUILD_ROOT, args.build_subdir)) | ||
|
||
# Compute derived information from the arguments. | ||
# | ||
# FIXME: We should move the platform-derived arguments to be entirely | ||
# data driven, so that we can eliminate this code duplication and just | ||
# iterate over all supported platforms. | ||
|
||
self.platforms_to_skip_build = set() | ||
if args.skip_build_linux: | ||
self.platforms_to_skip_build.add(StdlibDeploymentTarget.Linux) | ||
if args.skip_build_freebsd: | ||
self.platforms_to_skip_build.add(StdlibDeploymentTarget.FreeBSD) | ||
if args.skip_build_cygwin: | ||
self.platforms_to_skip_build.add(StdlibDeploymentTarget.Cygwin) | ||
if args.skip_build_osx: | ||
self.platforms_to_skip_build.add(StdlibDeploymentTarget.OSX) | ||
if args.skip_build_ios_device: | ||
self.platforms_to_skip_build.add(StdlibDeploymentTarget.iOS) | ||
if args.skip_build_ios_simulator: | ||
self.platforms_to_skip_build.add( | ||
StdlibDeploymentTarget.iOSSimulator) | ||
if args.skip_build_tvos_device: | ||
self.platforms_to_skip_build.add(StdlibDeploymentTarget.AppleTV) | ||
if args.skip_build_tvos_simulator: | ||
self.platforms_to_skip_build.add( | ||
StdlibDeploymentTarget.AppleTVSimulator) | ||
if args.skip_build_watchos_device: | ||
self.platforms_to_skip_build.add(StdlibDeploymentTarget.AppleWatch) | ||
if args.skip_build_watchos_simulator: | ||
self.platforms_to_skip_build.add( | ||
StdlibDeploymentTarget.AppleWatchSimulator) | ||
if args.skip_build_android: | ||
self.platforms_to_skip_build.add(StdlibDeploymentTarget.Android) | ||
|
||
self.platforms_to_skip_test = set() | ||
if args.skip_test_linux: | ||
self.platforms_to_skip_test.add(StdlibDeploymentTarget.Linux) | ||
if args.skip_test_freebsd: | ||
self.platforms_to_skip_test.add(StdlibDeploymentTarget.FreeBSD) | ||
if args.skip_test_cygwin: | ||
self.platforms_to_skip_test.add(StdlibDeploymentTarget.Cygwin) | ||
if args.skip_test_osx: | ||
self.platforms_to_skip_test.add(StdlibDeploymentTarget.OSX) | ||
if args.skip_test_ios_host: | ||
self.platforms_to_skip_test.add(StdlibDeploymentTarget.iOS) | ||
if args.skip_test_ios_simulator: | ||
self.platforms_to_skip_test.add( | ||
StdlibDeploymentTarget.iOSSimulator) | ||
if args.skip_test_tvos_host: | ||
self.platforms_to_skip_test.add(StdlibDeploymentTarget.AppleTV) | ||
if args.skip_test_tvos_simulator: | ||
self.platforms_to_skip_test.add( | ||
StdlibDeploymentTarget.AppleTVSimulator) | ||
if args.skip_test_watchos_host: | ||
self.platforms_to_skip_test.add(StdlibDeploymentTarget.AppleWatch) | ||
if args.skip_test_watchos_simulator: | ||
self.platforms_to_skip_test.add( | ||
StdlibDeploymentTarget.AppleWatchSimulator) | ||
# We never allow testing Android, currently. | ||
# | ||
# FIXME: Allow Android host tests to be enabled/disabled by the build | ||
# script. | ||
self.platforms_to_skip_test.add(StdlibDeploymentTarget.Android) | ||
|
||
self.platforms_to_skip_test_host = set() | ||
if args.skip_test_ios_host: | ||
self.platforms_to_skip_test_host.add(StdlibDeploymentTarget.iOS) | ||
if args.skip_test_tvos_host: | ||
self.platforms_to_skip_test_host.add( | ||
StdlibDeploymentTarget.AppleTV) | ||
if args.skip_test_watchos_host: | ||
self.platforms_to_skip_test_host.add( | ||
StdlibDeploymentTarget.AppleWatch) | ||
|
||
def initialize_runtime_environment(self): | ||
"""Change the program environment for building.""" | ||
|
||
|
@@ -322,10 +507,10 @@ class BuildScriptInvocation(object): | |
self.toolchain.ninja = ninja_build.ninja_bin_path | ||
|
||
def convert_to_impl_arguments(self): | ||
"""convert_to_impl_arguments() -> args | ||
"""convert_to_impl_arguments() -> (env, args) | ||
|
||
Convert the invocation to a list of arguments suitable for invoking | ||
`build-script-impl`. | ||
Convert the invocation to an environment and list of arguments suitable | ||
for invoking `build-script-impl`. | ||
""" | ||
|
||
# Create local shadows, for convenience. | ||
|
@@ -374,6 +559,14 @@ class BuildScriptInvocation(object): | |
pipes.quote(arg) for arg in cmake.build_args()), | ||
] | ||
|
||
if args.build_stdlib_deployment_targets: | ||
impl_args += [ | ||
"--build-stdlib-deployment-targets", " ".join( | ||
args.build_stdlib_deployment_targets)] | ||
if args.cross_compile_hosts: | ||
impl_args += [ | ||
"--cross-compile-hosts", " ".join(args.cross_compile_hosts)] | ||
|
||
if toolchain.ninja: | ||
impl_args += ["--ninja-bin=%s" % toolchain.ninja] | ||
if args.distcc: | ||
|
@@ -415,6 +608,8 @@ class BuildScriptInvocation(object): | |
impl_args += ["--skip-build-libdispatch"] | ||
if not args.build_swiftpm: | ||
impl_args += ["--skip-build-swiftpm"] | ||
if args.build_swift_stdlib_unittest_extra: | ||
impl_args += ["--build-swift-stdlib-unittest-extra"] | ||
|
||
if args.skip_build_linux: | ||
impl_args += ["--skip-build-linux"] | ||
|
@@ -536,7 +731,48 @@ class BuildScriptInvocation(object): | |
if args.dry_run: | ||
impl_args += ["--dry-run"] | ||
|
||
return impl_args | ||
# Compute the set of host-specific variables, which we pass through to | ||
# the build script via environment variables. | ||
host_specific_variables = self.compute_host_specific_variables() | ||
impl_env = {} | ||
for (host_target, options) in host_specific_variables.items(): | ||
for (name, value) in options.items(): | ||
# We mangle into an environment variable we can easily evaluate | ||
# from the `build-script-impl`. | ||
impl_env["HOST_VARIABLE_{}__{}".format( | ||
host_target.replace("-", "_"), name)] = value | ||
|
||
return (impl_env, impl_args) | ||
|
||
def compute_host_specific_variables(self): | ||
"""compute_host_specific_variables(args) -> dict | ||
|
||
Compute the host-specific options, organized as a dictionary keyed by | ||
host of options. | ||
""" | ||
|
||
args = self.args | ||
|
||
options = {} | ||
for host_target in [args.host_target] + args.cross_compile_hosts: | ||
# Compute the host specific configuration. | ||
config = HostSpecificConfiguration(host_target, self) | ||
|
||
# Convert into `build-script-impl` style variables. | ||
options[host_target] = { | ||
"SWIFT_SDKS": " ".join(sorted( | ||
config.sdks_to_configure)), | ||
"SWIFT_STDLIB_TARGETS": " ".join( | ||
config.swift_stdlib_build_targets), | ||
"SWIFT_BENCHMARK_TARGETS": " ".join( | ||
config.swift_benchmark_build_targets), | ||
"SWIFT_RUN_BENCHMARK_TARGETS": " ".join( | ||
config.swift_benchmark_run_targets), | ||
"SWIFT_TEST_TARGETS": " ".join( | ||
config.swift_test_run_targets), | ||
} | ||
|
||
return options | ||
|
||
|
||
# Main entry point for the preset mode. | ||
|
@@ -814,13 +1050,27 @@ details of the setups of other systems or automated environments.""") | |
help="The host target. LLVM, Clang, and Swift will be built for this " | ||
"target. The built LLVM and Clang will be used to compile Swift " | ||
"for the cross-compilation targets.", | ||
default=StdlibDeploymentTarget.host_target()) | ||
default=StdlibDeploymentTarget.host_target().name) | ||
targets_group.add_argument( | ||
"--cross-compile-hosts", | ||
help="A space separated list of targets to cross-compile host Swift " | ||
"tools for. Can be used multiple times.", | ||
action=arguments.action.concat, type=arguments.type.shell_split, | ||
default=[]) | ||
stdlib_targets = StdlibDeploymentTarget.default_stdlib_deployment_targets() | ||
targets_group.add_argument( | ||
"--stdlib-deployment-targets", | ||
help="list of targets to compile or cross-compile the Swift standard " | ||
"library for. %(default)s by default.", | ||
nargs="*", | ||
default=StdlibDeploymentTarget.default_stdlib_deployment_targets()) | ||
default=[ | ||
target.name | ||
for target in stdlib_targets]) | ||
targets_group.add_argument( | ||
"--build-stdlib-deployment-targets", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to have a consistent model (from the user's perspective) between cross-compiling the tools and the library. What about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm just porting over existing names from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be OK with adding these as FIXMEs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, I'm not even sure I understand the comment. This flag is just a filter on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, re-reading now I see that my initial message was confusing. What I meant is:
Does this make sense? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I take it we're not supporting cross-compiling for NxM hosts and targets? That is, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I can totally see adding support for building N cross-stdlibs in one run, but I would prefer to limit ourselves to cross-compiling just one compiler, which I think is the common case. If you think cross-compiling multiple compilers would be easy to support, I don't have objections in principle -- especially if there is a compelling usecase. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, actually I was just thinking maybe we shouldn't
If we get a list of lists, we can match up the host with the list of stdlib targets. That would be very cool, and opens up some possibilities: How about we replace it with a verb? Merge
This would work nicely with the explicit build/test lists, and any other options you might want to specify per-host. We already support multiple hosts in one run. I don't think there's any difference between supporting one foreign host or supporting several. It's just another spin of the loop. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a small difference -- for example, we lipo together the foreign compilers for different Darwin slices. But we could just say that if we build any cross-compile any compilers for Darwin hosts, we will lipo them together. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's what we do at the moment. Any lipo-able hosts will get merged together unless you tell it to skip that step (again, should really be opt-in instead of opt-out, but only the Darwin hosts are lipo-able anyway) |
||
help="A space-separated list that filters which of the configured " | ||
"targets to build the Swift standard library for, or 'all'.", | ||
type=arguments.type.shell_split, default=["all"]) | ||
|
||
projects_group = parser.add_argument_group( | ||
title="Options to select projects") | ||
|
@@ -1120,6 +1370,10 @@ details of the setups of other systems or automated environments.""") | |
help="Use the host compiler, not the self-built one to compile the " | ||
"Swift runtime", | ||
action="store_true") | ||
parser.add_argument( | ||
"--build-swift-stdlib-unittest-extra", | ||
help="Build optional StdlibUnittest components", | ||
action="store_true") | ||
|
||
run_build_group = parser.add_argument_group( | ||
title="Run build") | ||
|
@@ -1480,6 +1734,7 @@ details of the setups of other systems or automated environments.""") | |
"--build-jobs", | ||
"--common-cmake-options", | ||
"--only-execute", | ||
"--skip-test-optimized", | ||
action=arguments.action.unavailable) | ||
|
||
args = migration.parse_args(parser, sys.argv[1:]) | ||
|
@@ -1541,10 +1796,12 @@ details of the setups of other systems or automated environments.""") | |
invocation.build_ninja() | ||
|
||
# Convert to a build-script-impl invocation. | ||
build_script_impl_args = invocation.convert_to_impl_arguments() | ||
(build_script_impl_env, build_script_impl_args) = \ | ||
invocation.convert_to_impl_arguments() | ||
|
||
# Execute the underlying build script implementation. | ||
call_without_sleeping([build_script_impl] + build_script_impl_args) | ||
call_without_sleeping([build_script_impl] + build_script_impl_args, | ||
env=build_script_impl_env) | ||
|
||
if args.symbols_package: | ||
print('--- Creating symbols package ---') | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only place we use it, right? Seems easy enough to make generalised. I mean, if this were written in Swift, the compiler would go bananas at you checking for the existence of instance variables based solely on another instance variable. We should set a good example!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean, are you talking about the
is_darwin
flag? Yes it is the only use currently, but I believe there are other places we will need it as the build-script-impl merging progresses.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only have comparable checks in two places:
dsymutil
-- but we could generalise that as anexecutable_format
and check forMach-O
instead; it's technically more accurate and that's useful information for cross-compilers anyway. I builtllvm-dsymutil
for linux, and if I had any Mach-Os on there I needed to strip, I'd be using it.)Mach-O
heading.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This, however, doesn't include examples like the one I pasted above, where we have code duplication that is doing the same thing for three different Darwin embedded platforms, but hadn't been factored into common code.
IMHO, factoring it into common code (even using
is_darwin
) is monotonic improvement that then makes it easier to generalize to a non-Darwin attribute. So I see this as a step in the direction you want...Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That wasn't a great example; host tests are soon not going to be Darwin-exclusive. The fact that they are exclusive isn't actually inherent to Darwin; it's a limitation of what we have implemented right now. That's actually a great example of why not to hide everything behind
is_darwin
-esque magic IMO.Maybe it's bikeshedding, but this is how it starts. Once that
is_darwin
flag is in there, it'll be used for everything.