Skip to content

Commit 58862d3

Browse files
owenvLuke Daley
authored and
Luke Daley
committed
Setup a basic run of cmake-smoke-test in CI
1 parent 13ff9db commit 58862d3

File tree

2 files changed

+123
-19
lines changed

2 files changed

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

0 commit comments

Comments
 (0)