Skip to content

Commit 32eb1f5

Browse files
committed
Merge branch 'fix/frizlab/compilation_ancient_platforms' into develop
2 parents 9765c78 + bee1c2b commit 32eb1f5

File tree

5 files changed

+162
-20
lines changed

5 files changed

+162
-20
lines changed

Sources/JSONLogger.swift

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,24 @@ public struct JSONLogger : LogHandler {
3939

4040
public static let defaultJSONEncoder: JSONEncoder = {
4141
let res = JSONEncoder()
42-
#if swift(>=5.3)
43-
res.outputFormatting = [.withoutEscapingSlashes]
42+
#if canImport(Darwin) || swift(>=5.3)
43+
if #available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) {
44+
res.outputFormatting = [.withoutEscapingSlashes]
45+
}
4446
#endif
4547
res.keyEncodingStrategy = .useDefaultKeys
46-
res.dateEncodingStrategy = .iso8601
48+
if #available(macOS 10.12, tvOS 10.0, iOS 10.0, watchOS 3.0, *) {
49+
res.dateEncodingStrategy = .iso8601
50+
} else {
51+
res.dateEncodingStrategy = .formatted({
52+
/* Technically an RFC3339 date formatter (straight from the doc), but compatible with ISO8601. */
53+
let ret = DateFormatter()
54+
ret.locale = Locale(identifier: "en_US_POSIX")
55+
ret.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
56+
ret.timeZone = TimeZone(secondsFromGMT: 0)
57+
return ret
58+
}())
59+
}
4760
res.dataEncodingStrategy = .base64
4861
res.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "+inf", negativeInfinity: "-inf", nan: "nan")
4962
return res
@@ -52,22 +65,24 @@ public struct JSONLogger : LogHandler {
5265
#if swift(>=5.7)
5366
public static let defaultJSONCodersForStringConvertibles: (JSONEncoder, JSONDecoder) = {
5467
let encoder = JSONEncoder()
55-
encoder.outputFormatting = [.withoutEscapingSlashes]
68+
if #available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) {
69+
encoder.outputFormatting = [.withoutEscapingSlashes]
70+
}
5671
encoder.keyEncodingStrategy = .useDefaultKeys
5772
encoder.dateEncodingStrategy = .iso8601
5873
encoder.dataEncodingStrategy = .base64
5974
encoder.nonConformingFloatEncodingStrategy = .throw
6075
let decoder = JSONDecoder()
6176
/* #if os(Darwin) is not available on this version of the compiler. */
62-
#if !os(Linux)
77+
#if canImport(Darwin) || swift(>=6.0)
6378
if #available(macOS 12.0, tvOS 15.0, iOS 15.0, watchOS 8.0, *) {
6479
decoder.allowsJSON5 = false
6580
}
6681
#endif
6782
decoder.keyDecodingStrategy = .useDefaultKeys
6883
decoder.dateDecodingStrategy = .iso8601
6984
decoder.dataDecodingStrategy = .base64
70-
#if !os(Linux)
85+
#if canImport(Darwin) || swift(>=6.0)
7186
if #available(macOS 12.0, tvOS 15.0, iOS 15.0, watchOS 8.0, *) {
7287
decoder.assumesTopLevelDictionary = false
7388
}
@@ -210,7 +225,7 @@ public struct JSONLogger : LogHandler {
210225
}
211226
let lineDataNoSeparator = prefix + jsonLine + suffix
212227

