Skip to content

Port to Android #1442

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

Merged
merged 1 commit into from
Apr 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,20 @@ option(SWIFT_ENABLE_LTO
# The following only works with the Ninja generator in CMake >= 3.0.
set(SWIFT_PARALLEL_LINK_JOBS "" CACHE STRING
"Define the maximum number of linker jobs for swift.")
set(SWIFT_ANDROID_NDK_PATH "" CACHE STRING
"Path to the directory that contains the Android NDK tools that are executable on the build machine")
set(SWIFT_ANDROID_NDK_TOOLCHAIN_VERSION "" CACHE STRING
"A version of the toolchain to use when building for Android. Use 4.8 for 32-bit builds, 4.9 for 64-bit builds")
set(SWIFT_ANDROID_SDK_PATH "" CACHE STRING
"Path to the directory that contains the Android SDK tools that will be passed to the swiftc frontend")
set(SWIFT_ANDROID_ICU_UC "" CACHE STRING
"Path to a directory containing libicuuc.so")
set(SWIFT_ANDROID_ICU_UC_INCLUDE "" CACHE STRING
"Path to a directory containing headers for libicuuc")
set(SWIFT_ANDROID_ICU_I18N "" CACHE STRING
"Path to a directory containing libicui18n.so")
set(SWIFT_ANDROID_ICU_I18N_INCLUDE "" CACHE STRING
"Path to a directory containing headers libicui18n")

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

# FIXME: This will not work while trying to cross-compile.
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64")
configure_sdk_unix(LINUX "Linux" "linux" "linux" "x86_64" "x86_64-unknown-linux-gnu")
set(SWIFT_HOST_VARIANT_ARCH "x86_64")
set(SWIFT_PRIMARY_VARIANT_ARCH_default "x86_64")

if("${SWIFT_ANDROID_NDK_PATH}" STREQUAL "")
set(swift_can_crosscompile_stdlib FALSE)
else()
set(swift_can_crosscompile_stdlib TRUE)
endif()

is_sdk_requested(LINUX swift_build_linux)
if(swift_build_linux)
configure_sdk_unix(LINUX "Linux" "linux" "linux" "x86_64" "x86_64-unknown-linux-gnu")
set(SWIFT_PRIMARY_VARIANT_SDK_default "LINUX")
set(SWIFT_PRIMARY_VARIANT_ARCH_default "x86_64")
endif()

is_sdk_requested(ANDROID swift_build_android)
if(swift_build_android AND ${swift_can_crosscompile_stdlib})
configure_sdk_unix(ANDROID "Android" "android" "android" "armv7" "armv7-none-linux-androideabi")
set(SWIFT_SDK_ANDROID_PATH "${SWIFT_ANDROID_SDK_PATH}")

set(SWIFT_PRIMARY_VARIANT_SDK_default "ANDROID")
set(SWIFT_PRIMARY_VARIANT_ARCH_default "armv7")
endif()

# FIXME: This only matches ARMv6l (by far the most common variant).
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv6l")
configure_sdk_unix(LINUX "Linux" "linux" "linux" "armv6" "armv6-unknown-linux-gnueabihf")
Expand Down
25 changes: 23 additions & 2 deletions cmake/modules/AddSwift.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ function(_add_variant_c_compile_link_flags)
list(APPEND result
"-isysroot" "${SWIFT_SDK_${CFLAGS_SDK}_PATH}")

if("${CFLAGS_SDK}" STREQUAL "ANDROID")
list(APPEND result
"--sysroot=${SWIFT_ANDROID_SDK_PATH}"
# Use the linker included in the Android NDK.
"-B" "${SWIFT_ANDROID_NDK_PATH}/toolchains/arm-linux-androideabi-${SWIFT_ANDROID_NDK_TOOLCHAIN_VERSION}/prebuilt/linux-x86_64/arm-linux-androideabi/bin/")
endif()


if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")

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

if("${CFLAGS_SDK}" STREQUAL "ANDROID")
list(APPEND result
"-I${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++/libcxx/include"
"-I${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++abi/libcxxabi/include"
"-I${SWIFT_ANDROID_NDK_PATH}/sources/android/support/include")
endif()

set("${CFLAGS_RESULT_VAR_NAME}" "${result}" PARENT_SCOPE)
endfunction()

Expand Down Expand Up @@ -224,7 +239,13 @@ function(_add_variant_link_flags)
elseif("${LFLAGS_SDK}" STREQUAL "FREEBSD")
list(APPEND result "-lpthread")
elseif("${LFLAGS_SDK}" STREQUAL "CYGWIN")
# NO extra libraries required.
# No extra libraries required.
elseif("${LFLAGS_SDK}" STREQUAL "ANDROID")
list(APPEND result
"-ldl"
"-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}"
"${SWIFT_ANDROID_NDK_PATH}/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_shared.so"
"-L${SWIFT_ANDROID_ICU_UC}" "-L${SWIFT_ANDROID_ICU_I18N}")
else()
list(APPEND result "-lobjc")
endif()
Expand Down Expand Up @@ -997,7 +1018,7 @@ function(_add_swift_library_single target name)
set_target_properties("${target}"
PROPERTIES
INSTALL_NAME_DIR "${install_name_dir}")
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND NOT "${SWIFTLIB_SINGLE_SDK}" STREQUAL "ANDROID")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you handle library embedding on Android? Just curious.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be misunderstanding the question but if you're referring to swift's dynamic library dependencies on android, they currently need to be added to the android project by hand alongside libswiftCore.so.

