Skip to content

Commit 7c502b6

Browse files
zhuoweimodocache
authored andcommitted
Port to Android
This adds an Android target for the stdlib. It is also the first example of cross-compiling outside of Darwin. Mailing list discussions: 1. https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151207/000171.html 2. https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000492.html The Android variant of Swift may be built using the following `build-script` invocation: ``` $ utils/build-script \ -R \ # Build in ReleaseAssert mode. --android \ # Build for Android. --android-ndk ~/android-ndk-r10e \ # Path to an Android NDK. --android-ndk-version 21 \ --android-icu-uc ~/libicu-android/armeabi-v7a/libicuuc.so \ --android-icu-uc-include ~/libicu-android/armeabi-v7a/icu/source/common \ --android-icu-i18n ~/libicu-android/armeabi-v7a/libicui18n.so \ --android-icu-i18n-include ~/libicu-android/armeabi-v7a/icu/source/i18n/ ``` Android builds have the following dependencies, as can be seen in the build script invocation: 1. An Android NDK of version 21 or greater, available to download here: http://developer.android.com/ndk/downloads/index.html. 2. A libicu compatible with android-armv7.
1 parent 91f0284 commit 7c502b6

34 files changed

+592
-149
lines changed

CMakeLists.txt

+37-1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,20 @@ option(SWIFT_ENABLE_LTO
137137
# The following only works with the Ninja generator in CMake >= 3.0.
138138
set(SWIFT_PARALLEL_LINK_JOBS "" CACHE STRING
139139
"Define the maximum number of linker jobs for swift.")
140+
set(SWIFT_ANDROID_NDK_PATH "" CACHE STRING
141+
"Path to the directory that contains the Android NDK tools that are executable on the build machine")
142+
set(SWIFT_ANDROID_NDK_TOOLCHAIN_VERSION "" CACHE STRING
143+
"A version of the toolchain to use when building for Android. Use 4.8 for 32-bit builds, 4.9 for 64-bit builds")
144+
set(SWIFT_ANDROID_SDK_PATH "" CACHE STRING
145+
"Path to the directory that contains the Android SDK tools that will be passed to the swiftc frontend")
146+
set(SWIFT_ANDROID_ICU_UC "" CACHE STRING
147+
"Path to a directory containing libicuuc.so")
148+
set(SWIFT_ANDROID_ICU_UC_INCLUDE "" CACHE STRING
149+
"Path to a directory containing headers for libicuuc")
150+
set(SWIFT_ANDROID_ICU_I18N "" CACHE STRING
151+
"Path to a directory containing libicui18n.so")
152+
set(SWIFT_ANDROID_ICU_I18N_INCLUDE "" CACHE STRING
153+
"Path to a directory containing headers libicui18n")
140154

141155
#
142156
# User-configurable Darwin-specific options.
@@ -421,9 +435,31 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
421435

422436
# FIXME: This will not work while trying to cross-compile.
423437
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64")
424-
configure_sdk_unix(LINUX "Linux" "linux" "linux" "x86_64" "x86_64-unknown-linux-gnu")
425438
set(SWIFT_HOST_VARIANT_ARCH "x86_64")
426439
set(SWIFT_PRIMARY_VARIANT_ARCH_default "x86_64")
440+
441+
if("${SWIFT_ANDROID_NDK_PATH}" STREQUAL "")
442+
set(swift_can_crosscompile_stdlib FALSE)
443+
else()
444+
set(swift_can_crosscompile_stdlib TRUE)
445+
endif()
446+
447+
is_sdk_requested(LINUX swift_build_linux)
448+
if(swift_build_linux)
449+
configure_sdk_unix(LINUX "Linux" "linux" "linux" "x86_64" "x86_64-unknown-linux-gnu")
450+
set(SWIFT_PRIMARY_VARIANT_SDK_default "LINUX")
451+
set(SWIFT_PRIMARY_VARIANT_ARCH_default "x86_64")
452+
endif()
453+
454+
is_sdk_requested(ANDROID swift_build_android)
455+
if(swift_build_android AND ${swift_can_crosscompile_stdlib})
456+
configure_sdk_unix(ANDROID "Android" "android" "android" "armv7" "armv7-none-linux-androideabi")
457+
set(SWIFT_SDK_ANDROID_PATH "${SWIFT_ANDROID_SDK_PATH}")
458+
459+
set(SWIFT_PRIMARY_VARIANT_SDK_default "ANDROID")
460+
set(SWIFT_PRIMARY_VARIANT_ARCH_default "armv7")
461+
endif()
462+
427463
# FIXME: This only matches ARMv6l (by far the most common variant).
428464
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv6l")
429465
configure_sdk_unix(LINUX "Linux" "linux" "linux" "armv6" "armv6-unknown-linux-gnueabihf")

cmake/modules/AddSwift.cmake

+23-2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ function(_add_variant_c_compile_link_flags)
7373
list(APPEND result
7474
"-isysroot" "${SWIFT_SDK_${CFLAGS_SDK}_PATH}")
7575

76+
if("${CFLAGS_SDK}" STREQUAL "ANDROID")
77+
list(APPEND result
78+
"--sysroot=${SWIFT_ANDROID_SDK_PATH}"
79+
# Use the linker included in the Android NDK.
80+
"-B" "${SWIFT_ANDROID_NDK_PATH}/toolchains/arm-linux-androideabi-${SWIFT_ANDROID_NDK_TOOLCHAIN_VERSION}/prebuilt/linux-x86_64/arm-linux-androideabi/bin/")
81+
endif()
82+
83+
7684
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
7785

7886
# Check if there's a specific iOS deployment version needed for this invocation
@@ -156,6 +164,13 @@ function(_add_variant_c_compile_flags)
156164
"-fcoverage-mapping")
157165
endif()
158166

