From 96b4c6b1311a29d6399d03627e4e4b7bce23116d Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 17 Feb 2025 15:22:00 -0800 Subject: [PATCH 1/7] SWBCore: re-iterate `@unchecked Sendable` conformance This silences a warning from the newer Swift compiler by re-iterating the `Sendable` conformance. --- Sources/SWBCore/ProjectModel/Reference.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SWBCore/ProjectModel/Reference.swift b/Sources/SWBCore/ProjectModel/Reference.swift index 7971142d..af885341 100644 --- a/Sources/SWBCore/ProjectModel/Reference.swift +++ b/Sources/SWBCore/ProjectModel/Reference.swift @@ -324,7 +324,7 @@ public final class VariantGroup: GroupTreeReference, BuildFileRepresentable, @un /// A ProductReference represents the product of a StandardTarget object. It acts as a placeholder so that product can be represented in other targets, but it contains no meaningful information itself; rather, it vends information about itself by querying its target for that information. A ProductReference object is not part of a product's group tree and has no parent property; rather, it is owned by and has an unowned backpointer to its target. -public final class ProductReference: Reference, BuildFileRepresentable +public final class ProductReference: Reference, BuildFileRepresentable, @unchecked Sendable { /// The name of this reference - primarily for debugging purposes. public let name: String From 2c5ca0c9c6f31337892a0a02a1e66dfe62b665f0 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 17 Feb 2025 15:24:04 -0800 Subject: [PATCH 2/7] Tests: remove unnecessary `#equire`s Silence some warnings from the compiler about the unnecessary `#require` which were being used for the unwrapping. --- Tests/SWBCoreTests/PIFLoadingTests.swift | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Tests/SWBCoreTests/PIFLoadingTests.swift b/Tests/SWBCoreTests/PIFLoadingTests.swift index 33bb9cb9..54ac6a22 100644 --- a/Tests/SWBCoreTests/PIFLoadingTests.swift +++ b/Tests/SWBCoreTests/PIFLoadingTests.swift @@ -683,14 +683,14 @@ private final class ProjectModelItemClass: ProjectModelItem { #expect(fileGroup.children.count == 2) // Examine its children - if let fileRef = try? #require(fileGroup.children.first as? FileReference) { + if let fileRef = fileGroup.children.first as? FileReference { #expect(fileRef.guid == "first-fileReference-guid") #expect(fileRef.sourceTree == SourceTree.groupRelative) #expect(fileRef.path.stringRep == "ClassOne.m") #expect(fileRef.fileTypeIdentifier == "sourcecode.c.objc") #expect(fileRef.regionVariantName == nil) } - if let fileRef = try? #require(fileGroup.children[1] as? FileReference) { + if let fileRef = fileGroup.children[1] as? FileReference { #expect(fileRef.guid == "second-fileReference-guid") #expect(fileRef.sourceTree == SourceTree.groupRelative) #expect(fileRef.path.stringRep == "ClassOne.h") @@ -740,14 +740,14 @@ private final class ProjectModelItemClass: ProjectModelItem { #expect(versionGroup.children.count == 2) // Examine its children - if let fileRef = try? #require(versionGroup.children[0] as? FileReference) { + if let fileRef = versionGroup.children[0] as? FileReference { #expect(fileRef.guid == "first-versionedFile-guid") #expect(fileRef.sourceTree == SourceTree.groupRelative) #expect(fileRef.path.stringRep == "CoreData-1.xcdatamodel") #expect(fileRef.fileTypeIdentifier == "wrapper.xcdatamodel") #expect(fileRef.regionVariantName == nil) } - if let fileRef = try? #require(versionGroup.children[1] as? FileReference) { + if let fileRef = versionGroup.children[1] as? FileReference { #expect(fileRef.guid == "second-versionedFile-guid") #expect(fileRef.sourceTree == SourceTree.groupRelative) #expect(fileRef.path.stringRep == "CoreData-2.xcdatamodel") @@ -821,21 +821,21 @@ private final class ProjectModelItemClass: ProjectModelItem { #expect(variantGroup.name == "Thingy.xib") // Examine its children, the xib and its localized strings files - if let fileRef = try? #require(variantGroup.children[0] as? FileReference) { + if let fileRef = variantGroup.children[0] as? FileReference { #expect(fileRef.guid == "xib-fileReference-guid") #expect(fileRef.sourceTree == SourceTree.groupRelative) #expect(fileRef.path.stringRep == "Thingy.xib") #expect(fileRef.fileTypeIdentifier == "file.xib") #expect(fileRef.regionVariantName == nil) } - if let fileRef = try? #require(variantGroup.children[1] as? FileReference) { + if let fileRef = variantGroup.children[1] as? FileReference { #expect(fileRef.guid == "fr-strings-fileReference-guid") #expect(fileRef.sourceTree == SourceTree.groupRelative) #expect(fileRef.path.stringRep == "Thingy.strings") #expect(fileRef.fileTypeIdentifier == "text.plist.strings") #expect(fileRef.regionVariantName == "fr") } - if let fileRef = try? #require(variantGroup.children[2] as? FileReference) { + if let fileRef = variantGroup.children[2] as? FileReference { #expect(fileRef.guid == "de-strings-fileReference-guid") #expect(fileRef.sourceTree == SourceTree.groupRelative) #expect(fileRef.path.stringRep == "Thingy.strings") @@ -941,7 +941,7 @@ private final class ProjectModelItemClass: ProjectModelItem { ] // Convert the test data into a property list, then read the build phase from it. - if let buildPhase = try? #require(BuildPhase.parsePIFDictAsBuildPhase(buildPhasePIF, pifLoader: pifLoader) as? SourcesBuildPhase) { + if let buildPhase = try? BuildPhase.parsePIFDictAsBuildPhase(buildPhasePIF, pifLoader: pifLoader) as? SourcesBuildPhase { // Examine the build phase. #expect(buildPhase.buildFiles.count == 1) } @@ -962,7 +962,7 @@ private final class ProjectModelItemClass: ProjectModelItem { ] // Convert the test data into a property list, then read the build phase from it. - if let buildPhase = try? #require(BuildPhase.parsePIFDictAsBuildPhase(buildPhasePIF, pifLoader: pifLoader) as? HeadersBuildPhase) { + if let buildPhase = try? BuildPhase.parsePIFDictAsBuildPhase(buildPhasePIF, pifLoader: pifLoader) as? HeadersBuildPhase { // Examine the build phase. #expect(buildPhase.buildFiles.count == 1) } @@ -983,7 +983,7 @@ private final class ProjectModelItemClass: ProjectModelItem { ] // Convert the test data into a property list, then read the build phase from it. - if let buildPhase = try? #require(BuildPhase.parsePIFDictAsBuildPhase(buildPhasePIF, pifLoader: pifLoader) as? ResourcesBuildPhase) { + if let buildPhase = try? BuildPhase.parsePIFDictAsBuildPhase(buildPhasePIF, pifLoader: pifLoader) as? ResourcesBuildPhase { // Examine the build phase. #expect(buildPhase.buildFiles.count == 1) } @@ -1007,7 +1007,7 @@ private final class ProjectModelItemClass: ProjectModelItem { ] // Convert the test data into a property list, then read the build phase from it. - if let buildPhase = try? #require(BuildPhase.parsePIFDictAsBuildPhase(buildPhasePIF, pifLoader: pifLoader) as? CopyFilesBuildPhase) { + if let buildPhase = try? BuildPhase.parsePIFDictAsBuildPhase(buildPhasePIF, pifLoader: pifLoader) as? CopyFilesBuildPhase { // Examine the build phase. #expect(buildPhase.destinationSubfolder.stringRep == "Resources") #expect(buildPhase.destinationSubpath.stringRep == "Subpath") @@ -1036,7 +1036,7 @@ private final class ProjectModelItemClass: ProjectModelItem { ] // Convert the test data into a property list, then read the build phase from it. - if let buildPhase = try? #require(BuildPhase.parsePIFDictAsBuildPhase(buildPhasePIF, pifLoader: pifLoader) as? ShellScriptBuildPhase) { + if let buildPhase = try? BuildPhase.parsePIFDictAsBuildPhase(buildPhasePIF, pifLoader: pifLoader) as? ShellScriptBuildPhase { // Examine the build phase. #expect(buildPhase.guid == "some-shellScriptBuildPhase-guid") #expect(buildPhase.name == "A Shell Script Phase") @@ -1353,7 +1353,7 @@ private final class ProjectModelItemClass: ProjectModelItem { // Because of the way reference resolution of a BuildFile.BuildableItem works, we don't have a context to resolve the build file's references to real references, so all we can do is check that the GUID is what we expect. func checkBuildFileRef(of buildPhase: BuildPhaseWithBuildFiles, fileRef: FileReference) throws { - guard let buildFileRef = try? #require(buildPhase.buildFiles.first) else { + guard let buildFileRef = buildPhase.buildFiles.first else { return } guard case let .reference(buildFileRefGuid) = buildFileRef.buildableItem else { From 7371d7ad2f86c14cdc29dc3e2c2fc76efa66f7a6 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 17 Feb 2025 15:27:07 -0800 Subject: [PATCH 3/7] SWBUtil: use `FileManager` for file removal `unlink` is deprecated on Windows, and `RemoveFileW` should be preferred. However, that would limit the path to `MAX_PATH` (261) characters. Prefer to use `FileManager to remove the file to avoid the path limit. --- Sources/SWBUtil/FSProxy.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/SWBUtil/FSProxy.swift b/Sources/SWBUtil/FSProxy.swift index 5b9d7fc1..d6873b60 100644 --- a/Sources/SWBUtil/FSProxy.swift +++ b/Sources/SWBUtil/FSProxy.swift @@ -562,9 +562,13 @@ class LocalFS: FSProxy, @unchecked Sendable { } func remove(_ path: Path) throws { + #if os(Windows) + try fileManager.removeItem(atPath: path.str) + #else guard unlink(path.str) == 0 else { throw POSIXError(errno, context: "unlink", path.str) } + #endif } func removeDirectory(_ path: Path) throws { From 80dedf06b2dc28dd09f35baba716462e77520f96 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 17 Feb 2025 15:32:17 -0800 Subject: [PATCH 4/7] SWBUtil: adjust `strerror` usage for Windows Windows marks `strerror` as deprecated. `strerror_r` is the preferred spelling on POSIX systems, `strerror_s` is preferred on Windows. There is a limit of 94 characters on the error description. --- Sources/SWBUtil/POSIX.swift | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/Sources/SWBUtil/POSIX.swift b/Sources/SWBUtil/POSIX.swift index 546da9bd..ffb4ae1c 100644 --- a/Sources/SWBUtil/POSIX.swift +++ b/Sources/SWBUtil/POSIX.swift @@ -15,17 +15,22 @@ import SWBLibc public import protocol Foundation.LocalizedError #if os(Windows) -#if canImport(System) -import System -#else -import SystemPackage -#endif +public import Foundation + +private func strerror(_ code: CInt) -> String { + withUnsafeTemporaryAllocation(of: CChar.self, capacity: 95) { + guard strerror_s($0.baseAddress, $0.count, code) == 0 else { + return "unknown error" + } + return String(cString: $0.baseAddress!) + } +} #endif public enum POSIX: Sendable { public static func getenv(_ name: String) throws -> String? { #if os(Windows) - try name.withCString(encodedAs: CInterop.PlatformUnicodeEncoding.self) { wName in + try name.withCString(encodedAs: UTF16.self) { wName in let dwLength: DWORD = GetEnvironmentVariableW(wName, nil, 0) if dwLength == 0 { if GetLastError() == ERROR_ENVVAR_NOT_FOUND { @@ -36,7 +41,7 @@ public enum POSIX: Sendable { return try withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: Int(dwLength)) { switch GetEnvironmentVariableW(wName, $0.baseAddress!, DWORD($0.count)) { case dwLength - 1: - return String(decodingCString: $0.baseAddress!, as: CInterop.PlatformUnicodeEncoding.self) + return String(decodingCString: $0.baseAddress!, as: UTF16.self) case 0 where GetLastError() == ERROR_ENVVAR_NOT_FOUND: return nil default: @@ -54,13 +59,13 @@ public enum POSIX: Sendable { let valueString = String(cString: value) #if os(Windows) if overwrite == 0 { - if nameString.withCString(encodedAs: CInterop.PlatformUnicodeEncoding.self, { GetEnvironmentVariableW($0, nil, 0) }) == 0 && GetLastError() != ERROR_ENVVAR_NOT_FOUND { + if nameString.withCString(encodedAs: UTF16.self, { GetEnvironmentVariableW($0, nil, 0) }) == 0 && GetLastError() != ERROR_ENVVAR_NOT_FOUND { throw POSIXError(errno, context: "GetEnvironmentVariableW", nameString) } return } - guard nameString.withCString(encodedAs: CInterop.PlatformUnicodeEncoding.self, { nameWString in - valueString.withCString(encodedAs: CInterop.PlatformUnicodeEncoding.self, { valueWString in + guard nameString.withCString(encodedAs: UTF16.self, { nameWString in + valueString.withCString(encodedAs: UTF16.self, { valueWString in SetEnvironmentVariableW(nameWString, valueWString) }) }) else { @@ -77,7 +82,7 @@ public enum POSIX: Sendable { public static func unsetenv(_ name: UnsafePointer) throws { let nameString = String(cString: name) #if os(Windows) - guard nameString.withCString(encodedAs: CInterop.PlatformUnicodeEncoding.self, { SetEnvironmentVariableW($0, nil) }) else { + guard nameString.withCString(encodedAs: UTF16.self, { SetEnvironmentVariableW($0, nil) }) else { throw POSIXError(errno, context: "SetEnvironmentVariableW", nameString) } #else @@ -105,7 +110,11 @@ public struct POSIXError: Error, LocalizedError, CustomStringConvertible, Equata } public var description: String { + #if os(Windows) + let end = "\(strerror(code)) (\(code))" + #else let end = "\(String(cString: strerror(code))) (\(code))" + #endif if let context { return "\(context)(\(arguments.joined(separator: ", "))): \(end)" } From a8666d56f071c72795002b5cb487786595cc951c Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 17 Feb 2025 15:39:23 -0800 Subject: [PATCH 5/7] SWBUtilPerfTests: remove extraneous `#expect` Remove the unnecessary `#expect` wrapping expressions. This was causing warnings due to the comparison of a non-nil value. --- .../MsgPackSerializationPerfTests.swift | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Tests/SWBUtilPerfTests/MsgPackSerializationPerfTests.swift b/Tests/SWBUtilPerfTests/MsgPackSerializationPerfTests.swift index 5727c39e..f47d841b 100644 --- a/Tests/SWBUtilPerfTests/MsgPackSerializationPerfTests.swift +++ b/Tests/SWBUtilPerfTests/MsgPackSerializationPerfTests.swift @@ -42,9 +42,8 @@ fileprivate struct MsgPackSerializationPerfTests: PerfTests { let iterations = 100000 await measure { - for _ in 1...iterations - { - #expect(self.serializeScalarData() != nil) + for _ in 1...iterations { + _ = self.serializeScalarData() } } } @@ -91,7 +90,7 @@ fileprivate struct MsgPackSerializationPerfTests: PerfTests { await measure { for _ in 1...iterations { - #expect(self.serializeStringData() != nil) + _ = self.serializeStringData() } } } @@ -131,9 +130,8 @@ fileprivate struct MsgPackSerializationPerfTests: PerfTests { let iterations = 100000 await measure { - for _ in 1...iterations - { - #expect(self.serializeArrayData() != nil) + for _ in 1...iterations { + _ = self.serializeArrayData() } } } @@ -171,7 +169,7 @@ fileprivate struct MsgPackSerializationPerfTests: PerfTests { await measure { for _ in 1...iterations { - #expect(self.serializeDictionaryData() != nil) + _ = self.serializeDictionaryData() } } } @@ -218,9 +216,8 @@ fileprivate struct MsgPackSerializationPerfTests: PerfTests { await measure { - for _ in 1...iterations - { - #expect(self.serializeCustomElementData(elements) != nil) + for _ in 1...iterations { + _ = self.serializeCustomElementData(elements) } } } From ca5a0e1ee2ba389e5953a95b01ae70ed5393c81f Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 17 Feb 2025 15:42:07 -0800 Subject: [PATCH 6/7] SWBSpecificationsCompiler: silence unused value warning The deserialization is unused, assign the value to the blackhole to avoid a warning. --- Sources/SWBSpecificationsCompiler/Compiler.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SWBSpecificationsCompiler/Compiler.swift b/Sources/SWBSpecificationsCompiler/Compiler.swift index 3c86ba4a..43d2cdd6 100644 --- a/Sources/SWBSpecificationsCompiler/Compiler.swift +++ b/Sources/SWBSpecificationsCompiler/Compiler.swift @@ -34,7 +34,7 @@ struct Main { } var inputData = try Data(contentsOf: URL(fileURLWithPath: inputFile)) unifdef(&inputData) - try PropertyListSerialization.propertyList(from: inputData, options: [], format: nil) + _ = try PropertyListSerialization.propertyList(from: inputData, options: [], format: nil) inputData.removeAll(where: { $0 == Character("\r").asciiValue }) // normalize newlines for Windows try inputData.write(to: URL(fileURLWithPath: outputFile), options: .atomic) } From 7fb020e759223baf683699970e25c19e73fa2d34 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 17 Feb 2025 15:45:14 -0800 Subject: [PATCH 7/7] SWBCore: add a `strdup` wrapper `strdup` is marked as deprecated, preferring `_strdup`. The wrapper avoids the deprecation warning. --- Sources/SWBCore/LibclangVendored/CStringArray.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/SWBCore/LibclangVendored/CStringArray.swift b/Sources/SWBCore/LibclangVendored/CStringArray.swift index cb0768f7..f5a60379 100644 --- a/Sources/SWBCore/LibclangVendored/CStringArray.swift +++ b/Sources/SWBCore/LibclangVendored/CStringArray.swift @@ -12,6 +12,12 @@ import Foundation +#if os(Windows) +private func strdup(_ s: UnsafePointer) -> UnsafeMutablePointer { + return _strdup(s) +} +#endif + /// `CStringArray` represents a C null-terminated array of pointers to C strings. /// /// The lifetime of the C strings will correspond to the lifetime of the `CStringArray`