Then you use java's dlib api, i.e. System.loadLibrary("swiftCore")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use rpath (enabled by this if statement) so that you don't have to load them manually?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Bionic linker-loader ignores RPATH afaicr

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. How do apps typically bundle libraries? Do they have to call System.loadLibrary()? Is that the recommended approach?

I'm just concerned about putting in a hack that would cause issues later. For example, how would an app with a Swift main() work? It won't be able to call loadLibrary() before running Swift code.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's up to app developer how it will be loaded. Between other solutions one of the popular approaches it to link everything statically into libmyapp.so and then load it once with System.loadLibrary("myapp").

Usually you can't invoke something like main() in native code in Android directly (there are exceptions which usually depend on rooting your phone). Then your app starts it always starts in Java code, which then can instantly load native code and call C/Swift function of interest. Developers who don't want to use any Java code for their app can use NativeActivity as a shortcut, which comes from Android SDK and does exactly this - loads your library of choice and calls function android_main.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Thank you for the explanation. Given this platform choice, the current approach looks reasonable.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, in my use case it suffices to just run System.loadLibrary("mySwiftLibrary") at an appropriately early point in the app's lifecycle - the runtime linker finds the rest of the shared objects provided they're also in the APK bundle. Given that Android is basically a Java-first/-only platform (except for command line tools on rooted devices), this is the "best practices" approach as far as I can tell.

set_target_properties("${target}"
PROPERTIES
INSTALL_RPATH "$ORIGIN:/usr/lib/swift/linux")
Expand Down
7 changes: 7 additions & 0 deletions cmake/modules/FindICU.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,12 @@ foreach(MODULE ${ICU_FIND_COMPONENTS})
endif()
endforeach()

if(NOT "${SWIFT_ANDROID_ICU_UC_INCLUDE}" STREQUAL "")
set(ICU_UC_INCLUDE_DIR "${SWIFT_ANDROID_ICU_UC_INCLUDE}")
endif()
if(NOT "${SWIFT_ANDROID_ICU_I18N_INCLUDE}" STREQUAL "")
set(ICU_I18N_INCLUDE_DIR "${SWIFT_ANDROID_ICU_I18N_INCLUDE}")
endif()

