Skip to content

Commit 92abde1

Browse files
zhuoweimodocache
authored andcommitted
[RFC] 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 and tested using the following `build-script` invocation: ``` $ utils/build-script \ -R \ # Build in ReleaseAssert mode. -T \ # Run all tests. --android \ # Build for Android. --android-deploy-device-path /data/local/tmp \ # Temporary directory on the device where Android tests are run. --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 5c6d986 commit 92abde1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1597
-36
lines changed

CMakeLists.txt

+34-1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,21 @@ set(SWIFT_NATIVE_SWIFT_TOOLS_PATH "" CACHE STRING
125125
option(SWIFT_ENABLE_LTO
126126
"If set to true, build the swift compiler with link time optimization enabled" FALSE)
127127

128+
set(SWIFT_ANDROID_NDK_PATH "" CACHE STRING
129+
"Path to the directory that contains the Android NDK tools that are executable on the build machine")
130+
set(SWIFT_ANDROID_NDK_TOOLCHAIN_VERSION "" CACHE STRING
131+
"A version of the toolchain to use when building for Android. Use 4.8 for 32-bit builds, 4.9 for 64-bit builds")
132+
set(SWIFT_ANDROID_SDK_PATH "" CACHE STRING
133+
"Path to the directory that contains the Android SDK tools that will be linked to the swiftc frontend")
134+
set(SWIFT_ANDROID_ICU_UC "" CACHE STRING
135+
"Path to a directory containing libicuuc.so")
136+
set(SWIFT_ANDROID_ICU_UC_INCLUDE "" CACHE STRING
137+
"Path to a directory containing headers for libicuuc")
138+
set(SWIFT_ANDROID_ICU_I18N "" CACHE STRING
139+
"Path to a directory containing libicui18n.so")
140+
set(SWIFT_ANDROID_ICU_I18N_INCLUDE "" CACHE STRING
141+
"Path to a directory containing headers libicui18n")
142+
128143
#
129144
# User-configurable Darwin-specific options.
130145
#
@@ -408,9 +423,27 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
408423

409424
# FIXME: This will not work while trying to cross-compile.
410425
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64")
411-
configure_sdk_unix(LINUX "Linux" "linux" "linux" "x86_64" "x86_64-unknown-linux-gnu")
412426
set(SWIFT_HOST_VARIANT_ARCH "x86_64")
413427
set(SWIFT_PRIMARY_VARIANT_ARCH_default "x86_64")
428+
429+
set(swift_can_crosscompile_stdlib TRUE)
430+
431+
is_sdk_requested(LINUX swift_build_linux)
432+
if(swift_build_linux)
433+
configure_sdk_unix(LINUX "Linux" "linux" "linux" "x86_64" "x86_64-unknown-linux-gnu")
434+
set(SWIFT_PRIMARY_VARIANT_SDK_default "LINUX")
435+
set(SWIFT_PRIMARY_VARIANT_ARCH_default "x86_64")
436+
endif()
437+
438+
is_sdk_requested(ANDROID swift_build_android)
439+
if(swift_build_android AND ${swift_can_crosscompile_stdlib})
440+
configure_sdk_unix(ANDROID "Android" "android" "android" "armv7" "armv7-none-linux-androideabi")
441+
set(SWIFT_SDK_ANDROID_PATH "${SWIFT_ANDROID_SDK_PATH}")
442+
443+
set(SWIFT_PRIMARY_VARIANT_SDK_default "ANDROID")
444+
set(SWIFT_PRIMARY_VARIANT_ARCH_default "armv7")
445+
endif()
446+
414447
# FIXME: This only matches ARMv6l (by far the most common variant).
415448
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv6l")
416449
configure_sdk_unix(LINUX "Linux" "linux" "linux" "armv6" "armv6-unknown-linux-gnueabihf")

cmake/modules/AddSwift.cmake

+26-2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ function(_add_variant_c_compile_link_flags
5757
list(APPEND result
5858
"-isysroot" "${SWIFT_SDK_${sdk}_PATH}")
5959

60+
if("${sdk}" STREQUAL "ANDROID")
61+
list(APPEND result
62+
"--sysroot=${SWIFT_ANDROID_SDK_PATH}"
63+
# Use the linker included in the Android NDK.
64+
"-B" "${SWIFT_ANDROID_NDK_PATH}/toolchains/arm-linux-androideabi-${SWIFT_ANDROID_NDK_TOOLCHAIN_VERSION}/prebuilt/linux-x86_64/arm-linux-androideabi/bin/")
65+
endif()
66+
67+
6068
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
6169
list(APPEND result
6270
"-arch" "${arch}"
@@ -120,6 +128,13 @@ function(_add_variant_c_compile_flags
120128
"-fcoverage-mapping")
121129
endif()
122130

131+
if("${sdk}" STREQUAL "ANDROID")
132+
list(APPEND result
133+
"-I${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++/libcxx/include"
134+
"-I${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++abi/libcxxabi/include"
135+
"-I${SWIFT_ANDROID_NDK_PATH}/sources/android/support/include")
136+
endif()
137+
123138
set("${result_var_name}" "${result}" PARENT_SCOPE)
124139
endfunction()
125140

@@ -179,11 +194,20 @@ function(_add_variant_link_flags
179194
result)
180195

181196
if("${sdk}" STREQUAL "LINUX")
182-
list(APPEND result "-lpthread" "-ldl")
197+
list(APPEND result "-lpthread" "-ldl"
198+
# Include libbsd, which includes dependencies needed by Android stdlib
199+
# targets, such as `arc4random_uniform`.
200+
"${BSD_LIBRARIES}")
183201
elseif("${sdk}" STREQUAL "FREEBSD")
184202
list(APPEND result "-lpthread")
185203
elseif("${sdk}" STREQUAL "CYGWIN")
186204
# No extra libraries required.
205+
elseif("${sdk}" STREQUAL "ANDROID")
206+
list(APPEND result
207+
"-ldl"
208+
"-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}"
209+
"${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_shared.so"
210+
"-L${SWIFT_ANDROID_ICU_UC}" "-L${SWIFT_ANDROID_ICU_I18N}")
187211
else()
188212
list(APPEND result "-lobjc")
189213
endif()
@@ -967,7 +991,7 @@ function(_add_swift_library_single target name)
967991
set_target_properties("${target}"
968992
PROPERTIES
969993
INSTALL_NAME_DIR "${install_name_dir}")
970-
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
994+
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND NOT "${SWIFTLIB_SINGLE_SDK}" STREQUAL "ANDROID")
971995
set_target_properties("${target}"
972996
PROPERTIES
973997
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})

docs/Testing.rst

+42
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,48 @@ targets mentioned above and modify it as necessary. lit.py also has several
107107
useful features, like timing tests and providing a timeout. Check these features
108108
out with ``lit.py -h``.
109109

110+
Running tests hosted on an Android device
111+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
112+
113+
You may run tests targeting android-armv7 by connecting a device to run the
114+
tests on, then pushing the necessary dependencies to that device.
115+
116+
1. Connect your Android device to your computer via USB. Ensure that remote
117+
debugging is enabled for that device by following the official instructions:
118+
https://developer.chrome.com/devtools/docs/remote-debugging.
119+
2. Confirm the device is connected by running ``adb devices``. You should see
120+
your device listed.
121+
3. Push the built products for android-armv7 to your device, using the
122+
``utils/android/adb_push_built_products.py`` script. For example, to push
123+
the built products at a build directory
124+
``~/swift/Ninja-ReleaseAssert/swift-linux-x86_64``, with an Android NDK
125+
placed at ``~/android-ndk-r10e``, run the following:
126+
127+
$ utils/android/adb_push_built_products.py \
128+
~/swift/build/Ninja-ReleaseAssert/swift-linux-x86_64/lib/swift/android/ \
129+
--ndk ~/android-ndk-r10e
130+
4. Run the test suite for the ``android-armv7`` target.
131+
132+
You may run the tests using the build script as well. Specifying a
133+
``--android-deploy-device-path`` pushes the built Android products to your
134+
device as part of the build:
135+
136+
$ utils/build-script \
137+
-R \ # Build in ReleaseAssert mode.
138+
-T \ # Run all tests.
139+
--android \ # Build for Android.
140+
--android-deploy-device-path /data/local/tmp \ # Temporary directory on the device where Android tests are run.
141+
--android-ndk ~/android-ndk-r10e \ # Path to an Android NDK.
142+
--android-ndk-version 21 \
143+
--android-icu-uc ~/libicu-android/armeabi-v7a/libicuuc.so \
144+
--android-icu-uc-include ~/libicu-android/armeabi-v7a/icu/source/common \
145+
--android-icu-i18n ~/libicu-android/armeabi-v7a/libicui18n.so \
146+
--android-icu-i18n-include ~/libicu-android/armeabi-v7a/icu/source/i18n/
147+
148+
You must run the Linux tests once in order to build the Android test suite.
149+
After that, you may run the above command with the ``--skip-test-linux`` option
150+
to only run Android tests.
151+
110152
Writing tests
111153
-------------
112154

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

+4
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ StringRef swift::getPlatformNameForTriple(const llvm::Triple &triple) {
5858
return "watchos";
5959
}
6060

61+
if (triple.isAndroid()) {
62+
return "android";
63+
}
64+
6165
if (triple.isMacOSX())
6266
return "macosx";
6367

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/ToolChains.cpp

+27-6
Original file line numberDiff line numberDiff line change
@@ -1211,12 +1211,33 @@ toolchains::GenericUnix::constructInvocation(const LinkJobAction &job,
12111211
Arguments.push_back(context.Args.MakeArgString(LibProfile));
12121212
}
12131213

1214-
// FIXME: We probably shouldn't be adding an rpath here unless we know ahead
1215-
// of time the standard library won't be copied.
1216-
Arguments.push_back("-Xlinker");
1217-
Arguments.push_back("-rpath");
1218-
Arguments.push_back("-Xlinker");
1219-
Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath));
1214+
if (getTriple().isAndroid()) {
1215+
// FIXME: These should be set in CMake.
1216+
Arguments.push_back("-target");
1217+
Arguments.push_back("armv7-none-linux-androideabi");
1218+
1219+
const char* ndkhome = getenv("ANDROID_NDK_HOME");
1220+
assert(ndkhome && "ANDROID_NDK_HOME needs to be set to NDK "
1221+
"install directory for linking");
1222+
1223+
auto libgccpath = Twine(ndkhome) + "/toolchains/"
1224+
"arm-linux-androideabi-4.8/prebuilt/linux-x86_64/"
1225+
"lib/gcc/arm-linux-androideabi/4.8";
1226+
Arguments.push_back("-L");
1227+
Arguments.push_back(context.Args.MakeArgString(libgccpath));
1228+
1229+
auto libcxxpath = Twine(ndkhome) + "/sources/"
1230+
"cxx-stl/llvm-libc++/libs/armeabi-v7a";
1231+
Arguments.push_back("-L");
1232+
Arguments.push_back(context.Args.MakeArgString(libcxxpath));
1233+
} else {
1234+
// FIXME: We probably shouldn't be adding an rpath here unless we know ahead
1235+
// of time the standard library won't be copied.
1236+
Arguments.push_back("-Xlinker");
1237+
Arguments.push_back("-rpath");
1238+
Arguments.push_back("-Xlinker");
1239+
Arguments.push_back(context.Args.MakeArgString(RuntimeLibPath));
1240+
}
12201241

12211242
// Always add the stdlib
12221243
Arguments.push_back("-lswiftCore");

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

+18-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

@@ -1034,6 +1034,7 @@ public enum OSVersion : CustomStringConvertible {
10341034
case watchOSSimulator
10351035
case Linux
10361036
case FreeBSD
1037+
case Android
10371038

10381039
public var description: String {
10391040
switch self {
@@ -1055,6 +1056,8 @@ public enum OSVersion : CustomStringConvertible {
10551056
return "Linux"
10561057
case FreeBSD:
10571058
return "FreeBSD"
1059+
case Android:
1060+
return "Android"
10581061
}
10591062
}
10601063
}
@@ -1089,6 +1092,8 @@ func _getOSVersion() -> OSVersion {
10891092
return .Linux
10901093
#elseif os(FreeBSD)
10911094
return .FreeBSD
1095+
#elseif os(Android)
1096+
return .Android
10921097
#else
10931098
let productVersion = _stdlib_getSystemVersionPlistProperty("ProductVersion")!
10941099
let (major, minor, bugFix) = _parseDottedVersionTriple(productVersion)
@@ -1160,6 +1165,7 @@ public enum TestRunPredicate : CustomStringConvertible {
11601165
case watchOSSimulatorAny(/*reason:*/ String)
11611166

11621167
case LinuxAny(reason: String)
1168+
case AndroidAny(reason: String)
11631169

11641170
case FreeBSDAny(reason: String)
11651171

@@ -1243,6 +1249,9 @@ public enum TestRunPredicate : CustomStringConvertible {
12431249
case FreeBSDAny(reason: let reason):
12441250
return "FreeBSDAny(*, reason: \(reason))"
12451251

1252+
case AndroidAny(reason: let reason):
1253+
return "AndroidAny(*, reason: \(reason))"
1254+
12461255
case ObjCRuntime(let reason):
12471256
return "Objective-C runtime, reason: \(reason))"
12481257
case NativeRuntime(let reason):
@@ -1492,6 +1501,14 @@ public enum TestRunPredicate : CustomStringConvertible {
14921501
return false
14931502
}
14941503

1504+
case AndroidAny:
1505+
switch _getRunningOSVersion() {
1506+
case .Android:
1507+
return true
1508+
default:
1509+
return false
1510+
}
1511+
14951512
case ObjCRuntime:
14961513
#if _runtime(_ObjC)
14971514
return true

0 commit comments

Comments
 (0)