Skip to content

Commit 479b177

Browse files
committed
Setup a basic run of cmake-smoke-test in CI
1 parent a3caaf5 commit 479b177

File tree

2 files changed

+122
-19
lines changed

2 files changed

+122
-19
lines changed

.github/workflows/pull_request.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ jobs:
4545
linux_swift_versions: '["nightly-main", "nightly-6.2"]'
4646
windows_swift_versions: '["nightly-main"]'
4747
windows_build_command: 'swift test --no-parallel'
48+
cmake-smoke-test:
49+
name: cmake-smoke-test
50+
uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main
51+
with:
52+
linux_os_versions: '["noble"]'
53+
linux_pre_build_command: |
54+
apt-get update -y
55+
56+
# Build dependencies
57+
apt-get install -y libsqlite3-dev libncurses-dev
58+
59+
apt-get install -y cmake ninja-build
60+
linux_build_command: 'swift package cmake-smoke-test --disable-sandbox --cmake-path `which cmake` --ninja-path `which ninja` --extra-cmake-arg -DCMAKE_C_COMPILER=`which clang` --extra-cmake-arg -DCMAKE_CXX_COMPILER=`which clang++` --extra-cmake-arg -DCMAKE_Swift_COMPILER=`which swiftc`'
61+
linux_swift_versions: '["nightly-main"]'
62+
windows_swift_versions: '[]'
4863
soundness:
4964
name: Soundness
5065
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main

Plugins/cmake-smoke-test/cmake-smoke-test.swift

Lines changed: 107 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,24 @@ struct CMakeSmokeTest: CommandPlugin {
2424
}
2525

2626
guard let cmakePath = args.extractOption(named: "cmake-path").last else { throw Errors.missingRequiredOption("--cmake-path") }
27-
print("using cmake at \(cmakePath)")
27+
Diagnostics.progress("using cmake at \(cmakePath)")
2828
let cmakeURL = URL(filePath: cmakePath)
2929
guard let ninjaPath = args.extractOption(named: "ninja-path").last else { throw Errors.missingRequiredOption("--ninja-path") }
30-
print("using ninja at \(ninjaPath)")
30+
Diagnostics.progress("using ninja at \(ninjaPath)")
3131
let ninjaURL = URL(filePath: ninjaPath)
3232
let sysrootPath = args.extractOption(named: "sysroot-path").last
3333
if let sysrootPath {
34-
print("using sysroot at \(sysrootPath)")
34+
Diagnostics.progress("using sysroot at \(sysrootPath)")
3535
}
3636

37+
let extraCMakeArgs = args.extractOption(named: "extra-cmake-arg")
38+
Diagnostics.progress("Extra cmake args: \(extraCMakeArgs.joined(separator: " "))")
39+
3740
let moduleCachePath = context.pluginWorkDirectoryURL.appending(component: "module-cache").path()
3841

3942
let swiftBuildURL = context.package.directoryURL
4043
let swiftBuildBuildURL = context.pluginWorkDirectoryURL.appending(component: "swift-build")
41-
print("swift-build: \(swiftBuildURL.path())")
44+
Diagnostics.progress("swift-build: \(swiftBuildURL.path())")
4245

4346
let swiftToolsSupportCoreURL = try findDependency("swift-tools-support-core", pluginContext: context)
4447
let swiftToolsSupportCoreBuildURL = context.pluginWorkDirectoryURL.appending(component: "swift-tools-support-core")
@@ -80,39 +83,39 @@ struct CMakeSmokeTest: CommandPlugin {
8083
"-DCMAKE_MAKE_PROGRAM=\(ninjaPath)",
8184
"-DCMAKE_BUILD_TYPE:=Debug",
8285
"-DCMAKE_Swift_FLAGS='\(sharedSwiftFlags.joined(separator: " "))'"
83-
] + cMakeProjectArgs
86+
] + cMakeProjectArgs + extraCMakeArgs
8487