213-
/* We lock, because the writeAll function might split the write in more than 1 write
228+
/* We lock, because the write(contentsOf:) function might split the write in more than 1 write
214229
* (if the write system call only writes a part of the data).
215230
* If another part of the program writes to fd, we might get interleaved data,
216231
* because they cannot be aware of our lock (and we cannot be aware of theirs if they have one). */
@@ -222,7 +237,48 @@ public struct JSONLogger : LogHandler {
222237
/* Is the write retried on interrupt?
223238
* We’ll assume yes, but we don’t and can’t know for sure
224239
* until FileHandle has been migrated to the open-source Foundation. */
225-
_ = try? outputFileHandle.write(contentsOf: interLogData + lineDataNoSeparator)
240+
let data = interLogData + lineDataNoSeparator
241+
/* Is there a better idea than silently drop the message in case of fail? */
242+
/* Is the write retried on interrupt?
243+
* We’ll assume yes, but we don’t and can’t know for sure
244+
* until FileHandle has been migrated to the open-source Foundation. */
245+
if #available(macOS 10.15.4, tvOS 13.4, iOS 13.4, watchOS 6.2, *) {
246+
#if swift(>=5.2) || !canImport(Darwin)
247+
_ = try? outputFileHandle.write(contentsOf: data)
248+
#else
249+
/* Let’s write “manually” (FileHandle’s write(_:) method throws an ObjC exception in case of an error).
250+
* This code is copied below. */
251+
data.withUnsafeBytes{ bytes in
252+
guard !bytes.isEmpty else {
253+
return
254+
}
255+
var written: Int = 0
256+
repeat {
257+
written += write(
258+
outputFileHandle.fileDescriptor,
259+
bytes.baseAddress!.advanced(by: written),
260+
bytes.count - written
261+
)
262+
} while written < bytes.count && (errno == EINTR || errno == EAGAIN)
263+
}
264+
#endif
265+
} else {
266+
/* Let’s write “manually” (FileHandle’s write(_:) method throws an ObjC exception in case of an error).
267+
* This is a copy of the code just above. */
268+
data.withUnsafeBytes{ bytes in
269+
guard !bytes.isEmpty else {
270+
return
271+
}
272+
var written: Int = 0
273+
repeat {
274+
written += write(
275+
outputFileHandle.fileDescriptor,
276+
bytes.baseAddress!.advanced(by: written),
277+
bytes.count - written
278+
)
279+
} while written < bytes.count && (errno == EINTR || errno == EAGAIN)
280+
}
281+
}
226282
}
227283
}
228284

Sources/NSLock+Linux.swift renamed to Sources/NSLock+withLock.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#if os(Linux) && swift(<6.0)
1+
#if swift(<5.7) || (!canImport(Darwin) && swift(<6.0))
22
import Foundation
33

44

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import Foundation
2+
3+
4+
5+
#if swift(>=5.10)
6+
private nonisolated(unsafe) let system_read = read
7+
private nonisolated(unsafe) let system_close = close
8+
#else
9+
private let system_read = read
10+
private let system_close = close
11+
#endif
12+
13+
extension FileHandle {
14+
15+
func jl_close() throws {
16+
if #available(macOS 10.15, *) {
17+
try close()
18+
} else {
19+
/* closeFile exists but I’m not sure it never throws an ObjC exception, so I call the C function. */
20+
let ret = system_close(fileDescriptor)
21+
guard ret == 0 else {
22+
throw Errno()
23+
}
24+
}
25+
}
26+
27+
func jl_readToEnd() throws -> Data? {
28+
if #available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) {
29+
#if swift(>=5.2) || !canImport(Darwin)
30+
return try readToEnd()
31+
#else
32+
let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 512, alignment: MemoryLayout<UInt8>.alignment)
33+
defer {buffer.deallocate()}
34+
35+
var nread = 0
36+
var ret = Data()
37+
repeat {
38+
nread = system_read(fileDescriptor, buffer.baseAddress, buffer.count)
39+
ret += buffer[0..<nread]
40+
} while nread > 0
41+
guard nread >= 0 else {
42+
throw Errno()
43+
}
44+
return ret
45+
#endif
46+
} else {
47+
let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 512, alignment: MemoryLayout<UInt8>.alignment)
48+
defer {buffer.deallocate()}
49+
50+
var nread = 0
51+
var ret = Data()
52+
repeat {
53+
nread = system_read(fileDescriptor, buffer.baseAddress, buffer.count)
54+
ret += buffer[0..<nread]
55+
} while nread > 0
56+
guard nread >= 0 else {
57+
throw Errno()
58+
}
59+
return ret
60+
}
61+
}
62+
63+
}
64+
65+
66+
struct Errno : Error {
67+
#if canImport(Darwin)
68+
var err: errno_t
69+
#else
70+
var err: Int32
71+
#endif
72+
init() {
73+
self.err = errno
74+
}
75+
}

