Skip to content

Foundation: repair the build for Android API level 28+ #4889

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

Closed
wants to merge 9 commits into from
Closed
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ if(HAS_LIBDISPATCH_API)
find_package(Threads REQUIRED)
endif()

# CMake's Threads adds '-pthread' flag to the interface link
# libraries, which isn't supported by Swift. This is not enabled
# when building with MSVC, but it trips up the Android build, so
# we need to clear out the threads INTERFACE_LINK_LIBRARIES.
if (CMAKE_SYSTEM_NAME 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.

cc @iCharlesHu -- we will need to make sure this is kept in our transition to the newer cmake files

Copy link
Contributor

Choose a reason for hiding this comment

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

This has been updated now.

set_property(TARGET Threads::Threads PROPERTY INTERFACE_LINK_LIBRARIES "")
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 this probably addresses the same issue I fixed in build-script years ago. Maybe it would be better to add that CMake flag to build.ps1?

Copy link
Contributor

Choose a reason for hiding this comment

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

I resolved it differently now, thanks to @compnerd 's suggestion.

endif()

set(SAVED_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS NO)
add_subdirectory(CoreFoundation EXCLUDE_FROM_ALL)
Expand Down
1 change: 1 addition & 0 deletions CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#include <sys/stat.h>
#include <sys/syscall.h>
#include <termios.h>
#include <linux/stat.h>
#elif TARGET_OS_WASI
#include <fcntl.h>
#include <sys/stat.h>
Expand Down
6 changes: 6 additions & 0 deletions CoreFoundation/Base.subproj/module.modulemap
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
framework module CoreFoundation [extern_c] [system] {
umbrella header "CoreFoundation.h"
explicit module CFPlugInCOM { header "CFPlugInCOM.h" }
explicit module ForSwiftFoundationOnly {
header "ForSwiftFoundationOnly.h"
// Do not re-export imported Clang modules to avoid pulling in
// system headers like linux/stat.h whose constants might conflict
// with constants from the platform module.
}

export *
module * {
Expand Down
9 changes: 9 additions & 0 deletions Sources/Foundation/Data.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@
@usableFromInline let memset = WASILibc.memset
@usableFromInline let memcpy = WASILibc.memcpy
@usableFromInline let memcmp = WASILibc.memcmp
#elseif canImport(Android)
@usableFromInline let calloc = Android.calloc
@usableFromInline let malloc = Android.malloc
@usableFromInline let free = Android.free
@usableFromInline let memset = Android.memset
@usableFromInline let memcpy = Android.memcpy
@usableFromInline let memcmp = Android.memcmp
#endif

#if !canImport(Darwin)
Expand All @@ -57,6 +64,8 @@ internal func malloc_good_size(_ size: Int) -> Int {
import Glibc
#elseif canImport(Musl)
import Musl
#elseif canImport(Android)
import Android
#elseif canImport(WASILibc)
import WASILibc
#endif
Expand Down
7 changes: 6 additions & 1 deletion Sources/Foundation/FileHandle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ import WASILibc
fileprivate let _read = WASILibc.read(_:_:_:)
fileprivate let _write = WASILibc.write(_:_:_:)
fileprivate let _close = WASILibc.close(_:)
#elseif canImport(Android)
import Android
fileprivate let _read = Android.read(_:_:_:)
fileprivate let _write = Android.write(_:_:_:)
fileprivate let _close = Android.close(_:)
#endif

#if canImport(WinSDK)
Expand Down Expand Up @@ -324,7 +329,7 @@ open class FileHandle : NSObject {
let data = mmap(nil, mapSize, PROT_READ, MAP_PRIVATE, _fd, 0)
// Swift does not currently expose MAP_FAILURE
if data != UnsafeMutableRawPointer(bitPattern: -1) {
return NSData.NSDataReadResult(bytes: data!, length: mapSize) { buffer, length in
return NSData.NSDataReadResult(bytes: data, length: mapSize) { buffer, length in
munmap(buffer, length)
}
}
Expand Down
34 changes: 24 additions & 10 deletions Sources/Foundation/FileManager+POSIX.swift
Original file line number Diff line number Diff line change
Expand Up @@ -803,29 +803,36 @@ extension FileManager {
let ps = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: 2)
ps.initialize(to: UnsafeMutablePointer(mutating: fsRep))
ps.advanced(by: 1).initialize(to: nil)
let stream = fts_open(ps, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR | FTS_NOSTAT, nil)
let stream = ps.withMemoryRebound(to: UnsafeMutablePointer<CChar>.self, capacity: 2) { rebound_ps in
#if canImport(Android)
let arg = rebound_ps
Copy link
Member

Choose a reason for hiding this comment

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

This change and the similar one 370 lines below are likely no longer needed after swiftlang/swift#74829. Try it out and see, I will check natively on Android also.

#else
let arg = ps
#endif
return fts_open(arg, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR | FTS_NOSTAT, nil)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

ps.deinitialize(count: 2)
ps.deallocate()

if stream != nil {
if let stream {
defer {
fts_close(stream)
}

while let current = fts_read(stream)?.pointee {
let itemPath = string(withFileSystemRepresentation: current.fts_path, length: Int(current.fts_pathlen))
while let current = fts_read(stream)?.pointee, let fts_path = current.fts_path {
let itemPath = string(withFileSystemRepresentation: fts_path, length: Int(current.fts_pathlen))
guard alreadyConfirmed || shouldRemoveItemAtPath(itemPath, isURL: isURL) else {
continue
}

do {
switch Int32(current.fts_info) {
case FTS_DEFAULT, FTS_F, FTS_NSOK, FTS_SL, FTS_SLNONE:
if unlink(current.fts_path) == -1 {
if unlink(fts_path) == -1 {
throw _NSErrorWithErrno(errno, reading: false, path: itemPath)
}
case FTS_DP:
if rmdir(current.fts_path) == -1 {
if rmdir(fts_path) == -1 {
throw _NSErrorWithErrno(errno, reading: false, path: itemPath)
}
case FTS_DNR, FTS_ERR, FTS_NS:
Expand Down Expand Up @@ -1171,7 +1178,14 @@ extension FileManager {
defer { ps.deallocate() }
ps.initialize(to: UnsafeMutablePointer(mutating: fsRep))
ps.advanced(by: 1).initialize(to: nil)
return fts_open(ps, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR | FTS_NOSTAT, nil)
return ps.withMemoryRebound(to: UnsafeMutablePointer<CChar>.self, capacity: 2) { rebound_ps in
#if canImport(Android)
let arg = rebound_ps
#else
let arg = ps
#endif
return fts_open(arg, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR | FTS_NOSTAT, nil)
}
}
if _stream == nil {
throw _NSErrorWithErrno(errno, reading: true, url: url)
Expand Down Expand Up @@ -1218,13 +1232,13 @@ extension FileManager {

_current = fts_read(stream)
while let current = _current {
let filename = FileManager.default.string(withFileSystemRepresentation: current.pointee.fts_path, length: Int(current.pointee.fts_pathlen))
let filename = FileManager.default.string(withFileSystemRepresentation: current.pointee.fts_path!, length: Int(current.pointee.fts_pathlen))

switch Int32(current.pointee.fts_info) {
case FTS_D:
let (showFile, skipDescendants) = match(filename: filename, to: _options, isDir: true)
if skipDescendants {
fts_set(_stream, _current, FTS_SKIP)
fts_set(stream, _current!, FTS_SKIP)
}
if showFile {
return URL(fileURLWithPath: filename, isDirectory: true)
Expand Down Expand Up @@ -1398,7 +1412,7 @@ extension FileManager {
let finalErrno = originalItemURL.withUnsafeFileSystemRepresentation { (originalFS) -> Int32? in
return newItemURL.withUnsafeFileSystemRepresentation { (newItemFS) -> Int32? in
// This is an atomic operation in many OSes, but is not guaranteed to be atomic by the standard.
if rename(newItemFS, originalFS) == 0 {
if rename(newItemFS!, originalFS!) == 0 {
return nil
} else {
return errno
Expand Down
12 changes: 8 additions & 4 deletions Sources/Foundation/FileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import WinSDK
import WASILibc
#endif

#if canImport(Android)
import Android
#endif

#if os(Windows)
internal typealias NativeFSRCharType = WCHAR
internal let NativeFSREncoding = String.Encoding.utf16LittleEndian.rawValue
Expand Down Expand Up @@ -579,13 +583,13 @@ open class FileManager : NSObject {
#elseif os(WASI)
let type = FileAttributeType(statMode: mode_t(s.st_mode))
#else
if let pwd = getpwuid(s.st_uid), pwd.pointee.pw_name != nil {
let name = String(cString: pwd.pointee.pw_name)
if let pwd = getpwuid(s.st_uid), let pw_name = pwd.pointee.pw_name {
let name = String(cString: pw_name)
result[.ownerAccountName] = name
}

if let grd = getgrgid(s.st_gid), grd.pointee.gr_name != nil {
let name = String(cString: grd.pointee.gr_name)
if let grd = getgrgid(s.st_gid), let gr_name = grd.pointee.gr_name {
let name = String(cString: gr_name)
result[.groupOwnerAccountName] = name
}

Expand Down
9 changes: 5 additions & 4 deletions Sources/Foundation/Host.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
import WinSDK
#endif

#if os(Android)
// Android Glibc differs a little with respect to the Linux Glibc.
#if canImport(Android)
import Android
// Android Bionic differs a little with respect to the Linux Glibc.

// IFF_LOOPBACK is part of the enumeration net_device_flags, which needs to
// convert to UInt32.
Expand All @@ -24,8 +25,8 @@ import WinSDK
}

// getnameinfo uses size_t for its 4th and 6th arguments.
private func getnameinfo(_ addr: UnsafePointer<sockaddr>?, _ addrlen: socklen_t, _ host: UnsafeMutablePointer<Int8>?, _ hostlen: socklen_t, _ serv: UnsafeMutablePointer<Int8>?, _ servlen: socklen_t, _ flags: Int32) -> Int32 {
return Glibc.getnameinfo(addr, addrlen, host, Int(hostlen), serv, Int(servlen), flags)
private func getnameinfo(_ addr: UnsafePointer<sockaddr>, _ addrlen: socklen_t, _ host: UnsafeMutablePointer<Int8>?, _ hostlen: socklen_t, _ serv: UnsafeMutablePointer<Int8>?, _ servlen: socklen_t, _ flags: Int32) -> Int32 {
return Android.getnameinfo(addr, addrlen, host, Int(hostlen), serv, Int(servlen), flags)
}

// getifaddrs and freeifaddrs are not available in Android 6.0 or earlier, so call these functions dynamically.
Expand Down
2 changes: 2 additions & 0 deletions Sources/Foundation/NSData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,8 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
let createMode = Int(Musl.S_IRUSR) | Int(Musl.S_IWUSR) | Int(Musl.S_IRGRP) | Int(Musl.S_IWGRP) | Int(Musl.S_IROTH) | Int(Musl.S_IWOTH)
#elseif canImport(WASILibc)
let createMode = Int(WASILibc.S_IRUSR) | Int(WASILibc.S_IWUSR) | Int(WASILibc.S_IRGRP) | Int(WASILibc.S_IWGRP) | Int(WASILibc.S_IROTH) | Int(WASILibc.S_IWOTH)
#elseif canImport(Android)
let createMode = Int(Android.S_IRUSR) | Int(Android.S_IWUSR) | Int(Android.S_IRGRP) | Int(Android.S_IWGRP) | Int(Android.S_IROTH) | Int(Android.S_IWOTH)
#endif
guard let fh = FileHandle(path: path, flags: flags, createMode: createMode) else {
throw _NSErrorWithErrno(errno, reading: false, path: path)
Expand Down
2 changes: 2 additions & 0 deletions Sources/Foundation/NSError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import Darwin
import Glibc
#elseif canImport(CRT)
import CRT
#elseif canImport(Android)
import Android
#endif

@_implementationOnly import CoreFoundation
Expand Down
2 changes: 2 additions & 0 deletions Sources/Foundation/NSLock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#if canImport(Glibc)
import Glibc
#elseif canImport(Android)
import Android
#endif

#if os(Windows)
Expand Down
2 changes: 2 additions & 0 deletions Sources/Foundation/NSSwiftRuntime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
@_exported import Glibc
#elseif canImport(Musl)
@_exported import Musl
#elseif canImport(Android)
@_exported import Android
#elseif os(WASI)
@_exported import WASILibc
#elseif os(Windows)
Expand Down
2 changes: 2 additions & 0 deletions Sources/Foundation/NSURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import Darwin
import Glibc
#elseif canImport(Musl)
import Musl
#elseif canImport(Android)
import Android
#endif

// NOTE: this represents PLATFORM_PATH_STYLE
Expand Down
12 changes: 9 additions & 3 deletions Sources/Foundation/Port.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,28 @@ open class SocketPort: Port {}

#else

#if canImport(Glibc) && !os(Android) && !os(OpenBSD)
#if canImport(Glibc) && !os(OpenBSD)
import Glibc
fileprivate let SOCK_STREAM = Int32(Glibc.SOCK_STREAM.rawValue)
fileprivate let SOCK_DGRAM = Int32(Glibc.SOCK_DGRAM.rawValue)
fileprivate let IPPROTO_TCP = Int32(Glibc.IPPROTO_TCP)
#endif

#if canImport(Glibc) && os(Android) || os(OpenBSD)
#if canImport(Glibc) && os(OpenBSD)
import Glibc
fileprivate let SOCK_STREAM = Int32(Glibc.SOCK_STREAM)
fileprivate let SOCK_DGRAM = Int32(Glibc.SOCK_DGRAM)
fileprivate let IPPROTO_TCP = Int32(Glibc.IPPROTO_TCP)
fileprivate let INADDR_ANY: in_addr_t = 0
#if os(OpenBSD)
fileprivate let INADDR_LOOPBACK = 0x7f000001
#endif

#if canImport(Android)
import Android
fileprivate let SOCK_STREAM = Int32(Android.SOCK_STREAM)
fileprivate let SOCK_DGRAM = Int32(Android.SOCK_DGRAM)
fileprivate let IPPROTO_TCP = Int32(Android.IPPROTO_TCP)
fileprivate let INADDR_ANY: in_addr_t = 0
#endif


Expand Down
7 changes: 7 additions & 0 deletions Sources/Foundation/Process.swift
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,13 @@ open class Process: NSObject {
var spawnAttrs: posix_spawnattr_t? = nil
#else
var spawnAttrs: posix_spawnattr_t = posix_spawnattr_t()
#endif
#if os(Android)
guard var spawnAttrs else {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno), userInfo: [
NSURLErrorKey:self.executableURL!
])
}
#endif
try _throwIfPosixError(posix_spawnattr_init(&spawnAttrs))
try _throwIfPosixError(posix_spawnattr_setflags(&spawnAttrs, .init(POSIX_SPAWN_SETPGROUP)))
Expand Down
2 changes: 2 additions & 0 deletions Sources/Foundation/Thread.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import WinSDK
import Glibc
#elseif canImport(Musl)
import Musl
#elseif canImport(Android)
import Android
#endif

// WORKAROUND_SR9811
Expand Down
3 changes: 3 additions & 0 deletions Sources/Tools/plutil/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import Glibc
#elseif canImport(Musl)
import Foundation
import Musl
#elseif canImport(Android)
import Foundation
import Android
#elseif canImport(CRT)
import Foundation
import CRT
Expand Down
2 changes: 2 additions & 0 deletions Tests/Foundation/FTPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import Dispatch
import Glibc
#elseif canImport(Darwin)
import Darwin
#elseif canImport(Android)
import Android
#endif

public class ServerSemaphore {
Expand Down
2 changes: 2 additions & 0 deletions Tests/Foundation/HTTPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import Dispatch
import Darwin
#elseif canImport(Glibc)
import Glibc
#elseif canImport(Android)
import Android
#endif

#if !os(Windows)
Expand Down
9 changes: 8 additions & 1 deletion Tests/Foundation/Tests/TestFileHandle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,14 @@ class TestFileHandle : XCTestCase {
#else
var fds: [Int32] = [-1, -1]
fds.withUnsafeMutableBufferPointer { (pointer) -> Void in
pipe(pointer.baseAddress)
let baseAddress = pointer.baseAddress
#if canImport(Android)
// pipe takes in a non-nullable pointer in the Android NDK only.
guard let baseAddress else {
return
}
#endif
pipe(baseAddress)
}

close(fds[1])
Expand Down
4 changes: 4 additions & 0 deletions Tests/Foundation/Tests/TestNSData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,8 @@ class TestNSData: LoopbackServerTest {
let permission = try fileManager._permissionsOfItem(atPath: url.path)
#if canImport(Darwin)
let expected = Int(S_IRUSR) | Int(S_IWUSR) | Int(S_IRGRP) | Int(S_IWGRP) | Int(S_IROTH) | Int(S_IWOTH)
#elseif canImport(Android)
let expected = Int(Android.S_IRUSR) | Int(Android.S_IWUSR) | Int(Android.S_IRGRP) | Int(Android.S_IWGRP) | Int(Android.S_IROTH) | Int(Android.S_IWOTH)
#else
let expected = Int(Glibc.S_IRUSR) | Int(Glibc.S_IWUSR) | Int(Glibc.S_IRGRP) | Int(Glibc.S_IWGRP) | Int(Glibc.S_IROTH) | Int(Glibc.S_IWOTH)
#endif
Expand All @@ -612,6 +614,8 @@ class TestNSData: LoopbackServerTest {
let permission = try fileManager._permissionsOfItem(atPath: url.path)
#if canImport(Darwin)
let expected = Int(S_IRUSR) | Int(S_IWUSR) | Int(S_IRGRP) | Int(S_IWGRP) | Int(S_IROTH) | Int(S_IWOTH)
#elseif canImport(Android)
let expected = Int(Android.S_IRUSR) | Int(Android.S_IWUSR) | Int(Android.S_IRGRP) | Int(Android.S_IWGRP) | Int(Android.S_IROTH) | Int(Android.S_IWOTH)
#else
let expected = Int(Glibc.S_IRUSR) | Int(Glibc.S_IWUSR) | Int(Glibc.S_IRGRP) | Int(Glibc.S_IWGRP) | Int(Glibc.S_IROTH) | Int(Glibc.S_IWOTH)
#endif
Expand Down
7 changes: 6 additions & 1 deletion Tests/Foundation/Tests/TestTimeZone.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,12 @@ class TestTimeZone: XCTestCase {
var lt = tm()
localtime_r(&t, &lt)
let zoneName = NSTimeZone.system.abbreviation() ?? "Invalid Abbreviation"
let expectedName = String(cString: lt.tm_zone, encoding: .ascii) ?? "Invalid Zone"
// tm_zone is nullable in the Android NDK only.
let tm_zone: UnsafePointer<CChar>? = lt.tm_zone
guard let tm_zone else {
return
}
let expectedName = String(cString: tm_zone, encoding: .ascii) ?? "Invalid Zone"
XCTAssertEqual(zoneName, expectedName, "expected name \"\(expectedName)\" is not equal to \"\(zoneName)\"")
}
#endif
Expand Down
Loading