Skip to content

Commit 5ded24f

Browse files
committed
Add a command plugin which validates the cmake based build
1 parent 325197c commit 5ded24f

File tree

5 files changed

+202
-4
lines changed

5 files changed

+202
-4
lines changed

Package.swift

+7
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,13 @@ let package = Package(
394394
verb: "run-xcodebuild",
395395
description: "Run xcodebuild from the currently selected Xcode configured to use the just-built build service"
396396
))
397+
),
398+
.plugin(
399+
name: "cmake-smoke-test",
400+
capability: .command(intent: .custom(
401+
verb: "cmake-smoke-test",
402+
description: "Build Swift Build using CMake for validation purposes"
403+
))
397404
)
398405
],
399406
swiftLanguageModes: [.v5, .v6],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import PackagePlugin
14+
import Foundation
15+
16+
@main
17+
struct CMakeSmokeTest: CommandPlugin {
18+
func performCommand(context: PluginContext, arguments: [String]) async throws {
19+
var args = ArgumentExtractor(arguments)
20+
21+
let hostOS = try OS.host()
22+
23+
guard let cmakePath = args.extractOption(named: "cmake-path").last else { throw Errors.missingRequiredOption("--cmake-path") }
24+
print("using cmake at \(cmakePath)")
25+
let cmakeURL = URL(filePath: cmakePath)
26+
guard let ninjaPath = args.extractOption(named: "ninja-path").last else { throw Errors.missingRequiredOption("--ninja-path") }
27+
print("using ninja at \(ninjaPath)")
28+
let ninjaURL = URL(filePath: ninjaPath)
29+
guard let sysrootPath = args.extractOption(named: "sysroot-path").last else { throw Errors.missingRequiredOption("--sysroot-path") }
30+
print("using sysroot at \(sysrootPath)")
31+
32+
let moduleCachePath = context.pluginWorkDirectoryURL.appending(component: "module-cache").path()
33+
34+
let swiftBuildURL = context.package.directoryURL
35+
let swiftBuildBuildURL = context.pluginWorkDirectoryURL.appending(component: "swift-build")
36+
print("swift-build: \(swiftBuildURL.path())")
37+
38+
let swiftToolsSupportCoreURL = try findSiblingRepository("swift-tools-support-core", swiftBuildURL: swiftBuildURL)
39+
let swiftToolsSupportCoreBuildURL = context.pluginWorkDirectoryURL.appending(component: "swift-tools-support-core")
40+
41+
let swiftSystemURL = try findSiblingRepository("swift-system", swiftBuildURL: swiftBuildURL)
42+
let swiftSystemBuildURL = context.pluginWorkDirectoryURL.appending(component: "swift-system")
43+
44+
let swiftAsn1URL = try findSiblingRepository("swift-asn1", swiftBuildURL: swiftBuildURL)
45+
let swiftAsn1BuildURL = context.pluginWorkDirectoryURL.appending(component: "swift-asn1")
46+
47+
let swiftCryptoURL = try findSiblingRepository("swift-crypto", swiftBuildURL: swiftBuildURL)
48+
let swiftCryptoBuildURL = context.pluginWorkDirectoryURL.appending(component: "swift-crypto")
49+
50+
let llbuildURL = try findSiblingRepository("llbuild", swiftBuildURL: swiftBuildURL)
51+
let llbuildBuildURL = context.pluginWorkDirectoryURL.appending(component: "llbuild")
52+
53+
let swiftArgumentParserURL = try findSiblingRepository("swift-argument-parser", swiftBuildURL: swiftBuildURL)
54+
let swiftArgumentParserBuildURL = context.pluginWorkDirectoryURL.appending(component: "swift-argument-parser")
55+
56+
let swiftDriverURL = try findSiblingRepository("swift-driver", swiftBuildURL: swiftBuildURL)
57+
let swiftDriverBuildURL = context.pluginWorkDirectoryURL.appending(component: "swift-driver")
58+
59+
for url in [swiftToolsSupportCoreBuildURL, swiftAsn1BuildURL, swiftCryptoBuildURL, swiftSystemBuildURL, llbuildBuildURL, swiftArgumentParserBuildURL, swiftDriverBuildURL, swiftBuildBuildURL] {
60+
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
61+
}
62+
63+
let sharedSwiftFlags = [
64+
"-sdk", sysrootPath,
65+
"-module-cache-path", moduleCachePath
66+
]
67+
68+
let cMakeProjectArgs = [
69+
"-DArgumentParser_DIR=\(swiftArgumentParserBuildURL.appending(components: "cmake", "modules").path())",
70+
"-DLLBuild_DIR=\(llbuildBuildURL.appending(components: "cmake", "modules").path())",
71+
"-DTSC_DIR=\(swiftToolsSupportCoreBuildURL.appending(components: "cmake", "modules").path())",
72+
"-DSwiftASN1_DIR=\(swiftAsn1BuildURL.appending(components: "cmake", "modules").path())",
73+
"-DSwiftCrypto_DIR=\(swiftCryptoBuildURL.appending(components: "cmake", "modules").path())",
74+
"-DSwiftDriver_DIR=\(swiftDriverBuildURL.appending(components: "cmake", "modules").path())",
75+
"-DSwiftSystem_DIR=\(swiftSystemBuildURL.appending(components: "cmake", "modules").path())"
76+
]
77+
78+
let sharedCMakeArgs = [
79+
"-G", "Ninja",
80+
"-DCMAKE_MAKE_PROGRAM=\(ninjaPath)",
81+
"-DCMAKE_BUILD_TYPE:=Debug",
82+
"-DCMAKE_Swift_FLAGS='\(sharedSwiftFlags.joined(separator: " "))'"
83+
] + cMakeProjectArgs
84+
85+
print("Building swift-tools-support-core")
86+
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + [swiftToolsSupportCoreURL.path()], workingDirectory: swiftToolsSupportCoreBuildURL)
87+
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftToolsSupportCoreBuildURL)
88+
print("Built swift-tools-support-core")
89+
90+
if hostOS != .macOS && hostOS != .windows {
91+
print("Building swift-asn1")
92+
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + [swiftAsn1URL.path()], workingDirectory: swiftAsn1BuildURL)
93+
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftAsn1BuildURL)
94+
print("Built swift-asn1")
95+
96+
print("Building swift-crypto")
97+
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + [swiftCryptoURL.path()], workingDirectory: swiftCryptoBuildURL)
98+
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftCryptoBuildURL)
99+
print("Built swift-crypto")
100+
}
101+
102+
if hostOS != .macOS {
103+
print("Building swift-system")
104+
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + [swiftSystemURL.path()], workingDirectory: swiftSystemBuildURL)
105+
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftSystemBuildURL)
106+
print("Built swift-system")
107+
}
108+
109+
print("Building llbuild")
110+
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + ["-DLLBUILD_SUPPORT_BINDINGS:=Swift", llbuildURL.path()], workingDirectory: llbuildBuildURL)
111+
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: llbuildBuildURL)
112+
print("Built llbuild")
113+
114+
print("Building swift-argument-parser")
115+
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + ["-DBUILD_TESTING=NO", "-DBUILD_EXAMPLES=NO", swiftArgumentParserURL.path()], workingDirectory: swiftArgumentParserBuildURL)
116+
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftArgumentParserBuildURL)
117+
print("Built swift-argument-parser")
118+
119+
print("Building swift-driver")
120+
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + [swiftDriverURL.path()], workingDirectory: swiftDriverBuildURL)
121+
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftDriverBuildURL)
122+
print("Built swift-driver")
123+
124+
print("Building swift-build in \(swiftBuildBuildURL)")
125+
try await Process.checkNonZeroExit(url: cmakeURL, arguments: sharedCMakeArgs + [swiftBuildURL.path()], workingDirectory: swiftBuildBuildURL)
126+
try await Process.checkNonZeroExit(url: ninjaURL, arguments: [], workingDirectory: swiftBuildBuildURL)
127+
print("Built swift-build")
128+
}
129+
130+
func findSiblingRepository(_ name: String, swiftBuildURL: URL) throws -> URL {
131+
let url = swiftBuildURL.deletingLastPathComponent().appending(component: name)
132+
print("\(name): \(url.path())")
133+
guard FileManager.default.fileExists(atPath: url.path()) else { throw Errors.missingRepository(url.path()) }
134+
return url
135+
}
136+
}
137+
138+
enum Errors: Error {
139+
case processError(terminationReason: Process.TerminationReason, terminationStatus: Int32)
140+
case missingRequiredOption(String)
141+
case missingRepository(String)
142+
case unimplementedForHostOS
143+
}
144+
145+
enum OS {
146+
case macOS
147+
case linux
148+
case windows
149+
150+
static func host() throws -> Self {
151+
#if os(macOS)
152+
return .macOS
153+
#elseif os(Linux)
154+
return .linux
155+
#elseif os(Windows)
156+
return .windows
157+
#else
158+
throw Errors.unimplementedForHostOS
159+
#endif
160+
}
161+
}
162+
163+
extension Process {
164+
func run() async throws {
165+
try await withCheckedThrowingContinuation { continuation in
166+
terminationHandler = { _ in
167+
continuation.resume()
168+
}
169+
170+
do {
171+
try run()
172+
} catch {
173+
terminationHandler = nil
174+
continuation.resume(throwing: error)
175+
}
176+
}
177+
}
178+
179+
static func checkNonZeroExit(url: URL, arguments: [String], workingDirectory: URL, environment: [String: String]? = nil) async throws {
180+
print("\(url.path()) \(arguments.joined(separator: " "))")
181+
let process = Process()
182+
process.executableURL = url
183+
process.arguments = arguments
184+
process.currentDirectoryURL = workingDirectory
185+
process.environment = environment
186+
try await process.run()
187+
if process.terminationStatus != 0 {
188+
throw Errors.processError(terminationReason: process.terminationReason, terminationStatus: process.terminationStatus)
189+
}
190+
}
191+
}