find_package_handle_standard_args(ICU DEFAULT_MSG ${ICU_REQUIRED})
mark_as_advanced(${ICU_REQUIRED})
2 changes: 1 addition & 1 deletion include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ namespace swift {
} else if (Target.isWatchOS()) {
Target.getOSVersion(major, minor, revision);
} else if (Target.isOSLinux() || Target.isOSFreeBSD() ||
Target.isOSWindows() ||
Target.isAndroid() || Target.isOSWindows() ||
Target.getTriple().empty())
{
major = minor = revision = 0;
Expand Down
5 changes: 4 additions & 1 deletion lib/Basic/LangOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ static const StringRef SupportedConditionalCompilationOSs[] = {
"iOS",
"Linux",
"FreeBSD",
"Windows"
"Windows",
"Android"
};

static const StringRef SupportedConditionalCompilationArches[] = {
Expand Down Expand Up @@ -105,6 +106,8 @@ std::pair<bool, bool> LangOptions::setTarget(llvm::Triple triple) {
addPlatformConditionValue("os", "watchOS");
else if (triple.isiOS())
addPlatformConditionValue("os", "iOS");
else if (triple.isAndroid())
addPlatformConditionValue("os", "Android");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change needs a test like test/Parse/ConditionalCompilation/x64FreeBSDTarget.swift for each supported triple.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! I added test/Parse/ConditionalCompilation/armAndroidTarget.swift.

else if (triple.isOSLinux())
addPlatformConditionValue("os", "Linux");
else if (triple.isOSFreeBSD())
Expand Down
3 changes: 3 additions & 0 deletions lib/Basic/Platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ StringRef swift::getPlatformNameForTriple(const llvm::Triple &triple) {
if (triple.isOSDarwin())
return getPlatformNameForDarwin(getDarwinPlatformKind(triple));

if (triple.isAndroid())
return "android";

if (triple.isOSLinux())
return "linux";

Expand Down
1 change: 1 addition & 0 deletions lib/ClangImporter/MappedTypes.def
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ MAP_STDLIB_TYPE("u_int64_t", UnsignedInt, 64, "UInt64", false, DoNothing)
// FIXME: why does this not catch va_list on x86_64?
MAP_STDLIB_TYPE("va_list", VaList, 0, "CVaListPointer", false, DoNothing)
MAP_STDLIB_TYPE("__gnuc_va_list", VaList, 0, "CVaListPointer", false, DoNothing)
MAP_STDLIB_TYPE("__va_list", VaList, 0, "CVaListPointer", false, DoNothing)

// libkern/OSTypes.h types.
MAP_STDLIB_TYPE("UInt", UnsignedInt, 32, "CUnsignedInt", false, DoNothing)
Expand Down
6 changes: 6 additions & 0 deletions lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2031,6 +2031,12 @@ const ToolChain *Driver::getToolChain(const ArgList &Args) const {
TC = new toolchains::Darwin(*this, Target);
break;
case llvm::Triple::Linux:
if (Target.isAndroid()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should specialize GenericUnix into a GNUToolchain or LinuxToolchain and sink the android check into the toolchain. Android is an environment of linux, and the toolchain really is identical across the two. Creating two toolchains feels wrong. This has previously been a problem for modeling windows in clang, and it was a fair amount of effort to clean up afterwards. I think we should draw on our experiences there to improve our choices here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gribozavr originally commented that an Android check may not be appropriate in a GenericUnix toolchain. I interpreted that to mean I should split Android out into its own toolchain, but I agree with you here, @compnerd. I'll rename GenericUnix to Linux, then include the isAndroid() check. Does that sound reasonable, @gribozavr?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@compnerd What was the problem in Clang with a hierarchy of toolchains? It seems to me that littering branches everywhere creates very fiddly code, whereas a hierarchy with proper extension points makes it clear which parts might be different.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jrose-apple As you suggested, I'm going to go ahead and create a class hierarchy here. There's a ton of duplication across GenericUnix and Windows, so I'll begin by submitting a separate pull request that establishes the following hierarchy:

GenenricUnix
    Windows

Then this pull request could add an additional subclass:

GenenricUnix
    Windows
    Android

Does that seem reasonable? (Admittedly, it seems strange to have Windows inherit from GenericUnix...)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toolchains::Windows came from the Cygwin port, so if you rename it toolchains::Cygwin, that's a little less strange to inherit from toolchains::GenericUnix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agree. One potential wart is the fact that other parts of the codebase will reference things like os(Windows), whereas the toolchain will be named Cygwin. Even with that in mind, I still prefer naming the toolchain Cygwin, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect os(Windows) to be true for both Cygwin and a hypothetical port to the MSVC runtime and toolchains. Naming the toolchain Cygwin seems fine to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also agree renaming the toolchain Cygwin. I thought rename it if I add toolchain MSVC or Mingw someday.
and...
How about renaming GenericUnix to GNUToolchain?
The heirarchy will be

GNUToolchain
    Android
    Cygwin
    Mingw

Cygwin uses the GNU toolchain and has Unix compatible libraries, but it is not UNIX OS.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're using it for FreeBSD too, though. I think it's fair to say Cygwin is a Unix-like toolchain even if it's not a Unix-like target.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Maybe it is a Unix-like target too, and then not a Unix-like OS. But that's not about the Driver.)

TC = new toolchains::Android(*this, Target);
} else {
TC = new toolchains::GenericUnix(*this, Target);
}
break;
case llvm::Triple::FreeBSD:
TC = new toolchains::GenericUnix(*this, Target);
break;
Expand Down
33 changes: 25 additions & 8 deletions lib/Driver/ToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1210,11 +1210,11 @@ std::string toolchains::GenericUnix::getDefaultLinker() const {
}
}

bool toolchains::GenericUnix::shouldProvideRPathToLinker() const {
return true;
std::string toolchains::GenericUnix::getTargetForLinker() const {
return getTriple().str();
}

bool toolchains::GenericUnix::shouldSpecifyTargetTripleToLinker() const {
bool toolchains::GenericUnix::shouldProvideRPathToLinker() const {
return true;
}

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

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

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

std::string
toolchains::Android::getTargetForLinker() const {
// Explicitly set the linker target to "androideabi", as opposed to the
// llvm::Triple representation of "armv7-none-linux-android".
// This is the only ABI we currently support for Android.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to double-check, there's no arm64/aarch64 Android Swift yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're correct, we only support armv7.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's actually assert that then. That way, we'll know very loudly when it changes.

assert(getTriple().getArch() == llvm::Triple::arm && "No other architectures supported for Android");

or something.

assert(
getTriple().getArch() == llvm::Triple::arm &&
getTriple().getSubArch() == llvm::Triple::SubArchType::ARMSubArch_v7 &&
"Only armv7 targets are supported for Android");
return "armv7-none-linux-androideabi";
}

bool toolchains::Android::shouldProvideRPathToLinker() const {
return false;
}

std::string toolchains::Cygwin::getDefaultLinker() const {
// Cygwin uses the default BFD linker, even on ARM.
return "";
}

bool toolchains::Cygwin::shouldSpecifyTargetTripleToLinker() const {
return false;
std::string toolchains::Cygwin::getTargetForLinker() const {
return "";
}

std::string toolchains::Cygwin::getPreInputObjectPath(
Expand Down
22 changes: 16 additions & 6 deletions lib/Driver/ToolChains.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ class LLVM_LIBRARY_VISIBILITY GenericUnix : public ToolChain {
/// and to not provide a specific linker otherwise.
virtual std::string getDefaultLinker() const;

/// The target to be passed to the compiler invocation. By default, this
/// is the target triple, but this may be overridden to accomodate some
/// platforms.
virtual std::string getTargetForLinker() const;

/// Whether to specify a linker -rpath to the Swift runtime library path.
/// -rpath is not supported on all platforms, and subclasses may override
/// this method to return false on platforms that don't support it. The
/// default is to return true (and so specify an -rpath).
virtual bool shouldProvideRPathToLinker() const;

/// Whether to explicitly specify the target triple as the linker
/// '--target'. This is not desirable on all platforms, and subclasses may
/// override this method to return false in those cases.
virtual bool shouldSpecifyTargetTripleToLinker() const;

/// Provides a path to an object that should be linked first. On platforms
/// that use ELF binaries, an object that provides markers and sizes for
/// metadata sections must be linked first. Platforms that do not need this
Expand Down Expand Up @@ -96,11 +96,21 @@ class LLVM_LIBRARY_VISIBILITY GenericUnix : public ToolChain {
~GenericUnix() = default;
};

class LLVM_LIBRARY_VISIBILITY Android : public GenericUnix {
protected:
std::string getTargetForLinker() const override;

bool shouldProvideRPathToLinker() const override;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cygwin should need to return "" from getTargetForLinker now, correct?

We should be able to write platform-independent tests for these, like those in test/Driver/linker.swift.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nice catch, thanks! (I should really get a Cygwin environment so that I can catch these myself...)

Looking into adding tests now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tests to test/Driver/linker.swift 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, we probably want to include the negative check on Cygwin. For that you'll need to run FileCheck again on the same input but with a different prefix, something like

// RUN: FileCheck -check-prefix CYGWIN-x86_64-NEGATIVE %s < %t.cygwin.txt

// CYGWIN-x86_64-NEGATIVE-NOT: -target

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat trick! I'll add an ANDROID-armv7-NEGATIVE-NOT: -Xlinker -rpath. However I think the Cygwin negative expectation won't work here, since we explicitly invoke %swiftc_driver -target x86_64-unknown-windows-cygnus in the first place. So -target does appear in the output...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you can mix CHECK and CHECK-NOT lines, in which case the -NOT line is checking that the output doesn't appear since the previous CHECK (or the beginning of the input) and before the next CHECK (or the end of the input).

public:
Android(const Driver &D, const llvm::Triple &Triple) : GenericUnix(D, Triple) {}
~Android() = default;
};

class LLVM_LIBRARY_VISIBILITY Cygwin : public GenericUnix {
protected:
std::string getDefaultLinker() const override;

bool shouldSpecifyTargetTripleToLinker() const override;
std::string getTargetForLinker() const override;

std::string getPreInputObjectPath(
StringRef RuntimeLibraryPath) const override;
Expand Down
2 changes: 1 addition & 1 deletion stdlib/private/StdlibUnittest/RaceTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import SwiftPrivate
import SwiftPrivatePthreadExtras
#if os(OSX) || os(iOS)
import Darwin
#elseif os(Linux) || os(FreeBSD)
#elseif os(Linux) || os(FreeBSD) || os(Android)
import Glibc
#endif

Expand Down
2 changes: 1 addition & 1 deletion stdlib/private/StdlibUnittest/StdlibCoreExtras.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import SwiftPrivate
import SwiftPrivateLibcExtras
#if os(OSX) || os(iOS)
import Darwin
#elseif os(Linux) || os(FreeBSD)
#elseif os(Linux) || os(FreeBSD) || os(Android)
import Glibc
#endif

Expand Down
20 changes: 19 additions & 1 deletion stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import SwiftPrivateLibcExtras

#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
import Darwin
#elseif os(Linux) || os(FreeBSD)
#elseif os(Linux) || os(FreeBSD) || os(Android)
import Glibc
#endif

Expand Down Expand Up @@ -1170,6 +1170,7 @@ public enum OSVersion : CustomStringConvertible {
case watchOSSimulator
case linux
case freeBSD
case android

public var description: String {
switch self {
Expand All @@ -1191,6 +1192,8 @@ public enum OSVersion : CustomStringConvertible {
return "Linux"
case freeBSD:
return "FreeBSD"
case android:
return "Android"
}
}
}
Expand Down Expand Up @@ -1225,6 +1228,8 @@ func _getOSVersion() -> OSVersion {
return .linux
#elseif os(FreeBSD)
return .freeBSD
#elseif os(Android)
return .android
#else
let productVersion = _stdlib_getSystemVersionPlistProperty("ProductVersion")!
let (major, minor, bugFix) = _parseDottedVersionTriple(productVersion)
Expand Down Expand Up @@ -1299,6 +1304,8 @@ public enum TestRunPredicate : CustomStringConvertible {

case freeBSDAny(reason: String)

case androidAny(reason: String)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change needs a test like validation-test/StdlibUnittest/FreeBSD.swift.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! I added validation-test/StdlibUnittest/Android.swift.


case objCRuntime(/*reason:*/ String)
case nativeRuntime(/*reason:*/ String)

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

case androidAny(reason: let reason):
return "androidAny(*, reason: \(reason))"

case freeBSDAny(reason: let reason):
return "freeBSDAny(*, reason: \(reason))"

Expand Down Expand Up @@ -1620,6 +1630,14 @@ public enum TestRunPredicate : CustomStringConvertible {
return false
}

case androidAny:
switch _getRunningOSVersion() {
case .android:
return true
default:
return false
}

case freeBSDAny:
switch _getRunningOSVersion() {
case .freeBSD:
Expand Down
Loading