85-
print("Building swift-tools-support-core")
88+
Diagnostics.progress("Building swift-tools-support-core")
8689
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + [swiftToolsSupportCoreURL.path()], workingDirectory: swiftToolsSupportCoreBuildURL)
8790
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftToolsSupportCoreBuildURL)
88-
print("Built swift-tools-support-core")
91+
Diagnostics.progress("Built swift-tools-support-core")
8992

9093
if hostOS != .macOS {
91-
print("Building swift-system")
94+
Diagnostics.progress("Building swift-system")
9295
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + [swiftSystemURL.path()], workingDirectory: swiftSystemBuildURL)
9396
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftSystemBuildURL)
94-
print("Built swift-system")
97+
Diagnostics.progress("Built swift-system")
9598
}
9699

97-
print("Building llbuild")
100+
Diagnostics.progress("Building llbuild")
98101
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + ["-DLLBUILD_SUPPORT_BINDINGS:=Swift", llbuildURL.path()], workingDirectory: llbuildBuildURL)
99102
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: llbuildBuildURL)
100-
print("Built llbuild")
103+
Diagnostics.progress("Built llbuild")
101104

102-
print("Building swift-argument-parser")
105+
Diagnostics.progress("Building swift-argument-parser")
103106
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + ["-DBUILD_TESTING=NO", "-DBUILD_EXAMPLES=NO", swiftArgumentParserURL.path()], workingDirectory: swiftArgumentParserBuildURL)
104107
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftArgumentParserBuildURL)
105-
print("Built swift-argument-parser")
108+
Diagnostics.progress("Built swift-argument-parser")
106109

107-
print("Building swift-driver")
110+
Diagnostics.progress("Building swift-driver")
108111
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + [swiftDriverURL.path()], workingDirectory: swiftDriverBuildURL)
109112
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftDriverBuildURL)
110-
print("Built swift-driver")
113+
Diagnostics.progress("Built swift-driver")
111114

112-
print("Building swift-build in \(swiftBuildBuildURL)")
115+
Diagnostics.progress("Building swift-build in \(swiftBuildBuildURL)")
113116
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + [swiftBuildURL.path()], workingDirectory: swiftBuildBuildURL)
114117
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftBuildBuildURL)
115-
print("Built swift-build")
118+
Diagnostics.progress("Built swift-build")
116119
}
117120

