diff --git a/Package.resolved b/Package.resolved index ac259d52..2fd123a0 100644 --- a/Package.resolved +++ b/Package.resolved @@ -15,7 +15,7 @@ "repositoryURL": "https://github.com/apple/swift-llbuild.git", "state": { "branch": "master", - "revision": "40c302a0b2ac12e99d1355986e4f16590d1fb03e", + "revision": "0615bdec228d6ed5be4ffa9ae00de0c5e0cb937c", "version": null } }, @@ -24,7 +24,7 @@ "repositoryURL": "https://github.com/apple/swift-package-manager.git", "state": { "branch": "master", - "revision": "70829165806e77c2d0b72aa172cc6e1af7fa94e4", + "revision": "65900fb1355c6f3b8ee57dd43ddb57d5cc66da30", "version": null } } diff --git a/Package.swift b/Package.swift index d5a0be77..7835f73e 100644 --- a/Package.swift +++ b/Package.swift @@ -38,6 +38,9 @@ let package = Package( .target( name: "SKSwiftPMWorkspace", dependencies: ["SwiftPM", "SKCore"]), + .testTarget( + name: "SKSwiftPMWorkspaceTests", + dependencies: ["SKSwiftPMWorkspace", "SKTestSupport"]), // Csourcekitd: C modules wrapper for sourcekitd. .target( diff --git a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift index 844adca6..d9281a66 100644 --- a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift +++ b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift @@ -74,7 +74,6 @@ public final class SwiftPMWorkspace { // FIXME: duplicating code from UserToolchain setup in swiftpm. var sdkpath: AbsolutePath? = nil var platformPath: AbsolutePath? = nil - let target: Triple = Triple.hostTriple if case .darwin? = Platform.currentPlatform { if let path = try? Process.checkNonZeroExit(args: "/usr/bin/xcrun", "--show-sdk-path", "--sdk", "macosx") { sdkpath = try? AbsolutePath(validating: path.spm_chomp()) @@ -84,8 +83,8 @@ public final class SwiftPMWorkspace { } } - var extraSwiftFlags = ["-target", target.tripleString] - var extraClangFlags = ["-arch", target.arch.rawValue] + var extraSwiftFlags: [String] = [] + var extraClangFlags: [String] = [] if let sdkpath = sdkpath { extraSwiftFlags += [ "-sdk", sdkpath.asString @@ -344,16 +343,23 @@ extension ToolchainRegistry { /// A toolchain appropriate for using to load swiftpm manifests. fileprivate var swiftpmHost: SwiftPMToolchain? { - guard let base = self.default, base.swiftc != nil else { + var swiftc: AbsolutePath? = self.default?.swiftc + var clang: AbsolutePath? = self.default?.clang + if swiftc == nil { + swiftc = toolchains.first(where: { $0.swiftc != nil })?.swiftc + } + if clang == nil { + clang = toolchains.first(where: { $0.clang != nil })?.clang + } + + if swiftc == nil || clang == nil { return nil } - guard let clang = base.clang ?? toolchains.first(where: { $0.clang != nil })?.clang else { return nil } - return SwiftPMToolchain( - swiftCompiler: base.swiftc!, - clangCompiler: clang, - libDir: base.swiftc!.parentDirectory.parentDirectory.appending(components: "lib", "swift", "pm"), + swiftCompiler: swiftc!, + clangCompiler: clang!, + libDir: swiftc!.parentDirectory.parentDirectory.appending(components: "lib", "swift", "pm"), sdkRoot: nil, extraCCFlags: [], extraSwiftCFlags: [], diff --git a/Sources/SKTestSupport/FileSystem.swift b/Sources/SKTestSupport/FileSystem.swift new file mode 100644 index 00000000..91a49ff2 --- /dev/null +++ b/Sources/SKTestSupport/FileSystem.swift @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basic + +extension FileSystem { + + /// Creates files from a dictionary of path to contents. + /// + /// - parameters: + /// - root: The root directory that the paths are relative to. + /// - files: Dictionary from path (relative to root) to contents. + public func createFiles(root: AbsolutePath = .root, files: [String: ByteString]) throws { + for (path, contents) in files { + let path = AbsolutePath(path, relativeTo: root) + try createDirectory(path.parentDirectory, recursive: true) + try writeFileContents(path, bytes: contents) + } + } +} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index eb8321bf..fdf0f74f 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -4,6 +4,7 @@ import LanguageServerProtocolJSONRPCTests import LanguageServerProtocolTests import SKCoreTests import SKSupportTests +import SKSwiftPMWorkspaceTests import SourceKitTests var tests = [XCTestCaseEntry]() @@ -11,6 +12,7 @@ tests += LanguageServerProtocolJSONRPCTests.__allTests() tests += LanguageServerProtocolTests.__allTests() tests += SKCoreTests.__allTests() tests += SKSupportTests.__allTests() +tests += SKSwiftPMWorkspaceTests.__allTests() tests += SourceKitTests.__allTests() XCTMain(tests) diff --git a/Tests/SKSwiftPMWorkspaceTests/SwiftPMWorkspaceTests.swift b/Tests/SKSwiftPMWorkspaceTests/SwiftPMWorkspaceTests.swift new file mode 100644 index 00000000..7665bc8b --- /dev/null +++ b/Tests/SKSwiftPMWorkspaceTests/SwiftPMWorkspaceTests.swift @@ -0,0 +1,222 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SKSwiftPMWorkspace +import SKCore +import PackageModel +import Basic +import SKTestSupport +import XCTest + +final class SwiftPMWorkspaceTests: XCTestCase { + func testBasicSwiftArgs() { + // FIXME: should be possible to use InMemoryFileSystem. + let fs = localFileSystem + let tempDir = try! TemporaryDirectory(removeTreeOnDeinit: true) + try! fs.createFiles(root: tempDir.path, files: [ + "pkg/Sources/lib/a.swift": "", + "pkg/Package.swift": """ + // swift-tools-version:4.2 + import PackageDescription + let package = Package(name: "a", products: [], dependencies: [], + targets: [.target(name: "lib", dependencies: [])]) + """ + ]) + let packageRoot = tempDir.path.appending(component: "pkg") + let tr = ToolchainRegistry.shared + let ws = try! SwiftPMWorkspace( + workspacePath: packageRoot, + toolchainRegistry: tr, + fileSystem: fs)! + + let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift") + + XCTAssertEqual(ws.buildPath, packageRoot.appending(components: ".build", "debug")) + XCTAssertNotNil(ws.indexStorePath) + let arguments = ws.settings(for: aswift.asURL, language: .swift)!.compilerArguments + + check( + "-module-name", "lib", "-incremental", "-emit-dependencies", + "-emit-module", "-emit-module-path", arguments: arguments) + check("-parse-as-library", "-c", arguments: arguments) + + check("-target", arguments: arguments) // Only one! +#if os(macOS) + check("-target", "x86_64-apple-macosx10.10", arguments: arguments) + check("-sdk", arguments: arguments) + check("-F", arguments: arguments) +#else + check("-target", "x86_64-unknown-linux", arguments: arguments) +#endif + + check("-I", packageRoot.appending(components: ".build", "debug").asString, arguments: arguments) + + check(aswift.asString, arguments: arguments) + } + + func testMultiFileSwift() { + // FIXME: should be possible to use InMemoryFileSystem. + let fs = localFileSystem + let tempDir = try! TemporaryDirectory(removeTreeOnDeinit: true) + try! fs.createFiles(root: tempDir.path, files: [ + "pkg/Sources/lib/a.swift": "", + "pkg/Sources/lib/b.swift": "", + "pkg/Package.swift": """ + // swift-tools-version:4.2 + import PackageDescription + let package = Package(name: "a", products: [], dependencies: [], + targets: [.target(name: "lib", dependencies: [])]) + """ + ]) + let packageRoot = tempDir.path.appending(component: "pkg") + let tr = ToolchainRegistry.shared + let ws = try! SwiftPMWorkspace( + workspacePath: packageRoot, + toolchainRegistry: tr, + fileSystem: fs)! + + let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift") + let bswift = packageRoot.appending(components: "Sources", "lib", "b.swift") + + let argumentsA = ws.settings(for: aswift.asURL, language: .swift)!.compilerArguments + check(aswift.asString, arguments: argumentsA) + check(bswift.asString, arguments: argumentsA) + let argumentsB = ws.settings(for: aswift.asURL, language: .swift)!.compilerArguments + check(aswift.asString, arguments: argumentsB) + check(bswift.asString, arguments: argumentsB) + } + + func testMultiTargetSwift() { + // FIXME: should be possible to use InMemoryFileSystem. + let fs = localFileSystem + let tempDir = try! TemporaryDirectory(removeTreeOnDeinit: true) + try! fs.createFiles(root: tempDir.path, files: [ + "pkg/Sources/libA/a.swift": "", + "pkg/Sources/libB/b.swift": "", + "pkg/Sources/libC/include/libC.h": "", + "pkg/Sources/libC/libC.c": "", + "pkg/Package.swift": """ + // swift-tools-version:4.2 + import PackageDescription + let package = Package(name: "a", products: [], dependencies: [], + targets: [ + .target(name: "libA", dependencies: ["libB", "libC"]), + .target(name: "libB", dependencies: []), + .target(name: "libC", dependencies: []), + ]) + """ + ]) + let packageRoot = tempDir.path.appending(component: "pkg") + let tr = ToolchainRegistry.shared + let ws = try! SwiftPMWorkspace( + workspacePath: packageRoot, + toolchainRegistry: tr, + fileSystem: fs)! + + let aswift = packageRoot.appending(components: "Sources", "libA", "a.swift") + let bswift = packageRoot.appending(components: "Sources", "libB", "b.swift") + let arguments = ws.settings(for: aswift.asURL, language: .swift)!.compilerArguments + check(aswift.asString, arguments: arguments) + checkNot(bswift.asString, arguments: arguments) + check("-I", packageRoot.appending(components: "Sources", "libC", "include").asString, arguments: arguments) + + let argumentsB = ws.settings(for: bswift.asURL, language: .swift)!.compilerArguments + check(bswift.asString, arguments: argumentsB) + checkNot(aswift.asString, arguments: argumentsB) + checkNot("-I", packageRoot.appending(components: "Sources", "libC", "include").asString, arguments: argumentsB) + } + + func testBasicCXXArgs() { + // FIXME: should be possible to use InMemoryFileSystem. + let fs = localFileSystem + let tempDir = try! TemporaryDirectory(removeTreeOnDeinit: true) + try! fs.createFiles(root: tempDir.path, files: [ + "pkg/Sources/lib/a.cpp": "", + "pkg/Sources/lib/b.cpp": "", + "pkg/Sources/lib/include/a.h": "", + "pkg/Package.swift": """ + // swift-tools-version:4.2 + import PackageDescription + let package = Package(name: "a", products: [], dependencies: [], + targets: [.target(name: "lib", dependencies: [])], + cxxLanguageStandard: .cxx14) + """ + ]) + let packageRoot = tempDir.path.appending(component: "pkg") + let tr = ToolchainRegistry.shared + let ws = try! SwiftPMWorkspace( + workspacePath: packageRoot, + toolchainRegistry: tr, + fileSystem: fs)! + + let acxx = packageRoot.appending(components: "Sources", "lib", "a.cpp") + let bcxx = packageRoot.appending(components: "Sources", "lib", "b.cpp") + let build = packageRoot.appending(components: ".build", "debug") + + XCTAssertEqual(ws.buildPath, build) + XCTAssertNotNil(ws.indexStorePath) + let arguments = ws.settings(for: acxx.asURL, language: .cpp)!.compilerArguments + + check("-MD", "-MT", "dependencies", + "-MF", build.appending(components: "lib.build", "a.cpp.d").asString, + arguments: arguments) + check("-std=c++14", arguments: arguments) + + checkNot("-arch", arguments: arguments) + check("-target", arguments: arguments) // Only one! +#if os(macOS) + check("-target", "x86_64-apple-macosx10.10", arguments: arguments) + check("-isysroot", arguments: arguments) + check("-F", arguments: arguments) +#else + check("-target", "x86_64-unknown-linux", arguments: arguments) +#endif + + check("-I", packageRoot.appending(components: "Sources", "lib", "include").asString, arguments: arguments) + checkNot("-I", build.asString, arguments: arguments) + check("-c", acxx.asString, arguments: arguments) + checkNot(bcxx.asString, arguments: arguments) + check("-o", build.appending(components: "lib.build", "a.cpp.o").asString, arguments: arguments) + } +} + +private func checkNot( + _ pattern: String..., + arguments: [String], + file: StaticString = #file, + line: UInt = #line) +{ + if let index = arguments.firstIndex(of: pattern) { + XCTFail( + "not-pattern \(pattern) unexpectedly found at \(index) in arguments \(arguments)", + file: file, line: line) + return + } +} + +private func check( + _ pattern: String..., + arguments: [String], + file: StaticString = #file, + line: UInt = #line) +{ + guard let index = arguments.firstIndex(of: pattern) else { + XCTFail("pattern \(pattern) not found in arguments \(arguments)", file: file, line: line) + return + } + + if let index2 = arguments[(index+1)...].firstIndex(of: pattern) { + XCTFail( + "pattern \(pattern) found twice (\(index), \(index2)) in \(arguments)", + file: file, line: line) + } +} diff --git a/Tests/SKSwiftPMWorkspaceTests/XCTestManifests.swift b/Tests/SKSwiftPMWorkspaceTests/XCTestManifests.swift new file mode 100644 index 00000000..0f803a53 --- /dev/null +++ b/Tests/SKSwiftPMWorkspaceTests/XCTestManifests.swift @@ -0,0 +1,21 @@ +#if !canImport(ObjectiveC) +import XCTest + +extension SwiftPMWorkspaceTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__SwiftPMWorkspaceTests = [ + ("testBasicCXXArgs", testBasicCXXArgs), + ("testBasicSwiftArgs", testBasicSwiftArgs), + ("testMultiFileSwift", testMultiFileSwift), + ("testMultiTargetSwift", testMultiTargetSwift), + ] +} + +public func __allTests() -> [XCTestCaseEntry] { + return [ + testCase(SwiftPMWorkspaceTests.__allTests__SwiftPMWorkspaceTests), + ] +} +#endif