Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ let package = Package(
.package(url: "https://github.com/apple/swift-nio", from: "2.77.0"),

.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.0.0"),
.package(url: "https://github.com/swiftlang/swift-subprocess", .upToNextMinor(from: "0.4.0")),

.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.23.0"),
.package(url: "https://github.com/swift-server/swift-openapi-async-http-client", from: "1.0.0"),
Expand Down Expand Up @@ -92,7 +93,16 @@ let package = Package(
exclude: ["openapi-generator-config.yaml", "patch.js"]
),
// common utilities shared across xtool targets
.target(name: "XUtils"),
.target(
name: "XUtils",
dependencies: [
.product(
name: "Subprocess",
package: "swift-subprocess",
condition: .when(platforms: [.linux, .macOS])
),
]
),
.target(
name: "XKit",
dependencies: [
Expand Down
48 changes: 27 additions & 21 deletions Sources/PackLib/BuildSettings.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import Foundation
import Subprocess
import XUtils

public struct BuildSettings: Sendable {
private static let customBinDir =
// this is the same option used by SwiftPM itself for dev builds
ProcessInfo.processInfo.environment["SWIFTPM_CUSTOM_BIN_DIR"].map { URL(fileURLWithPath: $0) }
ProcessInfo.processInfo.environment["SWIFTPM_CUSTOM_BIN_DIR"].map { FilePath($0) }

private static let envURL = URL(fileURLWithPath: "/usr/bin/env")

Expand Down Expand Up @@ -34,31 +36,28 @@ public struct BuildSettings: Sendable {

#if os(macOS)
private static func xcrun(_ arguments: [String]) async throws -> String {
let xcrun = Process()
let pipe = Pipe()
xcrun.executableURL = URL(fileURLWithPath: "/usr/bin/xcrun")
xcrun.arguments = arguments
xcrun.standardOutput = pipe
try await xcrun.runUntilExit()
return String(decoding: pipe.fileHandleForReading.readDataToEndOfFile(), as: UTF8.self)
.trimmingCharacters(in: .whitespacesAndNewlines)
let result = try await Subprocess.run(
.path("/usr/bin/xcrun"),
arguments: .init(arguments),
output: .string(limit: .max)
).checkSuccess()
return result.standardOutput?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
}

private static let swiftURL = Task {
try await URL(fileURLWithPath: xcrun(["-f", "swift"]))
try await FilePath(xcrun(["-f", "swift"]))
}
#endif

public func swiftPMInvocation(
forTool tool: String,
arguments: [String],
packagePathOverride: String? = nil
) async throws -> Process {
let process = Process()

) async throws -> Subprocess.Configuration {
let executable: Executable
let baseArguments: [String]
if let customBinDir = Self.customBinDir {
process.executableURL = customBinDir.appendingPathComponent("swift-\(tool)")
executable = .path(customBinDir.appending("swift-\(tool)"))
baseArguments = []
} else {
#if os(macOS)
Expand All @@ -67,9 +66,9 @@ public struct BuildSettings: Sendable {
// 1) invoking the real swift executable (located with `xcrun -f`) and
// 2) explicitly removing SDKROOT from the env, as it may be inherited
// through the `swift run pack` invocation.
process.executableURL = try await Self.swiftURL.value
executable = .path(try await Self.swiftURL.value)
#else
process.executableURL = try await ToolRegistry.locate("swift")
executable = .name("swift")
#endif
baseArguments = [tool]
}
Expand All @@ -79,11 +78,18 @@ public struct BuildSettings: Sendable {
"--configuration", configuration.rawValue,
]

var env = ProcessInfo.processInfo.environment
env.removeValue(forKey: "SDKROOT")
process.environment = env
process.arguments = baseArguments + extraArguments + sdkOptions + options + arguments
return process
var rawEnv = ProcessInfo.processInfo.environment
rawEnv.removeValue(forKey: "SDKROOT")
let env = Dictionary(uniqueKeysWithValues: rawEnv.map {
(Environment.Key(rawValue: $0)!, $1)
})

return Configuration(
executable,
arguments: .init(baseArguments + extraArguments + sdkOptions + options + arguments),
environment: .custom(env),
platformOptions: .withGracefulShutDown,
)
}
}

Expand Down
10 changes: 7 additions & 3 deletions Sources/PackLib/Packer.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import XUtils
import Subprocess

public struct Packer: Sendable {
public let buildSettings: BuildSettings
Expand Down Expand Up @@ -54,7 +55,7 @@ public struct Packer: Sendable {
try Data().write(to: sources.appendingPathComponent("stub.c", isDirectory: false))
}

let builder = try await buildSettings.swiftPMInvocation(
let buildConfig = try await buildSettings.swiftPMInvocation(
forTool: "build",
arguments: [
"--package-path", packageDir.path,
Expand All @@ -68,8 +69,11 @@ public struct Packer: Sendable {
"--disable-automatic-resolution",
]
)
builder.standardOutput = FileHandle.standardError
try await builder.runUntilExit()
try await Subprocess.run(
buildConfig,
output: .standardError
)
.checkSuccess()
}

public func pack() async throws -> URL {
Expand Down
14 changes: 8 additions & 6 deletions Sources/PackLib/Planner.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import XUtils
import Subprocess

public struct Planner: Sendable {
public var buildSettings: BuildSettings
Expand Down Expand Up @@ -243,16 +244,17 @@ public struct Planner: Sendable {
}

private func _dumpAction(arguments: [String], path: String) async throws -> Data {
let dump = try await buildSettings.swiftPMInvocation(
let dumpConfig = try await buildSettings.swiftPMInvocation(
forTool: "package",
arguments: arguments,
packagePathOverride: path
)
let pipe = Pipe()
dump.standardOutput = pipe
async let task = Data(reading: pipe.fileHandleForReading)
try await dump.runUntilExit()
return try await task
return try await Subprocess.run(
dumpConfig,
output: .data(limit: .max)
)
.checkSuccess()
.standardOutput
}

private func selectLibrary(
Expand Down
63 changes: 0 additions & 63 deletions Sources/PackLib/Process+Helpers.swift

This file was deleted.

24 changes: 5 additions & 19 deletions Sources/PackLib/ToolRegistry.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Foundation
import Subprocess

package enum ToolRegistry {
package enum Errors: Error, CustomStringConvertible {
Expand All @@ -16,11 +17,6 @@ package enum ToolRegistry {

/// Obtain the full path to a tool in the user's `PATH`.
///
/// This effectively invokes `/bin/sh -c "command -v '$tool'"`.
///
/// - Warning: Make sure you trust/sanitize the `tool` parameter. If it
/// contains a single quote, it can be used in a shell escape.
///
/// - Throws: `Errors.toolNotFound` if the tool could not be located.
package static func locate(_ tool: String) async throws -> URL {
try await cache.locate(tool: tool)
Expand All @@ -30,20 +26,10 @@ package enum ToolRegistry {
private var cache: [String: Task<URL, Error>] = [:]

private func _locate(tool: String) async throws -> URL {
let pipe = Pipe()
let proc = Process()
proc.executableURL = URL(fileURLWithPath: "/bin/sh")
proc.arguments = ["-c", "command -v '\(tool)'"]
proc.standardOutput = pipe
async let bytes = pipe.fileHandleForReading.readToEnd()
do {
try await proc.runUntilExit()
} catch is Process.Failure {
throw Errors.toolNotFound(tool)
}
let path = String(decoding: try await bytes ?? Data(), as: UTF8.self)
.trimmingCharacters(in: .whitespacesAndNewlines)
return URL(fileURLWithPath: path)
guard let path = try? Executable.name(tool).resolveExecutablePath(in: .inherit),
let url = URL(filePath: path)
else { throw Errors.toolNotFound(tool) }
return url
}

func locate(tool: String) async throws -> URL {
Expand Down
23 changes: 12 additions & 11 deletions Sources/XKit/GrandSlam/Anisette/XADIProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import Foundation
import XADI
import Dependencies
import Subprocess

public actor XADIProvider: RawADIProvider {
@MainActor private static var loadTask: Task<Void, Error>?
Expand Down Expand Up @@ -45,17 +46,17 @@ public actor XADIProvider: RawADIProvider {
let archDir = "lib/\(arch)"

// TODO: Use ZIPFoundation
let proc = Process()
proc.executableURL = URL(filePath: "/usr/bin/env")
proc.arguments = [
"unzip", "-q",
applemusic.path(),
"\(archDir)/libCoreADI.so",
"\(archDir)/libstoreservicescore.so",
"-d", tmp.path()
]
try proc.run()
proc.waitUntilExit()
try await Subprocess.run(
.name("unzip"),
arguments: [
"-q",
applemusic.path,
"\(archDir)/libCoreADI.so",
"\(archDir)/libstoreservicescore.so",
"-d", tmp.path
],
output: .discarded
).checkSuccess()
try FileManager.default.moveItem(
at: tmp.appending(path: archDir),
to: libDir
Expand Down
2 changes: 1 addition & 1 deletion Sources/XToolSupport/Console.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation
import XKit
import SystemPackage
import XUtils
import NIOPosix
import NIOCore

Expand Down
12 changes: 8 additions & 4 deletions Sources/XToolSupport/DevCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -282,17 +282,21 @@ struct DevCommand: AsyncParsableCommand {
extension BuildConfiguration: ExpressibleByArgument {}

#if os(macOS)
import Subprocess

struct SimInstallOperation {
var path: URL

// TODO: allow customizing this
var simulator = "booted"

func run() async throws {
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/xcrun")
process.arguments = ["simctl", "install", simulator, path.path]
try await process.runUntilExit()
try await Subprocess.run(
.path("/usr/bin/xcrun"),
arguments: ["simctl", "install", simulator, path.path],
output: .discarded
)
.checkSuccess()
print("Installed to simulator")
}
}
Expand Down
Loading
Loading