167+
if("${CFLAGS_SDK}" STREQUAL "ANDROID")
168+
list(APPEND result
169+
"-I${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++/libcxx/include"
170+
"-I${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++abi/libcxxabi/include"
171+
"-I${SWIFT_ANDROID_NDK_PATH}/sources/android/support/include")
172+
endif()
173+
159174
set("${CFLAGS_RESULT_VAR_NAME}" "${result}" PARENT_SCOPE)
160175
endfunction()
161176

@@ -224,7 +239,13 @@ function(_add_variant_link_flags)
224239
elseif("${LFLAGS_SDK}" STREQUAL "FREEBSD")
225240
list(APPEND result "-lpthread")
226241
elseif("${LFLAGS_SDK}" STREQUAL "CYGWIN")
227-
# NO extra libraries required.
242+
# No extra libraries required.
243+
elseif("${LFLAGS_SDK}" STREQUAL "ANDROID")
244+
list(APPEND result
245+
"-ldl"
246+
"-L${SWIFT_ANDROID_NDK_PATH}/toolchains/arm-linux-androideabi-${SWIFT_ANDROID_NDK_TOOLCHAIN_VERSION}/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/${SWIFT_ANDROID_NDK_TOOLCHAIN_VERSION}"
247+
"${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_shared.so"
248+
"-L${SWIFT_ANDROID_ICU_UC}" "-L${SWIFT_ANDROID_ICU_I18N}")
228249
else()
229250
list(APPEND result "-lobjc")
230251
endif()
@@ -997,7 +1018,7 @@ function(_add_swift_library_single target name)
9971018
set_target_properties("${target}"
9981019
PROPERTIES
9991020
INSTALL_NAME_DIR "${install_name_dir}")
1000-
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
1021+
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND NOT "${SWIFTLIB_SINGLE_SDK}" STREQUAL "ANDROID")
10011022
set_target_properties("${target}"
10021023
PROPERTIES
10031024
INSTALL_RPATH "$ORIGIN:/usr/lib/swift/linux")

cmake/modules/FindICU.cmake

+7
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,12 @@ foreach(MODULE ${ICU_FIND_COMPONENTS})
2525
endif()
2626
endforeach()
2727