118121
func findDependency(_ name: String, pluginContext: PluginContext) throws -> URL {
@@ -132,7 +135,7 @@ struct CMakeSmokeTest: CommandPlugin {
132135
throw Errors.missingRepository(name)
133136
}
134137
let dependencyURL = dependency.directoryURL
135-
print("\(name): \(dependencyURL.path())")
138+
Diagnostics.progress("\(name): \(dependencyURL.path())")
136139
guard FileManager.default.fileExists(atPath: dependencyURL.path()) else {
137140
throw Errors.missingRepository(dependencyURL.path())
138141
}
@@ -145,6 +148,7 @@ enum Errors: Error {
145148
case missingRequiredOption(String)
146149
case missingRepository(String)
147150
case unimplementedForHostOS
151+
case miscError(String)
148152
}
149153

150154
enum OS {
@@ -182,7 +186,52 @@ extension Process {
182186
}
183187

184188
static func checkNonZeroExit(url: URL, arguments: [String], workingDirectory: URL, environment: [String: String]? = nil) async throws {
185-
print("\(url.path()) \(arguments.joined(separator: " "))")
189+
Diagnostics.progress("\(url.path()) \(arguments.joined(separator: " "))")
190+
#if os(Linux)
191+
// Linux workaround for https://github.com/swiftlang/swift-corelibs-foundation/issues/4772
192+
// Foundation.Process on Linux seems to inherit the Process.run()-calling thread's signal mask, creating processes that even have SIGTERM blocked
193+
// This manifests as CMake hanging when invoking 'uname' with incorrectly configured signal handlers.
194+
var fileActions = posix_spawn_file_actions_t()
195+
defer { posix_spawn_file_actions_destroy(&fileActions) }
196+
var attrs: posix_spawnattr_t = posix_spawnattr_t()
197+
defer { posix_spawnattr_destroy(&attrs) }
198+
posix_spawn_file_actions_init(&fileActions)
199+
posix_spawn_file_actions_addchdir_np(&fileActions, workingDirectory.path())
200+
201+
posix_spawnattr_init(&attrs)
202+
posix_spawnattr_setpgroup(&attrs, 0)
203+
var noSignals = sigset_t()
204+
sigemptyset(&noSignals)
205+
posix_spawnattr_setsigmask(&attrs, &noSignals)
206+
207+
var mostSignals = sigset_t()
208+
sigemptyset(&mostSignals)
209+
for i in 1 ..< SIGSYS {
210+
if i == SIGKILL || i == SIGSTOP {
211+
continue
212+
}
213+
sigaddset(&mostSignals, i)
214+
}
215+
posix_spawnattr_setsigdefault(&attrs, &mostSignals)
216+
posix_spawnattr_setflags(&attrs, numericCast(POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK))
217+
var pid: pid_t = -1
218+
try withArrayOfCStrings([url.path()] + arguments) { arguments in
219+
try withArrayOfCStrings((environment ?? [:]).map { key, value in "\(key)=\(value)" }) { environment in
220+
let spawnResult = posix_spawn(&pid, url.path(), /*file_actions=*/&fileActions, /*attrp=*/&attrs, arguments, nil);
221+
var exitCode: Int32 = -1
222+
var result = wait4(pid, &exitCode, 0, nil);
223+
while (result == -1 && errno == EINTR) {
224+
result = wait4(pid, &exitCode, 0, nil)
225+
}
226+
guard result != -1 else {
227+
throw Errors.miscError("wait failed")
228+
}
229+
guard exitCode == 0 else {
230+
throw Errors.miscError("exit code nonzero")
231+
}
232+
}
233+
}
234+
#else
186235
let process = Process()
187236
process.executableURL = url
188237
process.arguments = arguments
@@ -192,5 +241,44 @@ extension Process {
192241
if process.terminationStatus != 0 {
193242
throw Errors.processError(terminationReason: process.terminationReason, terminationStatus: process.terminationStatus)
194243
}
244+
#endif
245+
}
246+
}
247+
248+
func scan<S: Sequence, U>(_ seq: S, _ initial: U, _ combine: (U, S.Element) -> U) -> [U] {
249+
var result: [U] = []
250+
result.reserveCapacity(seq.underestimatedCount)
251+
var runningResult = initial
252+
for element in seq {
253+
runningResult = combine(runningResult, element)
254+
result.append(runningResult)
255+
}
256+
return result
257+
}
258+
259+
func withArrayOfCStrings<T>(
260+
_ args: [String],
261+
_ body: (UnsafePointer<UnsafeMutablePointer<Int8>?>) throws -> T
262+
) throws -> T {
263+
let argsCounts = Array(args.map { $0.utf8.count + 1 })
264+
let argsOffsets = [0] + scan(argsCounts, 0, +)
265+
let argsBufferSize = argsOffsets.last!
266+
var argsBuffer: [UInt8] = []
267+
argsBuffer.reserveCapacity(argsBufferSize)
268+
for arg in args {
269+
argsBuffer.append(contentsOf: arg.utf8)
270+
argsBuffer.append(0)
271+
}
272+
return try argsBuffer.withUnsafeMutableBufferPointer {
273+
(argsBuffer) in
274+
let ptr = UnsafeRawPointer(argsBuffer.baseAddress!).bindMemory(
275+
to: Int8.self, capacity: argsBuffer.count)
276+
var cStrings: [UnsafePointer<Int8>?] = argsOffsets.map { ptr + $0 }
277+
cStrings[cStrings.count - 1] = nil
278+
return try cStrings.withUnsafeMutableBufferPointer {
279+
let unsafeString = UnsafeMutableRawPointer($0.baseAddress!).bindMemory(
280+
to: UnsafeMutablePointer<Int8>?.self, capacity: $0.count)
281+
return try body(unsafeString)
195282
}
283+
}
196284
}

0 commit comments

Comments
 (0)