blob: cb42f52c633ed2a1a1b626c308a5ccbcf8f34389 [file] [log] [blame]
# swift_build_support/targets.py - Build target helpers -*- 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
import os
import platform
from . import shell
try:
from build_swift.build_swift.wrappers import xcrun
except ImportError:
from build_swift.wrappers import xcrun
class Platform(object):
"""
Abstract representation of a platform Swift can run on.
"""
def __init__(self, name, archs, sdk_name=None):
"""
Create a platform with the given name and list of architectures.
"""
self.name = name
self.targets = [Target(self, arch) for arch in archs]
# FIXME: Eliminate this argument; apparently the SDK names are
# internally a private implementation detail of the build script, so we
# should just make them the same as the platform name.
self.sdk_name = name.upper() if sdk_name is None else sdk_name
# Add a property for each arch.
for target in self.targets:
setattr(self, target.arch, target)
@property
def is_darwin(self):
"""Convenience function for checking if this is a Darwin platform."""
return isinstance(self, DarwinPlatform)
@property
def supports_benchmark(self):
# By default, we don't support benchmarks on most platforms.
return False
@property
def uses_host_tests(self):
"""
Check if this is a Darwin platform that needs a connected device
for tests.
"""
# By default, we don't use connected devices on most platforms.
return False
def contains(self, target_name):
"""
Returns True if the given target name belongs to a one of this
platform's targets.
"""
for target in self.targets:
if target.name == target_name:
return True
return False
class DarwinPlatform(Platform):
def __init__(self, name, archs, sdk_name=None, is_simulator=False):
self.is_simulator = is_simulator
super(DarwinPlatform, self).__init__(name, archs, sdk_name)
@property
def is_embedded(self):
"""Check if this is a Darwin platform for embedded devices."""
return self.name != "macosx" and self.name != "maccatalyst"
@property
def supports_benchmark(self):
# By default, on Darwin we support benchmarks on all non-simulator
# platforms.
return not self.is_simulator
@property
def uses_host_tests(self):
"""
Check if this is a Darwin platform that needs a connected device
for tests.
"""
return self.is_embedded and not self.is_simulator
def sdk_supports_architecture(self, arch, toolchain):
"""
Convenience function for checking whether the SDK supports the
target architecture.
"""
# The names match up with the xcrun SDK names.
xcrun_sdk_name = self.name
# 32-bit iOS and iOS simulator are supported, but are not covered
# by the SDK settings. Handle this special case here.
if (xcrun_sdk_name == 'iphoneos' and
(arch == 'armv7' or arch == 'armv7s')):
return True
if (xcrun_sdk_name == 'iphonesimulator' and arch == 'i386'):
return True
sdk_path = xcrun.sdk_path(sdk=xcrun_sdk_name, toolchain=toolchain)
if not sdk_path:
raise RuntimeError('Cannot find SDK path for %s' % xcrun_sdk_name)
# Find the SDKSettings.plist for this sdK
plistCommand = [
'/usr/libexec/PlistBuddy',
'-c',
'Print :SupportedTargets:%s:Archs' % (self.name),
'%s/SDKSettings.plist' % (sdk_path)
]
sdk_archs = shell.capture(plistCommand, dry_run=False, echo=True)
return arch in sdk_archs
class AndroidPlatform(Platform):
@property
def uses_host_tests(self):
"""
Check if this is a Darwin platform that needs a connected device
for tests.
"""
return True
class Target(object):
"""
Abstract representation of a target Swift can run on.
"""
def __init__(self, platform, arch):
self.platform = platform
self.arch = arch
# Delegate to the platform, this is usually not arch specific.
self.supports_benchmark = self.platform.supports_benchmark
@property
def name(self):
return "{}-{}".format(self.platform.name, self.arch)
class StdlibDeploymentTarget(object):
OSX = DarwinPlatform("macosx", archs=["x86_64", "arm64", "arm64e"],
sdk_name="OSX")
iOS = DarwinPlatform("iphoneos", archs=["armv7", "armv7s", "arm64", "arm64e"],
sdk_name="IOS")
iOSSimulator = DarwinPlatform("iphonesimulator", archs=["i386", "x86_64", "arm64"],
sdk_name="IOS_SIMULATOR",
is_simulator=True)
# Never build/test benchmarks on iOS armv7s.
iOS.armv7s.supports_benchmark = False
AppleTV = DarwinPlatform("appletvos", archs=["arm64"],
sdk_name="TVOS")
AppleTVSimulator = DarwinPlatform("appletvsimulator", archs=["x86_64", "arm64"],
sdk_name="TVOS_SIMULATOR",
is_simulator=True)
AppleWatch = DarwinPlatform("watchos", archs=["armv7k"],
sdk_name="WATCHOS")
AppleWatchSimulator = DarwinPlatform("watchsimulator", archs=["i386", "arm64"],
sdk_name="WATCHOS_SIMULATOR",
is_simulator=True)
# A platform that's not tied to any particular OS, and it meant to be used
# to build the stdlib as standalone and/or statically linked.
Freestanding = Platform("freestanding",
archs=["i386", "x86_64", "armv7", "armv7s", "armv7k",
"arm64", "arm64e"])
Linux = Platform("linux", archs=[
"x86_64",
"i686",
"armv6",
"armv7",
"aarch64",
"powerpc64",
"powerpc64le",
"s390x"])
FreeBSD = Platform("freebsd", archs=["x86_64"])
OpenBSD = Platform("openbsd", archs=["amd64"])
Cygwin = Platform("cygwin", archs=["x86_64"])
Android = AndroidPlatform("android", archs=["armv7", "aarch64", "x86_64"])
Windows = Platform("windows", archs=["x86_64"])
Haiku = Platform("haiku", archs=["x86_64"])
# The list of known platforms.
known_platforms = [
OSX,
iOS, iOSSimulator,
AppleTV, AppleTVSimulator,
AppleWatch, AppleWatchSimulator,
Freestanding,
Linux,
FreeBSD,
OpenBSD,
Cygwin,
Android,
Windows,
Haiku]
# Cache of targets by name.
_targets_by_name = dict((target.name, target)
for platform in known_platforms
for target in platform.targets)
_sdk_targets = {
'OSX': OSX.targets,
'IOS': iOS.targets,
'IOS_SIMULATOR': iOSSimulator.targets,
'TVOS': AppleTV.targets,
'TVOS_SIMULATOR': AppleTVSimulator.targets,
'WATCHOS': AppleWatch.targets,
'WATCHOS_SIMULATOR': AppleWatchSimulator.targets,
}
@staticmethod
def host_target():
"""
Return the host target for the build machine, if it is one of
the recognized targets. Otherwise, throw a NotImplementedError.
"""
system = platform.system()
machine = platform.machine()
if system == 'Linux':
if 'ANDROID_DATA' in os.environ:
if machine.startswith('armv7'):
return StdlibDeploymentTarget.Android.armv7
elif machine == 'aarch64':
return StdlibDeploymentTarget.Android.aarch64
raise NotImplementedError('Android System with architecture '
'"%s" is not supported' % machine)
if machine == 'x86_64':
return StdlibDeploymentTarget.Linux.x86_64
elif machine == 'i686':
return StdlibDeploymentTarget.Linux.i686
elif machine.startswith('armv7'):
# linux-armv7* is canonicalized to 'linux-armv7'
return StdlibDeploymentTarget.Linux.armv7
elif machine.startswith('armv6'):
# linux-armv6* is canonicalized to 'linux-armv6'
return StdlibDeploymentTarget.Linux.armv6
elif machine == 'aarch64':
return StdlibDeploymentTarget.Linux.aarch64
elif machine == 'ppc64':
return StdlibDeploymentTarget.Linux.powerpc64
elif machine == 'ppc64le':
return StdlibDeploymentTarget.Linux.powerpc64le
elif machine == 's390x':
return StdlibDeploymentTarget.Linux.s390x
elif system == 'Darwin':
if machine == 'x86_64':
return StdlibDeploymentTarget.OSX.x86_64
elif machine == 'arm64':
return StdlibDeploymentTarget.OSX.arm64
elif machine == 'arm64e':
return StdlibDeploymentTarget.OSX.arm64e
elif system == 'FreeBSD':
if machine == 'amd64':
return StdlibDeploymentTarget.FreeBSD.x86_64
elif system == 'OpenBSD':
if machine == 'amd64':
return StdlibDeploymentTarget.OpenBSD.amd64
elif system == 'CYGWIN_NT-10.0':
if machine == 'x86_64':
return StdlibDeploymentTarget.Cygwin.x86_64
elif system == 'Windows':
if machine == "AMD64":
return StdlibDeploymentTarget.Windows.x86_64
elif system == 'Haiku':
if machine == 'x86_64':
return StdlibDeploymentTarget.Haiku.x86_64
raise NotImplementedError('System "%s" with architecture "%s" is not '
'supported' % (system, machine))
@classmethod
def get_target_for_name(cls, name):
return cls._targets_by_name.get(name)
@classmethod
def get_targets_by_name(cls, names):
return [cls.get_target_for_name(name) for name in names]
@classmethod
def get_target_names(cls):
return sorted([name for (name, target) in
cls._targets_by_name.items()])
@classmethod
def get_migrated_targets_for_sdk(cls, sdk_name):
return cls._sdk_targets.get(sdk_name, None)
@classmethod
def get_all_migrated_sdks(cls):
return cls._sdk_targets.keys()
def install_prefix():
"""
Returns the default path at which built Swift products (like bin, lib,
and include) will be installed, based on the host machine's operating
system.
"""
if platform.system() == 'Darwin':
return '/Applications/Xcode.app/Contents/Developer/Toolchains/' + \
'XcodeDefault.xctoolchain/usr'
else:
return '/usr'
def darwin_toolchain_prefix(darwin_install_prefix):
"""
Given the install prefix for a Darwin system, and assuming that that path
is to a .xctoolchain directory, return the path to the .xctoolchain
directory.
"""
return os.path.split(darwin_install_prefix)[0]
def toolchain_path(install_destdir, install_prefix):
"""
Given the install prefix for a Darwin system, and assuming that that path
is to a .xctoolchain directory, return the path to the .xctoolchain
directory in the given install directory.
This toolchain is being populated during the build-script invocation.
Downstream products can use products that were previously installed into
this toolchain.
"""
built_toolchain_path = install_destdir
if platform.system() == 'Darwin':
# The prefix is an absolute path, so concatenate without os.path.
built_toolchain_path += darwin_toolchain_prefix(install_prefix) + "/usr"
else:
built_toolchain_path += install_prefix
return built_toolchain_path