-
Notifications
You must be signed in to change notification settings - Fork 10.6k
[Android] Add Android as availability platform #84574
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
base: main
Are you sure you want to change the base?
Changes from all commits
402100f
e00462e
d955d3e
ea73ef5
73bda81
94b5e4a
24710b5
5e87885
71a6851
91601fd
1297b46
973913f
12d2696
cf03af9
0516c31
1917895
ae7ef63
7d83142
b4e2b97
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -665,6 +665,12 @@ void importer::getNormalInvocationArguments( | |
}); | ||
} | ||
|
||
if (triple.isAndroid()) { | ||
invocationArgStrs.insert(invocationArgStrs.end(), { | ||
"-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It appears that the way this works is to turn off all compile-time preprocessor checking of Android API versions, turning them into link-time failures instead, if the API is more recent than the target API and the appropriate shared library link-time checking is enabled for strong references. I am leery of making this change to all Swift code compiled for Android: is there some way we can only limit this to files/modules that need it because they contain There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean that this turns off processor checking of Android API versions? This only changes the behaviour of the availability attribute attached to specific declarations. #if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__)
#define __BIONIC_AVAILABILITY(__what, ...) __attribute__((__availability__(android,__what __VA_OPT__(,) __VA_ARGS__)))
#define __INTRODUCED_IN_NO_GUARD_FOR_NDK(api_level) __INTRODUCED_IN(api_level)
#else
#define __BIONIC_AVAILABILITY(__what, ...) __attribute__((__availability__(android,strict,__what __VA_OPT__(,) __VA_ARGS__)))
#define __INTRODUCED_IN_NO_GUARD_FOR_NDK(api_level)
#endif There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe my perspective is skewed from only being familiar with Apple's SDKs, but I don't understand why we would not want this to be the default. From my quick reading it looks like it works exactly the same as on Apple platforms. The compiler will enforce that you must always check availability before calling the APIs which may not be available, so unless an API is mis-annotated there shouldn't be any harm and it seems like a strictly better developer experience to have access to newer APIs as long as you verify that they're present before you use them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In order to now be able to use older APIs than the target API, you have to disable compile-time API version checking to some extent. How extensive that would be is what worried me. Mads and I have been discussing this in Slack, which you saw now, and it looks like most Bionic APIs since NDK 28 can continue to be version checked at build time to some extent, by using the version annotations. If that's the case, we should explore it and see what it can check.
I have never used Let us see what he comes up with. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After giving it some more though, I agree that enabling weak symbols is the behaviour we want. The thing that they changed in r28, was actually removing It seems like the coverage of the Bionic in terms of I also added support to ClangImporter, such that it will actually import the NDK headers with correct This mode should not break anything for existing users that are compiling for Android. If their code was compiling, then all the NDK methods they were using must be supported on the version that they are compiling for, because of the Since this PR just adds support to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
About 80% were replaced when going from NDK 27 to 28, not all.
Did you try this yourself? If so, add some tests for it.
Nobody has suggested it would. Rather the question was whether introducing new Swift code would then keep the same level of compile-time version checking as before, after you set this macro, which is why this is not the default in the NDK.
Great, please add some tests for that and you will have not only kept this checking the same, but added an important new static version checking feature of multiple API levels. 😺 Thanks for working on and looking into all this, @madsodgaard, I think all Android devs would love to have this. I will try this out locally in the coming days and review it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This appears to work now, but I've asked for more test cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added tests that use Bionic APIs |
||
}); | ||
} | ||
|
||
if (triple.isOSWindows()) { | ||
switch (triple.getArch()) { | ||
default: llvm_unreachable("unsupported Windows architecture"); | ||
|
@@ -2568,6 +2574,10 @@ PlatformAvailability::PlatformAvailability(const LangOptions &langOpts) | |
deprecatedAsUnavailableMessage = ""; | ||
break; | ||
|
||
case PlatformKind::Android: | ||
deprecatedAsUnavailableMessage = ""; | ||
break; | ||
|
||
case PlatformKind::none: | ||
break; | ||
} | ||
|
@@ -2616,6 +2626,9 @@ bool PlatformAvailability::isPlatformRelevant(StringRef name) const { | |
case PlatformKind::Windows: | ||
return name == "windows"; | ||
|
||
case PlatformKind::Android: | ||
return name == "android"; | ||
|
||
case PlatformKind::none: | ||
return false; | ||
} | ||
|
@@ -2692,6 +2705,10 @@ bool PlatformAvailability::treatDeprecatedAsUnavailable( | |
case PlatformKind::Windows: | ||
// No deprecation filter on Windows | ||
return false; | ||
|
||
case PlatformKind::Android: | ||
// The minimum Android API level supported by Swift is 21 | ||
return major <= 20; | ||
} | ||
|
||
llvm_unreachable("Unexpected platform"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#include <android/versioning.h> | ||
|
||
void FunctionIntroducedIn24() __INTRODUCED_IN(24); | ||
void FunctionIntroducedIn28() __INTRODUCED_IN(28); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// RUN: %swift -target aarch64-unknown-linux-android28 -typecheck %s -parse-stdlib -dump-clang-diagnostics 2>&1 | %FileCheck %s -check-prefix CHECK-WEAK-SYMBOLS | ||
|
||
// CHECK-WEAK-SYMBOLS: -D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// RUN: %target-swift-frontend -typecheck -verify -I %S/Inputs/custom-modules -verify-ignore-unknown -target aarch64-unknown-linux-android24 %s | ||
|
||
// REQUIRES: OS=linux-android || OS=linux-androideabi | ||
|
||
import AndroidVersioning | ||
import Android | ||
|
||
FunctionIntroducedIn24() | ||
|
||
FunctionIntroducedIn28() | ||
// expected-error@-1 {{'FunctionIntroducedIn28()' is only available in Android 28 or newer}} | ||
// expected-note@-2 {{add 'if #available' version check}} | ||
|
||
func test_ifaddrs_introduced_in_24() { | ||
var ifaddr_ptr: UnsafeMutablePointer<ifaddrs>? = nil | ||
if getifaddrs(&ifaddr_ptr) == 0 { | ||
freeifaddrs(ifaddr_ptr) | ||
} | ||
} | ||
|
||
func test_getentropy_introduced_in_28() { | ||
var buffer: [UInt8] = .init(repeating: 0, count: 16) | ||
getentropy(&buffer, buffer.count) | ||
// expected-error@-1 {{'getentropy' is only available in Android 28 or newer}} | ||
// expected-note@-2 {{add 'if #available' version check}} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// RUN: %target-swift-frontend -target aarch64-unknown-linux-android24 -primary-file %s -emit-ir | %FileCheck %s | ||
|
||
// CHECK-LABEL: define{{.*}}$s20availability_android0A5CheckyyF | ||
// CHECK: call swiftcc i1 @"$ss26_stdlib_isOSVersionAtLeastyBi1_Bw_BwBwtF"( | ||
|
||
// REQUIRES: OS=linux-android || OS=linux-androideabi | ||
|
||
public func availabilityCheck() { | ||
if #available(Android 28, *) { | ||
print("test") | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// RUN: %swift -typecheck -verify -parse-stdlib -target aarch64-unknown-linux-android28 %s | ||
|
||
@available(Android, introduced: 1.0, deprecated: 2.0, obsoleted: 28.0, | ||
message: "you don't want to do that anyway") | ||
func doSomething() { } | ||
// expected-note @-1{{'doSomething()' was obsoleted in Android 28.0}} | ||
|
||
doSomething() // expected-error{{'doSomething()' is unavailable in Android: you don't want to do that anyway}} | ||
|
||
// Preservation of major.minor.micro | ||
@available(Android, introduced: 1.0, deprecated: 2.0, obsoleted: 27.0) | ||
func doSomethingElse() { } | ||
// expected-note @-1{{'doSomethingElse()' was obsoleted in Android 27.0}} | ||
|
||
doSomethingElse() // expected-error{{'doSomethingElse()' is unavailable in Android}} | ||
|
||
// Test deprecations in 28.0 and later | ||
|
||
@available(Android, introduced: 1.1, deprecated: 28.0, | ||
message: "Use another function") | ||
func deprecatedFunctionWithMessage() { } | ||
|
||
deprecatedFunctionWithMessage() // expected-warning{{'deprecatedFunctionWithMessage()' was deprecated in Android 28.0: Use another function}} | ||
|
||
|
||
@available(Android, introduced: 1.0, deprecated: 28.0) | ||
func deprecatedFunctionWithoutMessage() { } | ||
|
||
deprecatedFunctionWithoutMessage() // expected-warning{{'deprecatedFunctionWithoutMessage()' was deprecated in Android 28.0}} | ||
|
||
@available(Android, introduced: 1.0, deprecated: 28.0, | ||
message: "Use BetterClass instead") | ||
class DeprecatedClass { } | ||
|
||
func functionWithDeprecatedParameter(p: DeprecatedClass) { } // expected-warning{{'DeprecatedClass' was deprecated in Android 28.0: Use BetterClass instead}} | ||
|
||
@available(tvOS, introduced: 7.0, deprecated: 29, | ||
message: "Use BetterClass instead") | ||
class DeprecatedClassIn29_0 { } | ||
|
||
// Elements deprecated later than the minimum deployment target (which is 28.0, in this case) should not generate warnings | ||
func functionWithDeprecatedLaterParameter(p: DeprecatedClassIn29_0) { } | ||
|
||
@available(Android, introduced: 30) | ||
func functionIntroducedOnAndroid30() { } | ||
|
||
if #available(iOS 17.3, *) { | ||
functionIntroducedOnAndroid30() // expected-error{{'functionIntroducedOnAndroid30()' is only available in Android 30 or newer}} | ||
// expected-note @-1{{add 'if #available' version check}}{{3-34=if #available(Android 30, *) {\n functionIntroducedOnAndroid30()\n \} else {\n // Fallback on earlier versions\n \}}} | ||
} | ||
|
||
if #available(Android 28, *) { | ||
functionIntroducedOnAndroid30() // expected-error{{'functionIntroducedOnAndroid30()' is only available in Android 30 or newer}} | ||
// expected-note @-1{{add 'if #available' version check}}{{3-34=if #available(Android 30, *) {\n functionIntroducedOnAndroid30()\n \} else {\n // Fallback on earlier versions\n \}}} | ||
} | ||
|
||
if #available(Android 30, *) { | ||
functionIntroducedOnAndroid30() | ||
} |
Uh oh!
There was an error while loading. Please reload this page.