Skip to content

Commit 443fa2d

Browse files
authored
Add sandboxing to build tool plugins in swiftbuild build system (#8747)
* Add disable-able sandboxing to the custom tasks in the PIF with the declared input and output files. * Add more test coverage of plugin scenarios * Capture child diagnostics from Swift Build and emit those through equivalent observability scope in SwiftPM
1 parent fa5fcdb commit 443fa2d

File tree

5 files changed

+143
-65
lines changed

5 files changed

+143
-65
lines changed

Sources/CoreCommands/BuildSystemSupport.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ private struct SwiftBuildSystemFactory: BuildSystemFactory {
125125
},
126126
packageManagerResourcesDirectory: swiftCommandState.packageManagerResourcesDirectory,
127127
additionalFileRules: FileRuleDescription.swiftpmFileTypes + FileRuleDescription.xcbuildFileTypes,
128-
pkgConfigDirectories: self.swiftCommandState.options.locations.pkgConfigDirectories,
129128
outputStream: outputStream ?? self.swiftCommandState.outputStream,
130129
logLevel: logLevel ?? self.swiftCommandState.logLevel,
131130
fileSystem: self.swiftCommandState.fileSystem,

Sources/SwiftBuildSupport/PIFBuilder.swift

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import var TSCBasic.stdoutStream
2525

2626
import enum SwiftBuild.ProjectModel
2727

28-
public func memoize<T>(to cache: inout T?, build: () async throws -> T) async rethrows -> T {
28+
fileprivate func memoize<T>(to cache: inout T?, build: () async throws -> T) async rethrows -> T {
2929
if let value = cache {
3030
return value
3131
} else {
@@ -36,7 +36,7 @@ public func memoize<T>(to cache: inout T?, build: () async throws -> T) async re
3636
}
3737

3838
extension ModulesGraph {
39-
public static func computePluginGeneratedFiles(
39+
fileprivate static func computePluginGeneratedFiles(
4040
target: ResolvedModule,
4141
toolsVersion: ToolsVersion,
4242
additionalFileRules: [FileRuleDescription],
@@ -73,7 +73,7 @@ extension ModulesGraph {
7373
observabilityScope: observabilityScope
7474
)
7575
let pluginDerivedResources = derivedResources
76-
derivedSources.forEach { absPath in
76+
for absPath in derivedSources {
7777
let relPath = absPath.relative(to: pluginDerivedSources.root)
7878
pluginDerivedSources.relativePaths.append(relPath)
7979
}
@@ -106,6 +106,18 @@ struct PIFBuilderParameters {
106106

107107
/// The Swift language versions supported by the SwiftBuild being used for the build.
108108
let supportedSwiftVersions: [SwiftLanguageVersion]
109+
110+
/// The plugin script runner that will compile and run plugins.
111+
let pluginScriptRunner: PluginScriptRunner
112+
113+
/// Disable the sandbox for the custom tasks
114+
let disableSandbox: Bool
115+
116+
/// The working directory where the plugins should produce their results
117+
let pluginWorkingDirectory: AbsolutePath
118+
119+
/// Additional rules for including a source or resource file in a target
120+
let additionalFileRules: [FileRuleDescription]
109121
}
110122

111123
/// PIF object builder for a package graph.
@@ -128,14 +140,6 @@ public final class PIFBuilder {
128140
/// The file system to read from.
129141
let fileSystem: FileSystem
130142

131-
let pluginScriptRunner: PluginScriptRunner
132-
133-
let pluginWorkingDirectory: AbsolutePath
134-
135-
let pkgConfigDirectories: [Basics.AbsolutePath]
136-
137-
let additionalFileRules: [FileRuleDescription]
138-
139143
/// Creates a `PIFBuilder` instance.
140144
/// - Parameters:
141145
/// - graph: The package graph to build from.
@@ -147,19 +151,11 @@ public final class PIFBuilder {
147151
parameters: PIFBuilderParameters,
148152
fileSystem: FileSystem,
149153
observabilityScope: ObservabilityScope,
150-
pluginScriptRunner: PluginScriptRunner,
151-
pluginWorkingDirectory: AbsolutePath,
152-
pkgConfigDirectories: [Basics.AbsolutePath],
153-
additionalFileRules: [FileRuleDescription]
154154
) {
155155
self.graph = graph
156156
self.parameters = parameters
157157
self.fileSystem = fileSystem
158158
self.observabilityScope = observabilityScope.makeChildScope(description: "PIF Builder")
159-
self.pluginScriptRunner = pluginScriptRunner
160-
self.pluginWorkingDirectory = pluginWorkingDirectory
161-
self.pkgConfigDirectories = pkgConfigDirectories
162-
self.additionalFileRules = additionalFileRules
163159
}
164160

165161
/// Generates the PIF representation.
@@ -232,8 +228,8 @@ public final class PIFBuilder {
232228

233229
/// Constructs a `PIF.TopLevelObject` representing the package graph.
234230
private func constructPIF(buildParameters: BuildParameters) async throws -> PIF.TopLevelObject {
235-
let pluginScriptRunner = self.pluginScriptRunner
236-
let outputDir = self.pluginWorkingDirectory.appending("outputs")
231+
let pluginScriptRunner = self.parameters.pluginScriptRunner
232+
let outputDir = self.parameters.pluginWorkingDirectory.appending("outputs")
237233

238234
let pluginsPerModule = graph.pluginsPerModule(
239235
satisfying: buildParameters.buildEnvironment // .buildEnvironment(for: .host)
@@ -261,7 +257,7 @@ public final class PIFBuilder {
261257
var packagesAndProjects: [(ResolvedPackage, ProjectModel.Project)] = []
262258

263259
for package in sortedPackages {
264-
var buildtoolPluginResults: [String: PackagePIFBuilder.BuildToolPluginInvocationResult] = [:]
260+
var buildToolPluginResultsByTargetName: [String: PackagePIFBuilder.BuildToolPluginInvocationResult] = [:]
265261

266262
for module in package.modules {
267263
// Apply each build tool plugin used by the target in order,
@@ -308,7 +304,7 @@ public final class PIFBuilder {
308304
(pluginDerivedSources, pluginDerivedResources) = try ModulesGraph.computePluginGeneratedFiles(
309305
target: module,
310306
toolsVersion: package.manifest.toolsVersion,
311-
additionalFileRules: self.additionalFileRules,
307+
additionalFileRules: self.parameters.additionalFileRules,
312308
buildParameters: buildParameters,
313309
buildToolPluginInvocationResults: buildToolPluginResults,
314310
prebuildCommandResults: [],
@@ -336,7 +332,7 @@ public final class PIFBuilder {
336332
writableDirectories: writableDirectories,
337333
readOnlyDirectories: readOnlyDirectories,
338334
allowNetworkConnections: [],
339-
pkgConfigDirectories: self.pkgConfigDirectories,
335+
pkgConfigDirectories: self.parameters.pkgConfigDirectories,
340336
sdkRootPath: buildParameters.toolchain.sdkRootPath,
341337
fileSystem: fileSystem,
342338
modulesGraph: self.graph,
@@ -366,22 +362,33 @@ public final class PIFBuilder {
366362
env[key.rawValue] = value
367363
}
368364

365+
let workingDir = buildCommand.configuration.workingDirectory
366+
367+
let writableDirectories: [AbsolutePath] = buildCommand.outputFiles
368+
369369
return PackagePIFBuilder.CustomBuildCommand(
370370
displayName: buildCommand.configuration.displayName,
371371
executable: buildCommand.configuration.executable.pathString,
372372
arguments: buildCommand.configuration.arguments,
373373
environment: env,
374-
workingDir: buildCommand.configuration.workingDirectory,
374+
workingDir: workingDir,
375375
inputPaths: buildCommand.inputFiles,
376376
outputPaths: buildCommand.outputFiles.map(\.pathString),
377-
sandboxProfile: nil
377+
sandboxProfile:
378+
self.parameters.disableSandbox ?
379+
nil :
380+
.init(
381+
strictness: .default,
382+
writableDirectories: writableDirectories,
383+
readOnlyDirectories: buildCommand.inputFiles
384+
)
378385
)
379386
} )
380387
)
381388

382389
// Add a BuildToolPluginInvocationResult to the mapping.
383390
buildToolPluginResults.append(result2)
384-
buildtoolPluginResults[module.name] = result2
391+
buildToolPluginResultsByTargetName[module.name] = result2
385392
}
386393
}
387394

@@ -393,7 +400,7 @@ public final class PIFBuilder {
393400
resolvedPackage: package,
394401
packageManifest: package.manifest,
395402
delegate: packagePIFBuilderDelegate,
396-
buildToolPluginResultsByTargetName: buildtoolPluginResults,
403+
buildToolPluginResultsByTargetName: buildToolPluginResultsByTargetName,
397404
createDylibForDynamicProducts: self.parameters.shouldCreateDylibForDynamicProducts,
398405
packageDisplayVersion: package.manifest.displayName,
399406
fileSystem: self.fileSystem,
@@ -433,20 +440,24 @@ public final class PIFBuilder {
433440
observabilityScope: ObservabilityScope,
434441
preservePIFModelStructure: Bool,
435442
pluginScriptRunner: PluginScriptRunner,
443+
disableSandbox: Bool,
436444
pluginWorkingDirectory: AbsolutePath,
437445
pkgConfigDirectories: [Basics.AbsolutePath],
438446
additionalFileRules: [FileRuleDescription]
439447
) async throws -> String {
440-
let parameters = PIFBuilderParameters(buildParameters, supportedSwiftVersions: [])
448+
let parameters = PIFBuilderParameters(
449+
buildParameters,
450+
supportedSwiftVersions: [],
451+
pluginScriptRunner: pluginScriptRunner,
452+
disableSandbox: disableSandbox,
453+
pluginWorkingDirectory: pluginWorkingDirectory,
454+
additionalFileRules: additionalFileRules,
455+
)
441456
let builder = Self(
442457
graph: packageGraph,
443458
parameters: parameters,
444459
fileSystem: fileSystem,
445-
observabilityScope: observabilityScope,
446-
pluginScriptRunner: pluginScriptRunner,
447-
pluginWorkingDirectory: pluginWorkingDirectory,
448-
pkgConfigDirectories: pkgConfigDirectories,
449-
additionalFileRules: additionalFileRules
460+
observabilityScope: observabilityScope
450461
)
451462
return try await builder.generatePIF(preservePIFModelStructure: preservePIFModelStructure, buildParameters: buildParameters)
452463
}
@@ -696,7 +707,14 @@ extension PIFGenerationError: CustomStringConvertible {
696707
// MARK: - Helpers
697708

698709
extension PIFBuilderParameters {
699-
init(_ buildParameters: BuildParameters, supportedSwiftVersions: [SwiftLanguageVersion]) {
710+
init(
711+
_ buildParameters: BuildParameters,
712+
supportedSwiftVersions: [SwiftLanguageVersion],
713+
pluginScriptRunner: PluginScriptRunner,
714+
disableSandbox: Bool,
715+
pluginWorkingDirectory: AbsolutePath,
716+
additionalFileRules: [FileRuleDescription]
717+
) {
700718
self.init(
701719
triple: buildParameters.triple,
702720
isPackageAccessModifierSupported: buildParameters.driverParameters.isPackageAccessModifierSupported,
@@ -705,7 +723,11 @@ extension PIFBuilderParameters {
705723
toolchainLibDir: (try? buildParameters.toolchain.toolchainLibDir) ?? .root,
706724
pkgConfigDirectories: buildParameters.pkgConfigDirectories,
707725
sdkRootPath: buildParameters.toolchain.sdkRootPath,
708-
supportedSwiftVersions: supportedSwiftVersions
726+
supportedSwiftVersions: supportedSwiftVersions,
727+
pluginScriptRunner: pluginScriptRunner,
728+
disableSandbox: disableSandbox,
729+
pluginWorkingDirectory: pluginWorkingDirectory,
730+
additionalFileRules: additionalFileRules,
709731
)
710732
}
711733
}

Sources/SwiftBuildSupport/SwiftBuildSystem.swift

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,11 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
199199
/// The delegate used by the build system.
200200
public weak var delegate: SPMBuildCore.BuildSystemDelegate?
201201

202+
/// Configuration for building and invoking plugins.
202203
private let pluginConfiguration: PluginConfiguration
203204

205+
/// Additional rules for different file types generated from plugins.
204206
private let additionalFileRules: [FileRuleDescription]
205-
private let pkgConfigDirectories: [Basics.AbsolutePath]
206207

207208
public var builtTestProducts: [BuiltTestProduct] {
208209
get async {
@@ -244,7 +245,6 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
244245
packageGraphLoader: @escaping () async throws -> ModulesGraph,
245246
packageManagerResourcesDirectory: Basics.AbsolutePath?,
246247
additionalFileRules: [FileRuleDescription],
247-
pkgConfigDirectories: [Basics.AbsolutePath],
248248
outputStream: OutputByteStream,
249249
logLevel: Basics.Diagnostic.Severity,
250250
fileSystem: FileSystem,
@@ -255,7 +255,6 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
255255
self.packageGraphLoader = packageGraphLoader
256256
self.packageManagerResourcesDirectory = packageManagerResourcesDirectory
257257
self.additionalFileRules = additionalFileRules
258-
self.pkgConfigDirectories = pkgConfigDirectories
259258
self.outputStream = outputStream
260259
self.logLevel = logLevel
261260
self.fileSystem = fileSystem
@@ -405,23 +404,31 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
405404
}
406405
progressAnimation.update(step: step, total: 100, text: message)
407406
case .diagnostic(let info):
408-
let fixItsDescription = if info.fixIts.hasContent {
409-
": " + info.fixIts.map { String(describing: $0) }.joined(separator: ", ")
410-
} else {
411-
""
412-
}
413-
let message = if let locationDescription = info.location.userDescription {
414-
"\(locationDescription) \(info.message)\(fixItsDescription)"
415-
} else {
416-
"\(info.message)\(fixItsDescription)"
407+
func emitInfoAsDiagnostic(info: SwiftBuildMessage.DiagnosticInfo) {
408+
let fixItsDescription = if info.fixIts.hasContent {
409+
": " + info.fixIts.map { String(describing: $0) }.joined(separator: ", ")
410+
} else {
411+
""
412+
}
413+
let message = if let locationDescription = info.location.userDescription {
414+
"\(locationDescription) \(info.message)\(fixItsDescription)"
415+
} else {
416+
"\(info.message)\(fixItsDescription)"
417+
}
418+
let severity: Diagnostic.Severity = switch info.kind {
419+
case .error: .error
420+
case .warning: .warning
421+
case .note: .info
422+
case .remark: .debug
423+
}
424+
self.observabilityScope.emit(severity: severity, message: message)
425+
426+
for childDiagnostic in info.childDiagnostics {
427+
emitInfoAsDiagnostic(info: childDiagnostic)
428+
}
417429
}
418-
let severity: Diagnostic.Severity = switch info.kind {
419-
case .error: .error
420-
case .warning: .warning
421-
case .note: .info
422-
case .remark: .debug
423-
}
424-
self.observabilityScope.emit(severity: severity, message: message)
430+
431+
emitInfoAsDiagnostic(info: info)
425432
case .output(let info):
426433
self.observabilityScope.emit(info: "\(String(decoding: info.data, as: UTF8.self))")
427434
case .taskStarted(let info):
@@ -604,13 +611,16 @@ public final class SwiftBuildSystem: SPMBuildCore.BuildSystem {
604611
let graph = try await getPackageGraph()
605612
let pifBuilder = try PIFBuilder(
606613
graph: graph,
607-
parameters: .init(buildParameters, supportedSwiftVersions: supportedSwiftVersions()),
614+
parameters: .init(
615+
buildParameters,
616+
supportedSwiftVersions: supportedSwiftVersions(),
617+
pluginScriptRunner: self.pluginConfiguration.scriptRunner,
618+
disableSandbox: self.pluginConfiguration.disableSandbox,
619+
pluginWorkingDirectory: self.pluginConfiguration.workDirectory,
620+
additionalFileRules: additionalFileRules
621+
),
608622
fileSystem: self.fileSystem,
609-
observabilityScope: self.observabilityScope,
610-
pluginScriptRunner: self.pluginConfiguration.scriptRunner,
611-
pluginWorkingDirectory: self.pluginConfiguration.workDirectory,
612-
pkgConfigDirectories: pkgConfigDirectories,
613-
additionalFileRules: additionalFileRules
623+
observabilityScope: self.observabilityScope
614624
)
615625
return pifBuilder
616626
}

Sources/swift-bootstrap/main.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,6 @@ struct SwiftBootstrapBuildTool: AsyncParsableCommand {
372372
packageGraphLoader: asyncUnsafePackageGraphLoader,
373373
packageManagerResourcesDirectory: nil,
374374
additionalFileRules: [],
375-
pkgConfigDirectories: [],
376375
outputStream: TSCBasic.stdoutStream,
377376
logLevel: logLevel,
378377
fileSystem: self.fileSystem,

0 commit comments

Comments
 (0)