28+
if(NOT "${SWIFT_ANDROID_ICU_UC_INCLUDE}" STREQUAL "")
29+
set(ICU_UC_INCLUDE_DIR "${SWIFT_ANDROID_ICU_UC_INCLUDE}")
30+
endif()
31+
if(NOT "${SWIFT_ANDROID_ICU_I18N_INCLUDE}" STREQUAL "")
32+
set(ICU_I18N_INCLUDE_DIR "${SWIFT_ANDROID_ICU_I18N_INCLUDE}")
33+
endif()
34+
2835
find_package_handle_standard_args(ICU DEFAULT_MSG ${ICU_REQUIRED})
2936
mark_as_advanced(${ICU_REQUIRED})

include/swift/Basic/LangOptions.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ namespace swift {
184184
} else if (Target.isWatchOS()) {
185185
Target.getOSVersion(major, minor, revision);
186186
} else if (Target.isOSLinux() || Target.isOSFreeBSD() ||
187-
Target.isOSWindows() ||
187+
Target.isAndroid() || Target.isOSWindows() ||
188188
Target.getTriple().empty())
189189
{
190190
major = minor = revision = 0;

lib/Basic/LangOptions.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ static const StringRef SupportedConditionalCompilationOSs[] = {
3030
"iOS",
3131
"Linux",
3232
"FreeBSD",
33-
"Windows"
33+
"Windows",
34+
"Android"
3435
};
3536

3637
static const StringRef SupportedConditionalCompilationArches[] = {
@@ -105,6 +106,8 @@ std::pair<bool, bool> LangOptions::setTarget(llvm::Triple triple) {
105106
addPlatformConditionValue("os", "watchOS");
106107
else if (triple.isiOS())
107108
addPlatformConditionValue("os", "iOS");
109+
else if (triple.isAndroid())
110+
addPlatformConditionValue("os", "Android");
108111
else if (triple.isOSLinux())
109112
addPlatformConditionValue("os", "Linux");
110113
else if (triple.isOSFreeBSD())

lib/Basic/Platform.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ StringRef swift::getPlatformNameForTriple(const llvm::Triple &triple) {
8888
if (triple.isOSDarwin())
8989
return getPlatformNameForDarwin(getDarwinPlatformKind(triple));
9090

91+
if (triple.isAndroid())
92+
return "android";
93+
9194
if (triple.isOSLinux())
9295
return "linux";
9396

lib/ClangImporter/MappedTypes.def

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ MAP_STDLIB_TYPE("u_int64_t", UnsignedInt, 64, "UInt64", false, DoNothing)
127127
// FIXME: why does this not catch va_list on x86_64?
128128
MAP_STDLIB_TYPE("va_list", VaList, 0, "CVaListPointer", false, DoNothing)
129129
MAP_STDLIB_TYPE("__gnuc_va_list", VaList, 0, "CVaListPointer", false, DoNothing)
130+
MAP_STDLIB_TYPE("__va_list", VaList, 0, "CVaListPointer", false, DoNothing)
130131

131132
// libkern/OSTypes.h types.
132133
MAP_STDLIB_TYPE("UInt", UnsignedInt, 32, "CUnsignedInt", false, DoNothing)

lib/Driver/Driver.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -2031,6 +2031,12 @@ const ToolChain *Driver::getToolChain(const ArgList &Args) const {
20312031
TC = new toolchains::Darwin(*this, Target);
20322032
break;
20332033
case llvm::Triple::Linux:
2034+
if (Target.isAndroid()) {
2035+
TC = new toolchains::Android(*this, Target);
2036+
} else {
2037+
TC = new toolchains::GenericUnix(*this, Target);
2038+
}
2039+
break;
20342040
case llvm::Triple::FreeBSD:
20352041
TC = new toolchains::GenericUnix(*this, Target);
20362042
break;

lib/Driver/ToolChains.cpp

+25-8
Original file line numberDiff line numberDiff line change
@@ -1210,11 +1210,11 @@ std::string toolchains::GenericUnix::getDefaultLinker() const {
12101210
}
12111211
}
12121212

1213-
bool toolchains::GenericUnix::shouldProvideRPathToLinker() const {
1214-
return true;
1213+
std::string toolchains::GenericUnix::getTargetForLinker() const {
1214+
return getTriple().str();
12151215
}
12161216

1217-
bool toolchains::GenericUnix::shouldSpecifyTargetTripleToLinker() const {
1217+
bool toolchains::GenericUnix::shouldProvideRPathToLinker() const {
12181218
return true;
12191219
}
12201220

@@ -1266,9 +1266,10 @@ toolchains::GenericUnix::constructInvocation(const LinkJobAction &job,
12661266
Arguments.push_back(context.Args.MakeArgString("-fuse-ld=" + Linker));
12671267
}
12681268

1269-
// Explicitly pass the target to the linker
1270-
if (shouldSpecifyTargetTripleToLinker()) {
1271-
Arguments.push_back(context.Args.MakeArgString("--target=" + getTriple().str()));
1269+
std::string Target = getTargetForLinker();
1270+
if (!Target.empty()) {
1271+
Arguments.push_back("-target");
1272+
Arguments.push_back(context.Args.MakeArgString(Target));
12721273
}
12731274

12741275
// Add the runtime library link path, which is platform-specific and found
@@ -1340,13 +1341,29 @@ toolchains::GenericUnix::constructInvocation(const LinkJobAction &job,
13401341
return {"clang++", Arguments};
13411342
}
13421343

1344+
std::string
1345+
toolchains::Android::getTargetForLinker() const {
1346+
// Explicitly set the linker target to "androideabi", as opposed to the
1347+
// llvm::Triple representation of "armv7-none-linux-android".
1348+
// This is the only ABI we currently support for Android.
1349+
assert(
1350+
getTriple().getArch() == llvm::Triple::arm &&
1351+
getTriple().getSubArch() == llvm::Triple::SubArchType::ARMSubArch_v7 &&
1352+
"Only armv7 targets are supported for Android");
1353+
return "armv7-none-linux-androideabi";
1354+
}
1355+
1356+
bool toolchains::Android::shouldProvideRPathToLinker() const {
1357+
return false;
1358+
}
1359+
13431360
std::string toolchains::Cygwin::getDefaultLinker() const {
13441361
// Cygwin uses the default BFD linker, even on ARM.
13451362
return "";
13461363
}
13471364

1348-
bool toolchains::Cygwin::shouldSpecifyTargetTripleToLinker() const {
1349-
return false;
1365+
std::string toolchains::Cygwin::getTargetForLinker() const {
1366+
return "";
13501367
}
13511368

13521369
std::string toolchains::Cygwin::getPreInputObjectPath(

lib/Driver/ToolChains.h

+16-6
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,17 @@ class LLVM_LIBRARY_VISIBILITY GenericUnix : public ToolChain {
5353
/// and to not provide a specific linker otherwise.
5454
virtual std::string getDefaultLinker() const;
5555

56+
/// The target to be passed to the compiler invocation. By default, this
57+
/// is the target triple, but this may be overridden to accomodate some
58+
/// platforms.
59+
virtual std::string getTargetForLinker() const;
60+
5661
/// Whether to specify a linker -rpath to the Swift runtime library path.
5762
/// -rpath is not supported on all platforms, and subclasses may override
5863
/// this method to return false on platforms that don't support it. The
5964
/// default is to return true (and so specify an -rpath).
6065
virtual bool shouldProvideRPathToLinker() const;
6166

62-
/// Whether to explicitly specify the target triple as the linker
63-
/// '--target'. This is not desirable on all platforms, and subclasses may
64-
/// override this method to return false in those cases.
65-
virtual bool shouldSpecifyTargetTripleToLinker() const;
66-
6767
/// Provides a path to an object that should be linked first. On platforms
6868
/// that use ELF binaries, an object that provides markers and sizes for
6969
/// metadata sections must be linked first. Platforms that do not need this
@@ -96,11 +96,21 @@ class LLVM_LIBRARY_VISIBILITY GenericUnix : public ToolChain {
9696
~GenericUnix() = default;
9797
};
9898

99+
class LLVM_LIBRARY_VISIBILITY Android : public GenericUnix {
100+
protected:
101+
std::string getTargetForLinker() const override;
102+
103+
bool shouldProvideRPathToLinker() const override;
104+
public:
105+
Android(const Driver &D, const llvm::Triple &Triple) : GenericUnix(D, Triple) {}
106+
~Android() = default;
107+
};
108+
99109
class LLVM_LIBRARY_VISIBILITY Cygwin : public GenericUnix {
100110
protected:
101111
std::string getDefaultLinker() const override;
102112

103-
bool shouldSpecifyTargetTripleToLinker() const override;
113+
std::string getTargetForLinker() const override;
104114

105115
std::string getPreInputObjectPath(
106116
StringRef RuntimeLibraryPath) const override;

stdlib/private/StdlibUnittest/RaceTest.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import SwiftPrivate
4040
import SwiftPrivatePthreadExtras
4141
#if os(OSX) || os(iOS)
4242
import Darwin
43-
#elseif os(Linux) || os(FreeBSD)
43+
#elseif os(Linux) || os(FreeBSD) || os(Android)
4444
import Glibc
4545
#endif
4646

stdlib/private/StdlibUnittest/StdlibCoreExtras.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import SwiftPrivate
1414
import SwiftPrivateLibcExtras
1515
#if os(OSX) || os(iOS)
1616
import Darwin
17-
#elseif os(Linux) || os(FreeBSD)
17+
#elseif os(Linux) || os(FreeBSD) || os(Android)
1818
import Glibc
1919
#endif
2020

stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb

+19-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import SwiftPrivateLibcExtras
1616

1717
#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
1818
import Darwin
19-
#elseif os(Linux) || os(FreeBSD)
19+
#elseif os(Linux) || os(FreeBSD) || os(Android)
2020
import Glibc
2121
#endif
2222

@@ -1170,6 +1170,7 @@ public enum OSVersion : CustomStringConvertible {
11701170
case watchOSSimulator
11711171
case linux
11721172
case freeBSD
1173+
case android
11731174

11741175
public var description: String {
11751176
switch self {
@@ -1191,6 +1192,8 @@ public enum OSVersion : CustomStringConvertible {
11911192
return "Linux"
11921193
case freeBSD:
11931194
return "FreeBSD"
1195+
case android:
1196+
return "Android"
11941197
}
11951198
}
11961199
}
@@ -1225,6 +1228,8 @@ func _getOSVersion() -> OSVersion {
12251228
return .linux
12261229
#elseif os(FreeBSD)
12271230
return .freeBSD
1231+
#elseif os(Android)
1232+
return .android
12281233
#else
12291234
let productVersion = _stdlib_getSystemVersionPlistProperty("ProductVersion")!
12301235
let (major, minor, bugFix) = _parseDottedVersionTriple(productVersion)
@@ -1299,6 +1304,8 @@ public enum TestRunPredicate : CustomStringConvertible {
12991304

13001305
case freeBSDAny(reason: String)
13011306

1307+
case androidAny(reason: String)
1308+
13021309
case objCRuntime(/*reason:*/ String)
13031310
case nativeRuntime(/*reason:*/ String)
13041311

@@ -1376,6 +1383,9 @@ public enum TestRunPredicate : CustomStringConvertible {
13761383
case linuxAny(reason: let reason):
13771384
return "linuxAny(*, reason: \(reason))"
13781385

1386+
case androidAny(reason: let reason):
1387+
return "androidAny(*, reason: \(reason))"
1388+
13791389
case freeBSDAny(reason: let reason):
13801390
return "freeBSDAny(*, reason: \(reason))"
13811391

@@ -1620,6 +1630,14 @@ public enum TestRunPredicate : CustomStringConvertible {
16201630
return false
16211631
}
16221632

1633+
case androidAny:
1634+
switch _getRunningOSVersion() {
1635+
case .android:
1636+
return true
1637+
default:
1638+
return false
1639+
}
1640+
16231641
case freeBSDAny:
16241642
switch _getRunningOSVersion() {
16251643
case .freeBSD:

0 commit comments

Comments
 (0)