diff --git a/Sources/FoundationEssentials/Data/Data+Reading.swift b/Sources/FoundationEssentials/Data/Data+Reading.swift index 48b95214d..612681d2d 100644 --- a/Sources/FoundationEssentials/Data/Data+Reading.swift +++ b/Sources/FoundationEssentials/Data/Data+Reading.swift @@ -34,7 +34,7 @@ import WASILibc func _fgetxattr(_ fd: Int32, _ name: UnsafePointer!, _ value: UnsafeMutableRawPointer!, _ size: Int, _ position: UInt32, _ options: Int32) -> Int { #if canImport(Darwin) return fgetxattr(fd, name, value, size, position, options) -#elseif canImport(Glibc) || canImport(Musl) +#elseif canImport(Glibc) || canImport(Musl) || canImport(Android) return fgetxattr(fd, name, value, size) #else return -1 @@ -355,7 +355,7 @@ internal func readBytesFromFile(path inPath: PathOrURL, reportProgress: Bool, ma let localProgress = (reportProgress && Progress.current() != nil) ? Progress(totalUnitCount: Int64(fileSize)) : nil if fileSize == 0 { - #if os(Linux) + #if os(Linux) || os(Android) // Linux has some files that may report a size of 0 but actually have contents let chunkSize = 1024 * 4 var buffer = malloc(chunkSize)! diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift index 9cd97525a..b5050a18c 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift @@ -955,6 +955,8 @@ extension _FileManagerImpl { #if os(WASI) // WASI does not support extended attributes throw CocoaError.errorWithFilePath(.featureUnsupported, path) + #elseif canImport(Android) + // Android doesn't allow setting this for normal apps, so just skip it. #else try Self._setAttributes(extendedAttrs, at: fileSystemRepresentation, followSymLinks: false) #endif diff --git a/Sources/FoundationEssentials/Platform.swift b/Sources/FoundationEssentials/Platform.swift index 4549a4524..0a0aaa393 100644 --- a/Sources/FoundationEssentials/Platform.swift +++ b/Sources/FoundationEssentials/Platform.swift @@ -192,7 +192,7 @@ extension Platform { extension Platform { @discardableResult package static func copyCString(dst: UnsafeMutablePointer, src: UnsafePointer, size: Int) -> Int { - #if canImport(Darwin) + #if canImport(Darwin) || canImport(Android) return strlcpy(dst, src, size) #else // Glibc doesn't support strlcpy @@ -267,7 +267,7 @@ extension Platform { return String(cString: buffer.baseAddress!).standardizingPath #endif } -#elseif os(Linux) +#elseif os(Linux) || os(Android) // For Linux, read /proc/self/exe return try? FileManager.default.destinationOfSymbolicLink( atPath: "/proc/self/exe").standardizingPath diff --git a/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift b/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift index eb55c273d..bb487c7f5 100644 --- a/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift +++ b/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift @@ -437,7 +437,7 @@ extension _ProcessInfo { var siInfo = SYSTEM_INFO() GetSystemInfo(&siInfo) return Int(siInfo.dwNumberOfProcessors) -#elseif os(Linux) || os(FreeBSD) +#elseif os(Linux) || os(FreeBSD) || canImport(Android) return Int(sysconf(Int32(_SC_NPROCESSORS_CONF))) #else return 1 @@ -454,7 +454,7 @@ extension _ProcessInfo { return 0 } return Int(count) -#elseif os(Linux) || os(FreeBSD) +#elseif os(Linux) || os(FreeBSD) || canImport(Android) #if os(Linux) if let fsCount = Self.fsCoreCount() { return fsCount @@ -548,7 +548,7 @@ extension _ProcessInfo { return 0 } return totalMemoryKB * 1024 -#elseif os(Linux) || os(FreeBSD) +#elseif os(Linux) || os(FreeBSD) || canImport(Android) var memory = sysconf(Int32(_SC_PHYS_PAGES)) memory *= sysconf(Int32(_SC_PAGESIZE)) return UInt64(memory) diff --git a/Sources/FoundationEssentials/TimeZone/TimeZone_Cache.swift b/Sources/FoundationEssentials/TimeZone/TimeZone_Cache.swift index 744d77b3b..efb8d678f 100644 --- a/Sources/FoundationEssentials/TimeZone/TimeZone_Cache.swift +++ b/Sources/FoundationEssentials/TimeZone/TimeZone_Cache.swift @@ -173,7 +173,7 @@ struct TimeZoneCache : Sendable { } } -#if os(Linux) && !os(WASI) +#if os(Linux) // Try localtime tzset() var t = time(nil) diff --git a/Tests/FoundationEssentialsTests/DataIOTests.swift b/Tests/FoundationEssentialsTests/DataIOTests.swift index 5087168ac..6b9ecf3f4 100644 --- a/Tests/FoundationEssentialsTests/DataIOTests.swift +++ b/Tests/FoundationEssentialsTests/DataIOTests.swift @@ -238,7 +238,7 @@ class DataIOTests : XCTestCase { } func test_zeroSizeFile() throws { - #if !os(Linux) + #if !os(Linux) && !os(Android) throw XCTSkip("This test is only applicable on Linux") #else // Some files in /proc report a file size of 0 bytes via a stat call diff --git a/Tests/FoundationEssentialsTests/DataTests.swift b/Tests/FoundationEssentialsTests/DataTests.swift index 7eae4692f..88428bfef 100644 --- a/Tests/FoundationEssentialsTests/DataTests.swift +++ b/Tests/FoundationEssentialsTests/DataTests.swift @@ -1837,7 +1837,7 @@ extension DataTests { } func testEOPNOTSUPP() throws { - #if !canImport(Darwin) && !os(Linux) + #if !canImport(Darwin) && !os(Linux) && !os(Android) throw XCTSkip("POSIXError.Code is not supported on this platform") #else // Opening a socket via open(2) on Darwin can result in the EOPNOTSUPP error code diff --git a/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift b/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift index a25638ab4..62dc23260 100644 --- a/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift +++ b/Tests/FoundationEssentialsTests/FileManager/FileManagerTests.swift @@ -23,6 +23,10 @@ import TestSupport @testable import Foundation #endif +#if canImport(Android) +import Android +#endif + extension FileManager { fileprivate var delegateCaptures: DelegateCaptures { (self.delegate as! CapturingFileManagerDelegate).captures @@ -329,8 +333,13 @@ final class FileManagerTests : XCTestCase { XCTAssertTrue($0.delegateCaptures.isEmpty) try $0.linkItem(atPath: "foo", toPath: "bar") XCTAssertEqual($0.delegateCaptures.shouldLink, [.init("foo", "bar")]) + #if os(Android) // Hard links are not normally allowed on Android. + XCTAssertEqual($0.delegateCaptures.shouldProceedAfterLinkError, [.init("foo", "bar", code: .fileWriteNoPermission)]) + XCTAssertFalse($0.fileExists(atPath: "bar")) + #else XCTAssertEqual($0.delegateCaptures.shouldProceedAfterLinkError, []) XCTAssertTrue($0.fileExists(atPath: "bar")) + #endif } try FileManagerPlayground { diff --git a/Tests/FoundationEssentialsTests/PredicateTests.swift b/Tests/FoundationEssentialsTests/PredicateTests.swift index 340b21a4a..d0972b537 100644 --- a/Tests/FoundationEssentialsTests/PredicateTests.swift +++ b/Tests/FoundationEssentialsTests/PredicateTests.swift @@ -364,7 +364,7 @@ final class PredicateTests: XCTestCase { func testRegex_RegexBuilder() throws { #if !canImport(RegexBuilder) throw XCTSkip("RegexBuilder is unavavailable on this platform") - #elseif !os(Linux) && !FOUNDATION_FRAMEWORK + #elseif !os(Linux) && !os(Android) && !FOUNDATION_FRAMEWORK // Disable this test in swift-foundation macOS CI because of incorrect availability annotations in the StringProcessing module throw XCTSkip("This test is currently disabled on this platform") #else diff --git a/Tests/FoundationEssentialsTests/ProcessInfoTests.swift b/Tests/FoundationEssentialsTests/ProcessInfoTests.swift index 1e73a9fbf..5b2cc36cf 100644 --- a/Tests/FoundationEssentialsTests/ProcessInfoTests.swift +++ b/Tests/FoundationEssentialsTests/ProcessInfoTests.swift @@ -115,7 +115,7 @@ final class ProcessInfoTests : XCTestCase { let expectedMinMajorVersion = 2 #endif XCTAssertGreaterThanOrEqual(version.majorVersion, expectedMinMajorVersion, "Unrealistic major system version") - #elseif os(Windows) || os(Linux) + #elseif os(Windows) || os(Linux) || os(Android) let minVersion = OperatingSystemVersion(majorVersion: 1, minorVersion: 0, patchVersion: 0) XCTAssertTrue(ProcessInfo.processInfo.isOperatingSystemAtLeast(minVersion)) #else @@ -171,7 +171,7 @@ final class ProcessInfoTests : XCTestCase { func testProcessName() { #if FOUNDATION_FRAMEWORK let targetName = "TestHost" -#elseif os(Linux) || os(Windows) +#elseif os(Linux) || os(Windows) || os(Android) let targetName = "FoundationPreviewPackageTests.xctest" #else let targetName = "xctest"