-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[build support] Generic prefixed binary lookup function in swift_build_support #1586
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
af6f844
6804328
83535dd
0aad53c
f4ba4d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
# -*- python -*- | ||
# swift_build_support/toolchain.py - Detect host machine's versioned | ||
# executables | ||
# | ||
# This source file is part of the Swift.org open source project | ||
# | ||
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors | ||
# Licensed under Apache License v2.0 with Runtime Library Exception | ||
# | ||
# See http://swift.org/LICENSE.txt for license information | ||
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
# | ||
# ---------------------------------------------------------------------------- | ||
# | ||
# Find the path to a Clang executable on the host machine that is most | ||
# suitable for building Swift. | ||
# | ||
# ---------------------------------------------------------------------------- | ||
|
||
from __future__ import absolute_import | ||
|
||
import platform | ||
import subprocess | ||
|
||
from . import xcrun | ||
from .which import which | ||
|
||
|
||
class Toolchain(object): | ||
""" | ||
A class that stores accessible paths to system commands. | ||
|
||
It requires a 'cc' and 'cxx' tool in its keyword args. | ||
""" | ||
def __init__(self, cc=None, cxx=None, **kwargs): | ||
self.cc = cc | ||
self.cxx = cxx | ||
self.tools = [cc, cxx] | ||
for tool, path in kwargs.iteritems(): | ||
self.tools.append(path) | ||
setattr(self, tool, path) | ||
|
||
|
||
def _freebsd_release_date(): | ||
""" | ||
Return the release date for FreeBSD operating system on this host. | ||
If the release date cannot be ascertained, return None. | ||
""" | ||
try: | ||
# For details on `sysctl`, see: | ||
# http://www.freebsd.org/cgi/man.cgi?sysctl(8) | ||
return int(subprocess.check_output( | ||
['sysctl', '-n', 'kern.osreldate']).rstrip()) | ||
except subprocess.CalledProcessError: | ||
return None | ||
|
||
|
||
def _first_common_toolchain(tools, suffixes=None): | ||
""" | ||
Return a Toolchain of resolved paths where each path has | ||
the same suffix. | ||
|
||
If there is no common version of all binaries found, return None. | ||
""" | ||
if suffixes is None: | ||
# No suffixes provided, default to using empty suffix only | ||
suffixes = [''] | ||
|
||
for suffix in suffixes: | ||
path_map = dict() | ||
for name, tool in tools.iteritems(): | ||
path = which(tool + suffix) | ||
if not path: | ||
break | ||
path_map[name] = path | ||
if len(path_map) == len(tools): | ||
return Toolchain(**path_map) | ||
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. Oh, is this why default arguments are provided above? A bit too much metaprogramming for me, but to each their own! 😅 |
||
return None | ||
|
||
|
||
def host_toolchain(xcrun_toolchain='default', tools=None, suffixes=None): | ||
""" | ||
Return a Toolchain with the first available versions of all | ||
specified tools, plus clang and clang++, searching in the order of the | ||
given suffixes. | ||
|
||
If no matching executables are found, return None. | ||
""" | ||
if tools is None: | ||
tools = dict() | ||
|
||
# Require cc and cxx for a toolchain; this ensures whatever llvm tool we | ||
# find matches the version of clang returned by a previous run of | ||
# this tool | ||
tools['cc'] = 'clang' | ||
tools['cxx'] = 'clang++' | ||
|
||
if platform.system() == 'Darwin': | ||
# Only use xcrun on Darwin | ||
path_map = {} | ||
for name, tool in tools.iteritems(): | ||
path = xcrun.find(xcrun_toolchain, tool) | ||
if not path: | ||
return None | ||
path_map[name] = path | ||
return Toolchain(**path_map) | ||
else: | ||
return _first_common_toolchain(tools, suffixes=suffixes) | ||
|
||
|
||
def host_clang(xcrun_toolchain): | ||
""" | ||
Return a Toolchain for the host platform. | ||
If no appropriate compilers can be found, return None. | ||
""" | ||
if platform.system() == 'FreeBSD': | ||
# See: https://github.com/apple/swift/pull/169 | ||
# Building Swift from source requires a recent version of the Clang | ||
# compiler with C++14 support. | ||
freebsd_release_date = _freebsd_release_date() | ||
if freebsd_release_date and freebsd_release_date >= 1100000: | ||
# On newer releases of FreeBSD, the default Clang is sufficient. | ||
return host_toolchain(xcrun_toolchain) | ||
else: | ||
# On older releases, or on releases for which we cannot determine | ||
# the release date, we search for the most modern version | ||
# available. | ||
return host_toolchain(xcrun_toolchain, | ||
suffixes=['38', '37', '36', '35']) | ||
return host_toolchain(xcrun_toolchain, | ||
suffixes=['-3.8', '-3.7', '-3.6', '-3.5']) | ||
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 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. Yep, you're right. Thanks for catching that! Fixed in master. |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# -*- python -*- | ||
# test_toolchain.py - Unit tests for swift_build_support.toolchain | ||
# | ||
# This source file is part of the Swift.org open source project | ||
# | ||
# Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors | ||
# Licensed under Apache License v2.0 with Runtime Library Exception | ||
# | ||
# See http://swift.org/LICENSE.txt for license information | ||
# See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
|
||
import os | ||
import platform | ||
import unittest | ||
|
||
from swift_build_support.toolchain import host_clang, host_toolchain | ||
|
||
|
||
class HostClangTestCase(unittest.TestCase): | ||
def test_clang_available_on_this_platform(self): | ||
# Test that Clang is installed on this platform, as a means of | ||
# testing host_clang(). | ||
clang = host_clang(xcrun_toolchain='default') | ||
|
||
# The CC and CXX from host_clang() should be of the form | ||
# 'path/to/clang', where 'clang' may have a trailing version | ||
# number. | ||
self.assertTrue(os.path.split(clang.cc)[-1].startswith('clang')) | ||
self.assertTrue(os.path.split(clang.cxx)[-1].startswith('clang++')) | ||
|
||
|
||
class HostToolchainTestCase(unittest.TestCase): | ||
def test_found_executables_match(self): | ||
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. nit-pick: This doesn't test |
||
# Test that the raw incovation of _first_common_executables | ||
# either returns None or matching paths. | ||
suffixes = ['', '-3.8', '-3.7', '-3.6'] | ||
toolchain = host_toolchain(suffixes=suffixes) | ||
self.assertTrue(len(toolchain.tools) == 2) | ||
|
||
exec_names = {'foo': 'a-tool-that-does-not-exist'} | ||
toolchain = host_toolchain(tools=exec_names, | ||
suffixes=suffixes) | ||
self.assertIsNone(toolchain) | ||
|
||
@unittest.skipUnless(platform.system() == 'Darwin', | ||
'llvm-cov is only guaranteed to exist on OS X') | ||
def test_can_find_llvm_cov(self): | ||
suffixes = ['', '-3.8', '-3.7', '-3.6'] | ||
exec_names = {'llvm_cov': 'llvm-cov'} | ||
toolchain = host_toolchain(tools=exec_names, suffixes=suffixes) | ||
|
||
# must have clang, clang++, and llvm-cov | ||
self.assertTrue(len(toolchain.tools) == 3) | ||
|
||
try: | ||
toolchain.llvm_cov | ||
except AttributeError: | ||
self.fail("toolchain does not have llvm_cov") | ||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
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.
Sorry if I'm not picking up on something here, but why provide default arguments for arguments the documentation claims are necessary? If you're looking for the ability to use named arguments like
Toolchain(cc="foo", cxx="bar")
, you can do that without supplying default arguments in Python.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 figured it was good to express that these arguments are strictly required, though the same could be expressed with
assert(tools.get('cc') is not None)