Skip to content

Commit

Permalink
Add support for --arch flag
Browse files Browse the repository at this point in the history
This allows customizing the target architecture and automatically uses
the Xcode build system support for build fat binaries when more than one
arch is passed.
  • Loading branch information
ankitspd committed Jun 23, 2020
1 parent 19c244e commit 0947542
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 11 deletions.
31 changes: 31 additions & 0 deletions IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift
Expand Up @@ -44,5 +44,36 @@ final class SwiftPMTests: XCTestCase {
}
}
}

func testArchCustomization() throws {
try withTemporaryDirectory { tmpDir in
let foo = tmpDir.appending(component: "foo")
try localFileSystem.createDirectory(foo)
try sh(swiftPackage, "--package-path", foo, "init", "--type", "executable")

try localFileSystem.removeFileTree(foo.appending(RelativePath("Sources/foo/main.swift")))
try localFileSystem.writeFileContents(foo.appending(RelativePath("Sources/foo/main.m"))) {
$0 <<< "int main() {}"
}
let archs = ["x86_64", "x86_64h"]

for arch in archs {
try sh(swiftBuild, "--package-path", foo, "--arch", arch)
let fooPath = foo.appending(RelativePath(".build/\(arch)-apple-macosx/debug/foo"))
XCTAssertFileExists(fooPath)
}

let args = [swiftBuild.pathString, "--package-path", foo.pathString] + archs.flatMap{ ["--arch", $0] }
try _sh(args)

let fooPath = foo.appending(RelativePath(".build/apple/Products/Debug/foo"))
XCTAssertFileExists(fooPath)

let objectsDir = foo.appending(RelativePath(".build/apple/Intermediates.noindex/foo.build/Debug/foo.build/Objects-normal"))
for arch in archs {
XCTAssertDirectoryExists(objectsDir.appending(component: arch))
}
}
}
#endif
}
10 changes: 9 additions & 1 deletion Sources/Commands/Options.swift
Expand Up @@ -57,6 +57,9 @@ public class ToolOptions {
/// Path to the compilation destination’s toolchain.
public var customCompileToolchain: AbsolutePath?

/// The architectures to compile for.
public var archs: [String] = []

/// If should link the Swift stdlib statically.
public var shouldLinkStaticSwiftStdlib = false

Expand Down Expand Up @@ -101,7 +104,12 @@ public class ToolOptions {
public var useExplicitModuleBuild: Bool = false

/// The build system to use.
public var buildSystem: BuildSystemKind = .native
public var buildSystem: BuildSystemKind {
// Force the Xcode build system if we want to build more than one arch.
archs.count > 1 ? .xcode : _buildSystem
}

public var _buildSystem: BuildSystemKind = .native

/// Extra arguments to pass when using xcbuild.
public var xcbuildFlags: [String] = []
Expand Down
25 changes: 19 additions & 6 deletions Sources/Commands/SwiftTool.swift
Expand Up @@ -389,6 +389,12 @@ public class SwiftTool<Options: ToolOptions> {
option: parser.add(option: "--toolchain", kind: PathArgument.self),
to: { $0.customCompileToolchain = $1.path })

binder.bind(
option: parser.add(
option: "--arch", kind: [String].self, strategy: .oneByOne,
usage: "Build the package for the these architectures"),
to: { $0.archs = $1 })

// FIXME: We need to allow -vv type options for this.
binder.bind(
option: parser.add(option: "--verbose", shortName: "-v", kind: Bool.self,
Expand Down Expand Up @@ -460,7 +466,7 @@ public class SwiftTool<Options: ToolOptions> {

binder.bind(
option: parser.add(option: "--build-system", kind: BuildSystemKind.self, usage: nil),
to: { $0.buildSystem = $1 })
to: { $0._buildSystem = $1 })

// Let subclasses bind arguments.
type(of: self).defineArguments(parser: parser, binder: binder)
Expand All @@ -482,7 +488,7 @@ public class SwiftTool<Options: ToolOptions> {

// Force building with the native build system on other platforms than macOS.
#if !os(macOS)
options.buildSystem = .native
options._buildSystem = .native
#endif

let processSet = ProcessSet()
Expand Down Expand Up @@ -542,10 +548,15 @@ public class SwiftTool<Options: ToolOptions> {
if result.exists(arg: "--multiroot-data-file") {
diagnostics.emit(.unsupportedFlag("--multiroot-data-file"))
}
if result.exists(arg: "--experimental-explicit-module-build") &&

if result.exists(arg: "--experimental-explicit-module-build") &&
!result.exists(arg: "--use-integrated-swift-driver") {
diagnostics.emit(error: "'--experimental-explicit-module-build' option requires '--use-integrated-swift-driver'")
}
diagnostics.emit(error: "'--experimental-explicit-module-build' option requires '--use-integrated-swift-driver'")
}

if result.exists(arg: "--arch") && result.exists(arg: "--triple") {
diagnostics.emit(.mutuallyExclusiveArgumentsError(arguments: ["--arch", "--triple"]))
}
}

class func defineArguments(parser: ArgumentParser, binder: ArgumentBinder<Options>) {
Expand Down Expand Up @@ -793,6 +804,7 @@ public class SwiftTool<Options: ToolOptions> {
configuration: options.configuration,
toolchain: toolchain,
destinationTriple: triple,
archs: options.archs,
flags: options.buildFlags,
xcbuildFlags: options.xcbuildFlags,
jobs: options.jobs ?? UInt32(ProcessInfo.processInfo.activeProcessorCount),
Expand Down Expand Up @@ -828,14 +840,15 @@ public class SwiftTool<Options: ToolOptions> {
}
// Apply any manual overrides.
if let triple = self.options.customCompileTriple {
destination.target = triple
destination.target = triple
}
if let binDir = self.options.customCompileToolchain {
destination.binDir = binDir.appending(components: "usr", "bin")
}
if let sdk = self.options.customCompileSDK {
destination.sdk = sdk
}
destination.archs = options.archs

// Check if we ended up with the host toolchain.
if hostDestination == destination {
Expand Down
5 changes: 5 additions & 0 deletions Sources/SPMBuildCore/BuildParameters.swift
Expand Up @@ -52,6 +52,9 @@ public struct BuildParameters: Encodable {
/// Destination triple.
public var triple: Triple

/// The architectures to build for.
public var archs: [String]

/// Extra build flags.
public var flags: BuildFlags

Expand Down Expand Up @@ -128,6 +131,7 @@ public struct BuildParameters: Encodable {
toolchain: Toolchain,
hostTriple: Triple? = nil,
destinationTriple: Triple? = nil,
archs: [String] = [],
flags: BuildFlags,
xcbuildFlags: [String] = [],
toolsVersion: ToolsVersion = ToolsVersion.currentToolsVersion,
Expand All @@ -150,6 +154,7 @@ public struct BuildParameters: Encodable {
self._toolchain = _Toolchain(toolchain: toolchain)
self.hostTriple = hostTriple ?? .getHostTriple(usingSwiftCompiler: toolchain.swiftCompiler)
self.triple = destinationTriple ?? .getHostTriple(usingSwiftCompiler: toolchain.swiftCompiler)
self.archs = archs
self.flags = flags
self.xcbuildFlags = xcbuildFlags
self.toolsVersion = toolsVersion
Expand Down
3 changes: 3 additions & 0 deletions Sources/SPMBuildCore/Triple.swift
Expand Up @@ -34,12 +34,15 @@ public struct Triple: Encodable, Equatable {

public enum Arch: String, Encodable {
case x86_64
case x86_64h
case i686
case powerpc64le
case s390x
case aarch64
case armv7
case arm
case arm64
case arm64e
case wasm32
}

Expand Down
3 changes: 3 additions & 0 deletions Sources/Workspace/Destination.swift
Expand Up @@ -37,6 +37,9 @@ public struct Destination: Encodable, Equatable {
/// for more information see //https://clang.llvm.org/docs/CrossCompilation.html
public var target: Triple?

/// The architectures to build for. We build for host architecture if this is empty.
public var archs: [String] = []

/// The SDK used to compile for the destination.
public var sdk: AbsolutePath

Expand Down
16 changes: 14 additions & 2 deletions Sources/Workspace/UserToolchain.swift
Expand Up @@ -53,6 +53,9 @@ public final class UserToolchain: Toolchain {
/// The target triple that should be used for compilation.
public let triple: Triple

/// The list of archs to build for.
public let archs: [String]

/// Search paths from the PATH environment variable.
let envSearchPaths: [AbsolutePath]

Expand Down Expand Up @@ -224,9 +227,18 @@ public final class UserToolchain: Toolchain {

let swiftCompilers = try UserToolchain.determineSwiftCompilers(binDir: binDir, lookup: { UserToolchain.lookup(variable: $0, searchPaths: searchPaths) }, envSearchPaths: searchPaths)
self.swiftCompiler = swiftCompilers.compile

self.archs = destination.archs

// Use the triple from destination or compute the host triple using swiftc.
let triple = destination.target ?? Triple.getHostTriple(usingSwiftCompiler: swiftCompilers.compile)
var triple = destination.target ?? Triple.getHostTriple(usingSwiftCompiler: swiftCompilers.compile)

// Change the triple to the specified arch if there's exactly one of them.
// The Triple property is only looked at by the native build system currently.
if archs.count == 1 {
let components = triple.tripleString.drop(while: { $0 != "-" })
triple = try Triple(archs[0] + components)
}

self.triple = triple

// We require xctest to exist on macOS.
Expand Down
72 changes: 70 additions & 2 deletions Sources/XCBuildSupport/XcodeBuildSystem.swift
Expand Up @@ -8,6 +8,8 @@ See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import class Foundation.JSONEncoder

import TSCBasic
import TSCUtility
import PackageModel
Expand Down Expand Up @@ -78,7 +80,7 @@ public final class XcodeBuildSystem: BuildSystem {
let pif = try pifBuilder.generatePIF()
try localFileSystem.writeIfChanged(path: buildParameters.pifManifest, bytes: ByteString(encodingAsUTF8: pif))

let arguments = [
var arguments = [
xcbuildPath.pathString,
"build",
buildParameters.pifManifest.pathString,
Expand All @@ -88,7 +90,14 @@ public final class XcodeBuildSystem: BuildSystem {
buildParameters.dataPath.pathString,
"--target",
subset.pifTargetName
] + buildParameters.xcbuildFlags
]

let buildParamsFile = try createBuildParametersFile()
if let buildParamsFile = buildParamsFile {
arguments += ["--buildParametersFile", buildParamsFile.pathString]
}

arguments += buildParameters.xcbuildFlags

let delegate = createBuildDelegate()
let redirection: Process.OutputRedirection = .stream(stdout: delegate.parse(bytes:), stderr: { bytes in
Expand All @@ -99,11 +108,47 @@ public final class XcodeBuildSystem: BuildSystem {
try process.launch()
let result = try process.waitUntilExit()

if let buildParamsFile = buildParamsFile {
try? localFileSystem.removeFileTree(buildParamsFile)
}

guard result.exitStatus == .terminated(code: 0) else {
throw Diagnostics.fatalError
}
}

func createBuildParametersFile() throws -> AbsolutePath? {
// We only generate the build parameters file if it's required.
guard !buildParameters.archs.isEmpty else { return nil }

let runDestination = XCBBuildParameters.RunDestination(
platform: "macosx",
sdk: "macosx",
sdkVariant: nil,
targetArchitecture: "x86_64",
supportedArchitectures: [],
disableOnlyActiveArch: true
)
let params = XCBBuildParameters(
configurationName: buildParameters.configuration.xcbuildName,
overrides: .init(commandLine: .init(table: [
"ARCHS": "\(buildParameters.archs.joined(separator: " "))",
])),
activeRunDestination: runDestination
)

let encoder = JSONEncoder()
if #available(macOS 10.13, *) {
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
}

let data = try encoder.encode(params)
let file = try withTemporaryFile(deleteOnClose: false) { $0.path }
try localFileSystem.writeFileContents(file, bytes: ByteString(data))

return file
}

public func cancel() {
}

Expand Down Expand Up @@ -138,6 +183,29 @@ public final class XcodeBuildSystem: BuildSystem {
}
}

struct XCBBuildParameters: Encodable {
struct RunDestination: Encodable {
var platform: String
var sdk: String
var sdkVariant: String?
var targetArchitecture: String
var supportedArchitectures: [String]
var disableOnlyActiveArch: Bool
}

struct XCBSettingsTable: Encodable {
var table: [String: String]
}

struct SettingsOverride: Encodable {
var commandLine: XCBSettingsTable? = nil
}

var configurationName: String
var overrides: SettingsOverride
var activeRunDestination: RunDestination
}

extension BuildConfiguration {
public var xcbuildName: String {
switch self {
Expand Down

0 comments on commit 0947542

Please sign in to comment.