Skip to content

Commit 142181b

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

File tree

2 files changed

+125
-19
lines changed

2 files changed

+125
-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: 110 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
import PackagePlugin
1414
import Foundation
15+
#if os(Linux)
16+
import Glibc
17+
#endif
1518

1619
@main
1720
struct CMakeSmokeTest: CommandPlugin {
@@ -24,21 +27,24 @@ struct CMakeSmokeTest: CommandPlugin {
2427
}
2528

2629
guard let cmakePath = args.extractOption(named: "cmake-path").last else { throw Errors.missingRequiredOption("--cmake-path") }
27-
print("using cmake at \(cmakePath)")
30+
Diagnostics.progress("using cmake at \(cmakePath)")
2831
let cmakeURL = URL(filePath: cmakePath)
2932
guard let ninjaPath = args.extractOption(named: "ninja-path").last else { throw Errors.missingRequiredOption("--ninja-path") }
30-
print("using ninja at \(ninjaPath)")
33+
Diagnostics.progress("using ninja at \(ninjaPath)")
3134
let ninjaURL = URL(filePath: ninjaPath)
3235
let sysrootPath = args.extractOption(named: "sysroot-path").last
3336
if let sysrootPath {
34-
print("using sysroot at \(sysrootPath)")
37+
Diagnostics.progress("using sysroot at \(sysrootPath)")
3538
}
3639

40+
let extraCMakeArgs = args.extractOption(named: "extra-cmake-arg")
41+
Diagnostics.progress("Extra cmake args: \(extraCMakeArgs.joined(separator: " "))")
42+
3743
let moduleCachePath = context.pluginWorkDirectoryURL.appending(component: "module-cache").path()
3844

3945
let swiftBuildURL = context.package.directoryURL
4046
let swiftBuildBuildURL = context.pluginWorkDirectoryURL.appending(component: "swift-build")
41-
print("swift-build: \(swiftBuildURL.path())")
47+
Diagnostics.progress("swift-build: \(swiftBuildURL.path())")
4248

4349
let swiftToolsSupportCoreURL = try findDependency("swift-tools-support-core", pluginContext: context)
4450
let swiftToolsSupportCoreBuildURL = context.pluginWorkDirectoryURL.appending(component: "swift-tools-support-core")
@@ -80,39 +86,39 @@ struct CMakeSmokeTest: CommandPlugin {
8086
"-DCMAKE_MAKE_PROGRAM=\(ninjaPath)",
8187
"-DCMAKE_BUILD_TYPE:=Debug",
8288
"-DCMAKE_Swift_FLAGS='\(sharedSwiftFlags.joined(separator: " "))'"
83-
] + cMakeProjectArgs
89+
] + cMakeProjectArgs + extraCMakeArgs
8490

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

9096
if hostOS != .macOS {
91-
print("Building swift-system")
97+
Diagnostics.progress("Building swift-system")
9298
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + [swiftSystemURL.path()], workingDirectory: swiftSystemBuildURL)
9399
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftSystemBuildURL)
94-
print("Built swift-system")
100+
Diagnostics.progress("Built swift-system")
95101
}
96102

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

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

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

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

118124
func findDependency(_ name: String, pluginContext: PluginContext) throws -> URL {
@@ -132,7 +138,7 @@ struct CMakeSmokeTest: CommandPlugin {
132138
throw Errors.missingRepository(name)
133139
}
134140
let dependencyURL = dependency.directoryURL
135-
print("\(name): \(dependencyURL.path())")
141+
Diagnostics.progress("\(name): \(dependencyURL.path())")
136142
guard FileManager.default.fileExists(atPath: dependencyURL.path()) else {
137143
throw Errors.missingRepository(dependencyURL.path())
138144
}
@@ -145,6 +151,7 @@ enum Errors: Error {
145151
case missingRequiredOption(String)
146152
case missingRepository(String)
147153
case unimplementedForHostOS
154+
case miscError(String)
148155
}
149156

150157
enum OS {
@@ -182,7 +189,52 @@ extension Process {
182189
}
183190

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

0 commit comments

Comments
 (0)