Skip to content

Commit 1a4e3f2

Browse files
hartbitaciidgh
authored andcommitted
Compile manifests instead of interpreting them
<rdar://problem/58028627>
1 parent f9cf27e commit 1a4e3f2

File tree

3 files changed

+70
-53
lines changed

3 files changed

+70
-53
lines changed

Sources/PackageDescription/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ foreach(PACKAGE_DESCRIPTION_VERSION 4 4_2)
2929
$<$<COMPILE_LANGUAGE:Swift>:-enable-library-evolution>)
3030
target_compile_options(PD${PACKAGE_DESCRIPTION_VERSION} PUBLIC
3131
$<$<COMPILE_LANGUAGE:Swift>:-emit-module-interface-path$<SEMICOLON>${SWIFT_INTERFACE_PATH}>)
32+
target_link_options(PD${PACKAGE_DESCRIPTION_VERSION} PRIVATE
33+
"SHELL:-Xlinker -install_name -Xlinker @rpath/libPackageDescription.dylib")
3234
endif()
3335

3436
set_target_properties(PD${PACKAGE_DESCRIPTION_VERSION} PROPERTIES

Sources/PackageLoading/ManifestLoader.swift

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -531,39 +531,34 @@ public final class ManifestLoader: ManifestLoaderProtocol {
531531

532532
// Compute the path to runtime we need to load.
533533
let runtimePath = self.runtimePath(for: toolsVersion)
534-
let interpreterFlags = self.interpreterFlags(for: toolsVersion)
534+
let compilerFlags = self.interpreterFlags(for: toolsVersion)
535535

536536
// FIXME: Workaround for the module cache bug that's been haunting Swift CI
537537
// <rdar://problem/48443680>
538538
let moduleCachePath = ProcessEnv.vars["SWIFTPM_MODULECACHE_OVERRIDE"] ?? ProcessEnv.vars["SWIFTPM_TESTS_MODULECACHE"]
539539

540-
var cmd = [String]()
541-
#if os(macOS)
542-
// If enabled, use sandbox-exec on macOS. This provides some safety against
543-
// arbitrary code execution when parsing manifest files. We only allow
544-
// the permissions which are absolutely necessary for manifest parsing.
545-
if isManifestSandboxEnabled {
546-
let cacheDirs = [
547-
cacheDir,
548-
moduleCachePath.map{ AbsolutePath($0) }
549-
].compactMap{$0}
550-
cmd += ["sandbox-exec", "-p", sandboxProfile(cacheDirs)]
551-
}
552-
#endif
540+
var cmd: [String] = []
553541
cmd += [resources.swiftCompiler.pathString]
554-
cmd += ["--driver-mode=swift"]
555542
cmd += verbosity.ccArgs
556543

557544
// If we got the binDir that means we could be developing SwiftPM in Xcode
558545
// which produces a framework for dynamic package products.
559-
let runtimeFrameworkPath = runtimePath.appending(component: "PackageFrameworks")
560-
if resources.binDir != nil, localFileSystem.exists(runtimeFrameworkPath) {
561-
cmd += ["-F", runtimeFrameworkPath.pathString, "-framework", "PackageDescription"]
546+
let packageFrameworkPath = runtimePath.appending(component: "PackageFrameworks")
547+
if resources.binDir != nil, localFileSystem.exists(packageFrameworkPath) {
548+
cmd += [
549+
"-F", packageFrameworkPath.pathString,
550+
"-framework", "PackageDescription",
551+
"-Xlinker", "-rpath", "-Xlinker", packageFrameworkPath.pathString,
552+
]
562553
} else {
563-
cmd += ["-L", runtimePath.pathString, "-lPackageDescription"]
554+
cmd += [
555+
"-L", runtimePath.pathString,
556+
"-lPackageDescription",
557+
"-Xlinker", "-rpath", "-Xlinker", runtimePath.pathString
558+
]
564559
}
565560

566-
cmd += interpreterFlags
561+
cmd += compilerFlags
567562
if let moduleCachePath = moduleCachePath {
568563
cmd += ["-module-cache-path", moduleCachePath]
569564
}
@@ -579,36 +574,50 @@ public final class ManifestLoader: ManifestLoaderProtocol {
579574

580575
cmd += [manifestPath.pathString]
581576

582-
// Create and open a temporary file to write json to.
583577
try withTemporaryFile { file in
578+
// Set path to compiled manifest executable.
579+
cmd += ["-o", file.path.pathString]
580+
581+
try Process.popen(arguments: cmd)
582+
583+
// Compile the manifest.
584+
let compilerResult = try Process.popen(arguments: cmd)
585+
let compilerOutput = try (compilerResult.utf8Output() + compilerResult.utf8stderrOutput()).spm_chuzzle()
586+
manifestParseResult.compilerOutput = compilerOutput
587+
588+
// Return now if there was an error.
589+
if compilerResult.exitStatus != .terminated(code: 0) {
590+
return
591+
}
592+
584593
// Pass the fd in arguments.
585-
cmd += ["-fileno", "\(file.fileHandle.fileDescriptor)"]
586-
587-
// Prefer swiftinterface if both swiftmodule and swiftinterface files are present.
588-
//
589-
// This will avoid failures during incremental builds when the
590-
// slate swiftmodule file is still present from the previous
591-
// install. We should be able to remove this after some
592-
// transition period.
593-
var env = ProcessEnv.vars
594+
cmd = [file.path.pathString, "-fileno", "1"]
595+
594596
#if os(macOS)
595-
env["SWIFT_FORCE_MODULE_LOADING"] = "prefer-parseable"
597+
// If enabled, use sandbox-exec on macOS. This provides some safety against
598+
// arbitrary code execution when parsing manifest files. We only allow
599+
// the permissions which are absolutely necessary for manifest parsing.
600+
if isManifestSandboxEnabled {
601+
let cacheDirectories = [
602+
cacheDir,
603+
moduleCachePath.map({ AbsolutePath($0) })
604+
].compactMap({ $0 })
605+
let profile = sandboxProfile(toolsVersion: toolsVersion, cacheDirectories: cacheDirectories)
606+
cmd += ["sandbox-exec", "-p", profile]
607+
}
596608
#endif
597609

598610
// Run the command.
599-
let result = try Process.popen(arguments: cmd, environment: env)
600-
let output = try (result.utf8Output() + result.utf8stderrOutput()).spm_chuzzle()
601-
manifestParseResult.compilerOutput = output
611+
let runResult = try Process.popen(arguments: cmd)
612+
let runOutput = try (runResult.utf8Output() + runResult.utf8stderrOutput()).spm_chuzzle()
602613

603614
// Return now if there was an error.
604-
if result.exitStatus != .terminated(code: 0) {
615+
if runResult.exitStatus != .terminated(code: 0) {
616+
manifestParseResult.errorOutput = runOutput
605617
return
606618
}
607619

608-
guard let json = try localFileSystem.readFileContents(file.path).validDescription else {
609-
throw StringError("the manifest has invalid encoding")
610-
}
611-
manifestParseResult.parsedManifest = json
620+
manifestParseResult.parsedManifest = runOutput
612621
}
613622
}
614623

@@ -670,10 +679,10 @@ public final class ManifestLoader: ManifestLoaderProtocol {
670679
cmd += ["-I", runtimePath.pathString]
671680
#if os(macOS)
672681
cmd += ["-target", "x86_64-apple-macosx10.10"]
673-
#endif
674682
if let sdkRoot = resources.sdkRoot ?? self.sdkRoot() {
675683
cmd += ["-sdk", sdkRoot.pathString]
676684
}
685+
#endif
677686
cmd += ["-package-description-version", toolsVersion.description]
678687
return cmd
679688
}
@@ -705,26 +714,31 @@ public final class ManifestLoader: ManifestLoaderProtocol {
705714
}
706715

707716
/// Returns the sandbox profile to be used when parsing manifest on macOS.
708-
private func sandboxProfile(_ cacheDirs: [AbsolutePath] = []) -> String {
717+
private func sandboxProfile(toolsVersion: ToolsVersion, cacheDirectories: [AbsolutePath] = []) -> String {
709718
let stream = BufferedOutputByteStream()
710719
stream <<< "(version 1)" <<< "\n"
711720
// Deny everything by default.
712721
stream <<< "(deny default)" <<< "\n"
713722
// Import the system sandbox profile.
714723
stream <<< "(import \"system.sb\")" <<< "\n"
715-
// Allow reading all files.
716-
stream <<< "(allow file-read*)" <<< "\n"
717-
// These are required by the Swift compiler.
718-
stream <<< "(allow process*)" <<< "\n"
719-
stream <<< "(allow sysctl*)" <<< "\n"
720-
// Allow writing in temporary locations.
721-
stream <<< "(allow file-write*" <<< "\n"
722-
for directory in Platform.darwinCacheDirectories() {
723-
stream <<< " (regex #\"^\(directory.pathString)/org\\.llvm\\.clang.*\")" <<< "\n"
724-
}
725-
for cacheDir in cacheDirs {
726-
stream <<< " (subpath \"\(cacheDir.pathString)\")" <<< "\n"
724+
725+
// The following accesses are only needed when interpreting the manifest (versus running a compiled version).
726+
if toolsVersion < .vNext {
727+
// Allow reading all files.
728+
stream <<< "(allow file-read*)" <<< "\n"
729+
// These are required by the Swift compiler.
730+
stream <<< "(allow process*)" <<< "\n"
731+
stream <<< "(allow sysctl*)" <<< "\n"
732+
// Allow writing in temporary locations.
733+
stream <<< "(allow file-write*" <<< "\n"
734+
for directory in Platform.darwinCacheDirectories() {
735+
stream <<< " (regex #\"^\(directory.pathString)/org\\.llvm\\.clang.*\")" <<< "\n"
736+
}
737+
for directory in cacheDirectories {
738+
stream <<< " (subpath \"\(directory.pathString)\")" <<< "\n"
739+
}
727740
}
741+
728742
stream <<< ")" <<< "\n"
729743
return stream.bytes.description
730744
}

Sources/PackageModel/ToolsVersion.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public struct ToolsVersion: CustomStringConvertible, Comparable, Hashable, Codab
2121
public static let v4_2 = ToolsVersion(version: "4.2.0")
2222
public static let v5 = ToolsVersion(version: "5.0.0")
2323
public static let v5_2 = ToolsVersion(version: "5.2.0")
24+
public static let vNext = ToolsVersion(version: "999.0.0")
2425

2526
/// The current tools version in use.
2627
public static let currentToolsVersion = ToolsVersion(string:

0 commit comments

Comments
 (0)