Tests/JSONLoggerTests/JSONLoggerTests.swift

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,26 @@ final class JSONLoggerTests : XCTestCase {
1212
public static let defaultJSONDecoder: JSONDecoder = {
1313
let res = JSONDecoder()
1414
if #available(macOS 12.0, tvOS 15.0, iOS 15.0, watchOS 8.0, *) {
15-
#if canImport(Darwin) || compiler(>=6)
15+
#if (canImport(Darwin) && swift(>=5.5)) || swift(>=6)
1616
res.allowsJSON5 = false
1717
#endif
1818
}
1919
res.keyDecodingStrategy = .useDefaultKeys
20-
res.dateDecodingStrategy = .iso8601
20+
if #available(macOS 10.12, tvOS 10.0, iOS 10.0, watchOS 3.0, *) {
21+
res.dateDecodingStrategy = .iso8601
22+
} else {
23+
res.dateDecodingStrategy = .formatted({
24+
/* The same formatter we give in the encoding part (in `defaultJSONEncoder`). */
25+
let ret = DateFormatter()
26+
ret.locale = Locale(identifier: "en_US_POSIX")
27+
ret.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
28+
ret.timeZone = TimeZone(secondsFromGMT: 0)
29+
return ret
30+
}())
31+
}
2132
res.dataDecodingStrategy = .base64
2233
if #available(macOS 12.0, tvOS 15.0, iOS 15.0, watchOS 8.0, *) {
23-
#if canImport(Darwin) || compiler(>=6)
34+
#if (canImport(Darwin) && swift(>=5.5)) || swift(>=6)
2435
res.assumesTopLevelDictionary = false
2536
#endif
2637
}
@@ -55,17 +66,17 @@ final class JSONLoggerTests : XCTestCase {
5566
let pipe = Pipe()
5667
let jsonLogger = JSONLogger(label: "best-logger", fileHandle: pipe.fileHandleForWriting, lineSeparator: Data([0x0a]), prefix: Data(), suffix: Data())
5768
jsonLogger.log(level: .info, message: "First log message", metadata: nil, source: "dummy-source", file: "dummy-file", function: "dummy-function", line: 42)
58-
try pipe.fileHandleForWriting.close()
59-
let data = try pipe.fileHandleForReading.readToEnd() ?? Data()
69+
try pipe.fileHandleForWriting.jl_close()
70+
let data = try pipe.fileHandleForReading.jl_readToEnd() ?? Data()
6071
XCTAssertEqual(data.first, 0x7b)
6172
}
6273

6374
func testSeparatorForNotFirstLog() throws {
6475
let pipe = Pipe()
6576
let jsonLogger = JSONLogger(label: "best-logger", fileHandle: pipe.fileHandleForWriting, lineSeparator: Data([0x0a]), prefix: Data(), suffix: Data())
6677
jsonLogger.log(level: .info, message: "Not first log message", metadata: nil, source: "dummy-source", file: "dummy-file", function: "dummy-function", line: 42)
67-
try pipe.fileHandleForWriting.close()
68-
let data = try pipe.fileHandleForReading.readToEnd() ?? Data()
78+
try pipe.fileHandleForWriting.jl_close()
79+
let data = try pipe.fileHandleForReading.jl_readToEnd() ?? Data()
6980
XCTAssertEqual(data.first, 0x0a)
7081
}
7182

@@ -80,8 +91,8 @@ final class JSONLoggerTests : XCTestCase {
8091
let pipe = Pipe()
8192
let jsonLogger = JSONLogger(label: "best-logger", fileHandle: pipe.fileHandleForWriting)
8293
jsonLogger.log(level: ref.level, message: "\(ref.message)", metadata: ["yolo": .stringConvertible(BestStruct(val: 21))], source: ref.source, file: ref.file, function: ref.function, line: ref.line)
83-
try pipe.fileHandleForWriting.close()
84-
let data = try pipe.fileHandleForReading.readToEnd() ?? Data()
94+
try pipe.fileHandleForWriting.jl_close()
95+
let data = try pipe.fileHandleForReading.jl_readToEnd() ?? Data()
8596
//print(data.reduce("", { $0 + String(format: "%02x", $1) }))
8697
var line = try Self.defaultJSONDecoder.decode(LogLine.self, from: data)
8798
XCTAssertLessThanOrEqual(line.date.timeIntervalSince(ref.date), 0.1)
@@ -111,8 +122,8 @@ final class JSONLoggerTests : XCTestCase {
111122
let pipe = Pipe()
112123
let jsonLogger = JSONLogger(label: "best-logger", fileHandle: pipe.fileHandleForWriting, jsonEncoder: failEncoder)
113124
jsonLogger.log(level: ref.level, message: "\(ref.message)", metadata: ["yolo": .stringConvertible(BestStruct(val: 21))], source: ref.source, file: ref.file, function: ref.function, line: ref.line)
114-
try pipe.fileHandleForWriting.close()
115-
let data = try pipe.fileHandleForReading.readToEnd() ?? Data()
125+
try pipe.fileHandleForWriting.jl_close()
126+
let data = try pipe.fileHandleForReading.jl_readToEnd() ?? Data()
116127
var line = try Self.defaultJSONDecoder.decode(LogLine.self, from: data)
117128
XCTAssertLessThanOrEqual(line.date.timeIntervalSince(ref.date), 0.1)
118129
line.date = mangled.date

0 commit comments

Comments
 (0)