Skip to content

Commit ca3e11b

Browse files
committed
[build-script] Determine HOST_CC in build-script
https://bugs.swift.org/browse/SR-237 calls for `build-script` and `build-script-impl` to be merged. This commit takes another step towards that goal by moving the logic that finds the path to the `clang` and `clang++` executables up into Python-land. Rather than simply moving all of the logic into `utils/build-script`, this commit moves relevant functions into a new Python module, named `swift_build_support`. This has several benefits: - The logic can be tested. Whereas `build-script-impl` needed to be run in order to verify its behavior, the logic extracted out of it into `swift_build_support` can be tested in isolation. - The logic can be split up into several files without polluting the `utils` directory, which now contains many different files that are unrelated to `build-script`.
1 parent 4b6d7b9 commit ca3e11b

File tree

14 files changed

+409
-29
lines changed

14 files changed

+409
-29
lines changed

utils/build-script

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ import shutil
1818
import sys
1919
import textwrap
2020

21+
# FIXME: Instead of modifying the system path in order to enable imports from
22+
# other directories, all Python modules related to the build script
23+
# should be moved to the `swift_build_support` module.
24+
# For additional information, see: https://bugs.swift.org/browse/SR-237.
2125
sys.path.append(os.path.dirname(__file__))
22-
2326
from SwiftBuildSupport import (
2427
HOME,
2528
SWIFT_BUILD_ROOT,
@@ -31,6 +34,11 @@ from SwiftBuildSupport import (
3134
quote_shell_command,
3235
)
3336

37+
sys.path.append(os.path.join(os.path.dirname(__file__), 'swift_build_support'))
38+
import swift_build_support.clang
39+
from swift_build_support.migration import migrate_impl_args
40+
41+
3442
# Main entry point for the preset mode.
3543
def main_preset():
3644
parser = argparse.ArgumentParser(
@@ -495,6 +503,10 @@ the number of parallel build jobs to use""",
495503
dest="build_jobs",
496504
default=multiprocessing.cpu_count())
497505

506+
parser.add_argument("--darwin-xcrun-toolchain",
507+
help="the name of the toolchain to use on Darwin",
508+
default="default")
509+
498510
parser.add_argument("--extra-swift-args", help=textwrap.dedent("""
499511
Pass through extra flags to swift in the form of a cmake list 'module_regexp;flag'. Can
500512
be called multiple times to add multiple such module_regexp flag pairs. All semicolons
@@ -505,7 +517,8 @@ the number of parallel build jobs to use""",
505517
help="",
506518
nargs="*")
507519

508-
args = parser.parse_args()
520+
args = parser.parse_args(migrate_impl_args(sys.argv[1:], [
521+
'--darwin-xcrun-toolchain']))
509522

510523
# Build cmark if any cmark-related options were specified.
511524
if (args.cmark_build_variant is not None):
@@ -709,9 +722,18 @@ the number of parallel build jobs to use""",
709722
if args.clean:
710723
shutil.rmtree(build_dir)
711724

725+
host_clang = swift_build_support.clang.host_clang(
726+
xcrun_toolchain=args.darwin_xcrun_toolchain)
727+
if not host_clang:
728+
print_with_argv0(
729+
"Can't find clang. Please install clang-3.5 or a later version.")
730+
return 1
712731
build_script_impl_args = [
713732
os.path.join(SWIFT_SOURCE_ROOT, "swift", "utils", "build-script-impl"),
714733
"--build-dir", build_dir,
734+
"--host-cc", host_clang.cc,
735+
"--host-cxx", host_clang.cxx,
736+
"--darwin-xcrun-toolchain", args.darwin_xcrun_toolchain,
715737
"--cmark-build-type", args.cmark_build_variant,
716738
"--llvm-build-type", args.llvm_build_variant,
717739
"--swift-build-type", args.swift_build_variant,

utils/build-script-impl

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ KNOWN_SETTINGS=(
5757
# name default description
5858
build-args "" "arguments to the build tool; defaults to -j8 when CMake generator is \"Unix Makefiles\""
5959
build-dir "" "out-of-tree build directory; default is in-tree. **This argument is required**"
60+
host-cc "" "the path to CC, the 'clang' compiler for the host platform. **This argument is requied**"
61+
host-cxx "" "the path to CXX, the 'clang++' compiler for the host platform. **This argument is requied**"
6062
darwin-xcrun-toolchain "default" "the name of the toolchain to use on Darwin"
6163
build-ninja "" "build the Ninja tool"
6264
cmark-build-type "Debug" "the CMake build variant for CommonMark (Debug, RelWithDebInfo, Release, MinSizeRel). Defaults to Debug."
@@ -1037,33 +1039,10 @@ if [[ "${EXPORT_COMPILE_COMMANDS}" ]] ; then
10371039
)
10381040
fi
10391041

1040-
if [ -z "${HOST_CC}" ] ; then
1041-
if [ "$(uname -s)" == "Darwin" ] ; then
1042-
HOST_CC="$(xcrun_find_tool clang)"
1043-
HOST_CXX="$(xcrun_find_tool clang++)"
1044-
elif [ "$(uname -s)" == "FreeBSD" ]; then
1045-
if [ $(sysctl -n kern.osreldate) -ge 1100000 ]; then
1046-
HOST_CC="clang"
1047-
HOST_CXX="clang++"
1048-
else
1049-
for clang_candidate_suffix in "38" "37" "36" "35" ; do
1050-
if which "clang${clang_candidate_suffix}" > /dev/null ; then
1051-
HOST_CC="clang${clang_candidate_suffix}"
1052-
HOST_CXX="clang++${clang_candidate_suffix}"
1053-
break
1054-
fi
1055-
done
1056-
fi
1057-
else
1058-
for clang_candidate_suffix in "" "-3.8" "-3.7" "-3.6" "-3.5" ; do
1059-
if which "clang${clang_candidate_suffix}" > /dev/null ; then
1060-
HOST_CC="clang${clang_candidate_suffix}"
1061-
HOST_CXX="clang++${clang_candidate_suffix}"
1062-
break
1063-
fi
1064-
done
1065-
fi
1066-
fi
1042+
# FIXME: HOST_CC is set and its presence is validated by utils/build-script.
1043+
# This check is redundant, but must remain until build-script-impl
1044+
# is merged completely with utils/build-script.
1045+
# For additional information, see: https://bugs.swift.org/browse/SR-237
10671046
if [ -z "${HOST_CC}" ] ; then
10681047
echo "Can't find clang. Please install clang-3.5 or a later version."
10691048
exit 1

utils/swift_build_support/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# swift_build_support
2+
3+
`swift_build_support` is a Python module containing functions and data
4+
structures used by the Swift build script.
5+
6+
You may run unit tests for `swift_build_support` from the command line:
7+
8+
```sh
9+
apple/swift $ python -m unittest discover -s utils/swift_build_support
10+
```
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# swift_build_support/__init__.py - Helpers for building Swift -*- python -*-
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ----------------------------------------------------------------------------
12+
#
13+
# This file needs to be here in order for Python to treat the
14+
# utils/swift_build_support/ directory as a module.
15+
#
16+
# ----------------------------------------------------------------------------
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# swift_build_support/clang.py - Detect host machine's Clang -*- python -*-
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ----------------------------------------------------------------------------
12+
#
13+
# Find the path to a Clang executable on the host machine that is most
14+
# suitable for building Swift.
15+
#
16+
# ----------------------------------------------------------------------------
17+
18+
from __future__ import absolute_import
19+
20+
import collections
21+
import platform
22+
import subprocess
23+
24+
from . import xcrun
25+
from .which import which
26+
27+
28+
# A named tuple consisting of two paths:
29+
# 1. 'cc' is the path to a program used for compiling C.
30+
# 2. 'cxx' is the path to a program used for compiling C++.
31+
CompilerExecutable = collections.namedtuple('CompilerExecutable', 'cc cxx')
32+
33+
34+
def _freebsd_release_date():
35+
"""
36+
Return the release date for FreeBSD operating system on this host.
37+
If the release date cannot be ascertained, return None.
38+
"""
39+
try:
40+
# For details on `sysctl`, see:
41+
# http://www.freebsd.org/cgi/man.cgi?sysctl(8)
42+
return int(subprocess.check_output(
43+
['sysctl', '-n', 'kern.osreldate']).rstrip())
44+
except subprocess.CalledProcessError:
45+
return None
46+
47+
48+
def _first_clang(suffixes):
49+
"""
50+
Return a CompilerExecutable with the first available versions of clang
51+
and clang++, searching in the order of the given suffixes.
52+
53+
If no Clang executables are found, return None.
54+
"""
55+
for suffix in suffixes:
56+
cc_path = which('clang{}'.format(suffix))
57+
cxx_path = which('clang++{}'.format(suffix))
58+
if cc_path and cxx_path:
59+
return CompilerExecutable(cc=cc_path, cxx=cxx_path)
60+
61+
return None
62+
63+
64+
def host_clang(xcrun_toolchain):
65+
"""
66+
Return a CompilerExecutable for the host platform.
67+
If no appropriate compilers can be found, return None.
68+
"""
69+
if platform.system() == 'Darwin':
70+
cc = xcrun.find(xcrun_toolchain, 'clang')
71+
cxx = xcrun.find(xcrun_toolchain, 'clang++')
72+
if cc and cxx:
73+
return CompilerExecutable(cc=cc, cxx=cxx)
74+
else:
75+
return None
76+
elif platform.system() == 'FreeBSD':
77+
# See: https://github.com/apple/swift/pull/169
78+
# Building Swift from source requires a recent version of the Clang
79+
# compiler with C++14 support.
80+
freebsd_release_date = _freebsd_release_date()
81+
if freebsd_release_date and freebsd_release_date >= 1100000:
82+
# On newer releases of FreeBSD, the default Clang is sufficient.
83+
return CompilerExecutable(cc='clang', cxx='clang++')
84+
else:
85+
# On older releases, or on releases for which we cannot determine
86+
# the release date, we search for the most modern version
87+
# available.
88+
return _first_clang(['38', '37', '36', '35'])
89+
else:
90+
return _first_clang(['', '-3.8', '-3.7', '-3.6', '-3.5'])
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# swift_build_support/migration.py - Migrating build-script -*- python -*-
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ----------------------------------------------------------------------------
12+
#
13+
# utils/build-script takes arguments for its argument parser, as well as
14+
# arguments that are meant to be passed directly to utils/build-script-impl.
15+
# In order to gradually migrate away from build-script-impl, this module
16+
# provides tools to handle parsing of these args.
17+
#
18+
# ----------------------------------------------------------------------------
19+
20+
21+
def migrate_impl_args(argv, migrate_args):
22+
"""
23+
Given a list of arguments of the form:
24+
25+
--foo --bar=baz -- --flim=flam
26+
27+
And a list of arguments to migrate, return a list in which the arguments
28+
to migrate come before the '--' separator. For example, were we to migrate
29+
'--flim', we would return:
30+
31+
--foo --bar=baz --flim=flam --
32+
33+
Note that we do not attempt to remove the '--' separator if it is no longer
34+
necessary, nor do we replace '--flim' if it already appears before the
35+
separator. In these cases, argparse "does the right thing": it can handle
36+
a trailing separator, and when options that are specified twice argparse
37+
uses the second value.
38+
"""
39+
try:
40+
split_index = argv.index('--')
41+
except ValueError:
42+
# If there is no separator, then we have nothing to migrate.
43+
return argv
44+
45+
args = argv[:split_index]
46+
impl_args = argv[split_index:]
47+
impl_args_to_remove = []
48+
for index, impl_arg in enumerate(impl_args):
49+
if impl_arg.split('=')[0] in migrate_args:
50+
args.append(impl_arg)
51+
impl_args_to_remove.append(impl_arg)
52+
53+
for impl_arg_to_remove in impl_args_to_remove:
54+
impl_args.remove(impl_arg_to_remove)
55+
56+
return args + impl_args
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# swift_build_support/which.py - shutil.which() for Python 2.7 -*- python -*-
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ----------------------------------------------------------------------------
12+
#
13+
# A naive reimplementation of shutil.which() for Python 2.7. This can be
14+
# removed if shutil.which() is backported, or if the Swift build toolchain
15+
# migrates completely to Python 3.3+.
16+
#
17+
# ----------------------------------------------------------------------------
18+
19+
import subprocess
20+
21+
22+
def which(cmd):
23+
"""
24+
Return the path to an executable which would be run if
25+
the given cmd was called. If no cmd would be called, return None.
26+
27+
Python 3.3+ provides this behavior via the shutil.which() function;
28+
see: https://docs.python.org/3.3/library/shutil.html#shutil.which
29+
30+
We provide our own implementation because shutil.which() has not
31+
been backported to Python 2.7, which we support.
32+
"""
33+
try:
34+
return subprocess.check_output(['which', cmd]).rstrip()
35+
except subprocess.CalledProcessError:
36+
return None
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# swift_build_support/xcrun.py - Invoke xcrun from Python -*- python -*-
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ----------------------------------------------------------------------------
12+
#
13+
# Python wrappers for invoking `xcrun` on the command-line.
14+
#
15+
# ----------------------------------------------------------------------------
16+
17+
import subprocess
18+
19+
20+
def find(toolchain, tool):
21+
"""
22+
Return the path for the given tool, according to `xcrun --find`, using
23+
the given toolchain. If `xcrun --find` cannot find the tool, return None.
24+
"""
25+
try:
26+
# `xcrun --find` prints to stderr when it fails to find the
27+
# given tool. We swallow that output with a pipe.
28+
out = subprocess.check_output(['xcrun', '--sdk', 'macosx',
29+
'--toolchain', toolchain,
30+
'--find', tool],
31+
stderr=subprocess.PIPE)
32+
return out.rstrip()
33+
except subprocess.CalledProcessError:
34+
return None
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# swift_build_support/tests/__init__.py - Test module -*- python -*-
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See http://swift.org/LICENSE.txt for license information
9+
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ----------------------------------------------------------------------------
12+
#
13+
# This file needs to be here in order for Python to treat the
14+
# utils/swift_build_support/tests/ directory as a module.
15+
#
16+
# ----------------------------------------------------------------------------

0 commit comments

Comments
 (0)