blob: 061d22410e5db89cd00baab00e1f0f7e79afaba3 [file] [log] [blame]
# swift_build_support/toolchain.py ------------------------------*- python -*-
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
#
# ----------------------------------------------------------------------------
"""
Represent toolchain - the versioned executables.
"""
# ----------------------------------------------------------------------------
from __future__ import absolute_import
import platform
from . import cache_util
from . import shell
from . import xcrun
from .which import which
__all__ = [
'host_toolchain',
]
class Toolchain(object):
"""Represents native host toolchain
"""
def find_tool(self, *names):
raise NotImplementedError('Subclasses must implement this method')
# Declare properties for each tools.
# These properties are loaded lazily and assignable.
def _register(name, *tool):
def _getter(self):
return self.find_tool(*tool)
_getter.__name__ = name
setattr(Toolchain, name, cache_util.reify(_getter))
_register("cc", "clang")
_register("cxx", "clang++")
_register("ninja", "ninja", "ninja-build")
_register("cmake", "cmake")
_register("distcc", "distcc")
_register("distcc_pump", "distcc-pump", "pump")
_register("llvm_profdata", "llvm-profdata")
_register("llvm_cov", "llvm-cov")
_register("lipo", "lipo")
_register("libtool", "libtool")
class Darwin(Toolchain):
def __init__(self, sdk, toolchain):
super(Darwin, self).__init__()
self.xcrun_sdk = sdk
self.xcrun_toolchain = toolchain
def find_tool(self, *names):
for name in names:
# NOTE: xcrun searches from developer tools directory *and* from
# PATH. Relatively slow, but we don't need `which` for
# Darwin.
found = xcrun.find(name,
sdk=self.xcrun_sdk,
toolchain=self.xcrun_toolchain)
if found is not None:
return found
return None
class GenericUnix(Toolchain):
def __init__(self, suffixes):
super(GenericUnix, self).__init__()
# On these platforms, search 'clang', 'clang++' unconditionally.
# To determine the llvm_suffix.
ret = self.find_clang(['clang', 'clang++'], suffixes)
if ret is None:
self.cc = None
self.cxx = None
# We don't have clang, then we don't have any llvm tools.
self.llvm_suffixes = []
else:
found, suffix = ret
self.cc, self.cxx = found
if suffix == '':
# Some platform may have `clang`, `clang++`, `llvm-cov-3.6`
# but not `llvm-cov`. In that case, we assume `clang` is
# corresponding to the best version of llvm tools found.
self.llvm_suffixes = suffixes
else:
# Otherwise, we must have llvm tools with the same suffix as
# `clang` or `clang++`
self.llvm_suffixes = [suffix]
def find_clang(self, tools, suffixes):
for suffix in suffixes:
ret = [which(t + suffix) for t in tools]
if all(t is not None for t in ret):
return (ret, suffix)
return None
def find_llvm_tool(self, tool):
for suffix in self.llvm_suffixes:
found = which(tool + suffix)
if found is not None:
# If we found the tool with the suffix, lock suffixes to it.
self.llvm_suffix = [suffix]
return found
return None
def find_tool(self, *names):
for name in names:
if name.startswith('llvm-'):
found = self.find_llvm_tool(name)
else:
found = which(name)
if found is not None:
return found
return None
class MacOSX(Darwin):
def __init__(self, toolchain='default'):
super(MacOSX, self).__init__(sdk='macosx', toolchain=toolchain)
class Linux(GenericUnix):
def __init__(self):
super(Linux, self).__init__(['', '-3.8', '-3.7', '-3.6', '-3.5'])
class FreeBSD(GenericUnix):
def __init__(self):
# For testing toolchain initializer on non-FreeBSD systems
sys = platform.system()
if sys != 'FreeBSD':
suffixes = ['']
# See: https://github.com/apple/swift/pull/169
# Building Swift from source requires a recent version of the Clang
# compiler with C++14 support.
elif self._release_date and self._release_date >= 1100000:
suffixes = ['']
else:
suffixes = ['38', '37', '36', '35']
super(FreeBSD, self).__init__(suffixes)
@cache_util.reify
def _release_date(self):
"""Return the release date for FreeBSD operating system on this host.
If the release date cannot be ascertained, return None.
"""
# For details on `sysctl`, see:
# http://www.freebsd.org/cgi/man.cgi?sysctl(8)
out = shell.capture(['sysctl', '-n', 'kern.osreldate'],
dry_run=False, echo=False, optional=True)
if out is None:
return None
return int(out)
class Cygwin(Linux):
# Currently, Cygwin is considered as the same as Linux.
pass
def host_toolchain(**kwargs):
sys = platform.system()
if sys == 'Darwin':
return MacOSX(kwargs.pop('xcrun_toolchain', 'default'))
elif sys == 'Linux':
return Linux()
elif sys == 'FreeBSD':
return FreeBSD()
elif sys.startswith('CYGWIN'):
return Cygwin()
else:
raise NotImplementedError('The platform "%s" does not have a defined '
'toolchain.' % sys)