Sources/SWBBuildService/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,4 @@ target_link_libraries(SWBBuildService PUBLIC
3636
SWBUniversalPlatform
3737
SWBWebAssemblyPlatform
3838
SWBWindowsPlatform
39-
SwiftSystem::SystemPackage)
39+
$<$<NOT:$<PLATFORM_ID:Darwin>>:SwiftSystem::SystemPackage>)

Sources/SWBCSupport/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ target_include_directories(SWBCSupport PUBLIC
2020
${CMAKE_CURRENT_SOURCE_DIR})
2121
# TODO(compnerd) wire this up with `find_package`
2222
target_link_libraries(SWBCSupport PRIVATE
23-
BlocksRuntime)
23+
$<$<NOT:$<PLATFORM_ID:Darwin>>:BlocksRuntime>)

Sources/SWBUtil/CMakeLists.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -108,5 +108,5 @@ target_link_libraries(SWBUtil PUBLIC
108108
SWBCSupport
109109
SWBLibc
110110
ArgumentParser
111-
$<$<NOT:$<PLATFORM_ID:Windows>>:Crypto::Crypto>
112-
SwiftSystem::SystemPackage)
111+
$<$<AND:$<NOT:$<PLATFORM_ID:Windows>>,$<NOT:$<PLATFORM_ID:Darwin>>>:Crypto::Crypto>
112+
$<$<NOT:$<PLATFORM_ID:Darwin>>:SwiftSystem::SystemPackage>)

0 commit comments

Comments
 (0)