Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import XCTest
XCTMain([])
17 changes: 17 additions & 0 deletions Fixtures/Miscellaneous/UnreachableTargets/A/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// swift-tools-version:4.0
import PackageDescription

let package = Package(
name: "A",
products: [
.executable(name: "aexec", targets: ["ATarget"])
],
dependencies: [
.package(url: "../B", from: "1.0.0"),
.package(url: "../C", from: "1.0.0")
],
targets: [
.target(name: "ATarget", dependencies: [
.product(name: "BLibrary")
])
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import BTarget1

BTarget1()
13 changes: 13 additions & 0 deletions Fixtures/Miscellaneous/UnreachableTargets/B/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// swift-tools-version:4.0
import PackageDescription

let package = Package(
name: "B",
products: [
.library(name: "BLibrary", targets: ["BTarget1"]),
.executable(name: "bexec", targets: ["BTarget2"])
],
targets: [
.target(name: "BTarget1", dependencies: []),
.target(name: "BTarget2", dependencies: [])
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public struct BTarget1 {
public init() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
print("BTarget2")
11 changes: 11 additions & 0 deletions Fixtures/Miscellaneous/UnreachableTargets/C/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// swift-tools-version:4.0
import PackageDescription

let package = Package(
name: "C",
products: [
.executable(name: "cexec", targets: ["CTarget"])
],
targets: [
.target(name: "CTarget", dependencies: [])
])
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
print("CTarget")
9 changes: 3 additions & 6 deletions Sources/Build/BuildPlan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ public class BuildPlan {

// Create build target description for each target which we need to plan.
var targetMap = [ResolvedTarget: TargetDescription]()
for target in graph.targets {
for target in graph.allTargets {
switch target.underlyingTarget {
case is SwiftTarget:
targetMap[target] = .swift(SwiftTargetDescription(target: target, buildParameters: buildParameters))
Expand All @@ -518,10 +518,7 @@ public class BuildPlan {
if buildParameters.triple.isLinux() {
// FIXME: Create a target for LinuxMain file on linux.
// This will go away once it is possible to auto detect tests.
let testProducts = graph.products.filter({ $0.type == .test })
if testProducts.count > 1 {
fatalError("It is not possible to have multiple test products on linux \(testProducts)")
}
let testProducts = graph.allProducts.filter({ $0.type == .test })

for product in testProducts {
guard let linuxMainTarget = product.linuxMainTarget else {
Expand All @@ -536,7 +533,7 @@ public class BuildPlan {
var productMap: [ResolvedProduct: ProductBuildDescription] = [:]
// Create product description for each product we have in the package graph except
// for automatic libraries because they don't produce any output.
for product in graph.products where product.type != .library(.automatic) {
for product in graph.allProducts where product.type != .library(.automatic) {
productMap[product] = ProductBuildDescription(
product: product, buildParameters: buildParameters)
}
Expand Down
41 changes: 26 additions & 15 deletions Sources/Build/llbuild.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public struct LLBuildManifestGenerator {
private var otherTargets: [Target] = []

/// Append a command.
mutating func append(_ target: Target, isTest: Bool) {
mutating func append(_ target: Target, buildByDefault: Bool, isTest: Bool) {
// Create a phony command with a virtual output node that represents the target.
let virtualNodeName = "<\(target.name)>"
let phonyTool = PhonyTool(inputs: target.outputs.values, outputs: [virtualNodeName])
Expand All @@ -63,14 +63,17 @@ public struct LLBuildManifestGenerator {
newTarget.cmds.insert(phonyCommand)
otherTargets.append(newTarget)

if !isTest {
main.outputs += newTarget.outputs
main.cmds += newTarget.cmds
if buildByDefault {
if !isTest {
main.outputs += newTarget.outputs
main.cmds += newTarget.cmds
}

// Always build everything for the test target.
test.outputs += newTarget.outputs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean we will end up compiling tests from other packages for when building tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because the code only runs whenbuildByDefault is true, which is set to plan.graph.reachableTargets.contains(target) on line 91. See below the vapor build manifest output from a master build of swiftpm vs with this branch:

llbuild_manifests.zip

test.cmds += newTarget.cmds
}

// Always build everything for the test target.
test.outputs += newTarget.outputs
test.cmds += newTarget.cmds
allCommands += newTarget.cmds
}
}
Expand All @@ -80,18 +83,26 @@ public struct LLBuildManifestGenerator {
var targets = Targets()

// Create commands for all target description in the plan.
for buildTarget in plan.targets {
switch buildTarget {
case .swift(let target):
targets.append(createSwiftCompileTarget(target), isTest: target.isTestTarget)
case .clang(let target):
targets.append(createClangCompileTarget(target), isTest: target.isTestTarget)
for (target, description) in plan.targetMap {
switch description {
case .swift(let description):
// Only build targets by default if they are reachabe from a root target.
targets.append(createSwiftCompileTarget(description),
buildByDefault: plan.graph.reachableTargets.contains(target),
isTest: description.isTestTarget)
case .clang(let description):
targets.append(createClangCompileTarget(description),
buildByDefault: plan.graph.reachableTargets.contains(target),
isTest: description.isTestTarget)
}
}

// Create command for all products in the plan.
for buildProduct in plan.buildProducts {
targets.append(createProductTarget(buildProduct), isTest: buildProduct.product.type == .test)
for (product, description) in plan.productMap {
// Only build products by default if they are reachabe from a root target.
targets.append(createProductTarget(description),
buildByDefault: plan.graph.reachableProducts.contains(product),
isTest: product.type == .test)
}

// Write the manifest.
Expand Down
27 changes: 15 additions & 12 deletions Sources/Commands/SwiftRunTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,27 +126,30 @@ public class SwiftRunTool: SwiftTool<RunToolOptions> {

/// Returns the path to the correct executable based on options.
private func findProduct(in plan: BuildPlan) throws -> ResolvedProduct {
let executableProducts = plan.graph.products.filter({ $0.type == .executable })

// Error out if the product contains no executable.
guard executableProducts.count > 0 else {
throw RunError.noExecutableFound
}

if let executable = options.executable {
// If the exectuable is explicitly specified, verify that it exists.
guard let executableProduct = executableProducts.first(where: { $0.name == executable }) else {
// If the exectuable is explicitly specified, search through all products.
guard let executableProduct = plan.graph.allProducts.first(where: {
$0.type == .executable && $0.name == executable
}) else {
throw RunError.executableNotFound(executable)
}

return executableProduct
} else {
// If the executable is implicit, search through root products.
let rootExecutables = plan.graph.rootPackages.flatMap({ $0.products }).filter({ $0.type == .executable })

// Error out if the package contains no executables.
guard rootExecutables.count > 0 else {
throw RunError.noExecutableFound
}

// Only implicitly deduce the executable if it is the only one.
guard executableProducts.count == 1 else {
throw RunError.multipleExecutables(executableProducts.map({ $0.name }))
guard rootExecutables.count == 1 else {
throw RunError.multipleExecutables(rootExecutables.map({ $0.name }))
}

return executableProducts[0]
return rootExecutables[0]
}
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/Commands/SwiftTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -579,13 +579,13 @@ extension BuildSubset {
case .allIncludingTests:
return LLBuildManifestGenerator.llbuildTestTargetName
case .product(let productName):
guard let product = graph.products.first(where: { $0.name == productName }) else {
guard let product = graph.allProducts.first(where: { $0.name == productName }) else {
diagnostics.emit(data: ProductNotFoundDiagnostic(productName: productName))
return nil
}
return product.llbuildTargetName
case .target(let targetName):
guard let target = graph.targets.first(where: { $0.name == targetName }) else {
guard let target = graph.allTargets.first(where: { $0.name == targetName }) else {
diagnostics.emit(data: TargetNotFoundDiagnostic(targetName: targetName))
return nil
}
Expand Down
32 changes: 18 additions & 14 deletions Sources/PackageGraph/PackageGraph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ public struct PackageGraph {
/// with the root packages.
public let packages: [ResolvedPackage]

/// Returns list of all targets (reachable from root targets) in the graph.
public let targets: [ResolvedTarget]
/// The list of all targets reachable from root targets.
public let reachableTargets: Set<ResolvedTarget>

/// Contains all the products of the root packages and the product dependencies of the root targets.
/// i.e. this array will not contain the products which are not needed to be built.
public let products: [ResolvedProduct]
/// The list of all products reachable from root targets.
public let reachableProducts: Set<ResolvedProduct>

/// Returns all the targets in the graph, regardless if they are reachable from the root targets or not.
public let allTargets: Set<ResolvedTarget>

/// Returns all the products in the graph, regardless if they are reachable from the root targets or not.
public var allProducts: Set<ResolvedProduct>

/// Returns true if a given target is present in root packages.
public func isInRootPackages(_ target: ResolvedTarget) -> Bool {
Expand All @@ -38,29 +43,28 @@ public struct PackageGraph {
self.rootPackages = rootPackages
let inputPackages = rootPackages + rootDependencies
self.packages = try! topologicalSort(inputPackages, successors: { $0.dependencies })
allTargets = Set(packages.flatMap({ $0.targets }))
allProducts = Set(packages.flatMap({ $0.products }))

// Compute the input targets.
let inputTargets = inputPackages.flatMap({ $0.targets }).map(ResolvedTarget.Dependency.target)
// Find all the dependencies of the root targets.
let dependencies = try! topologicalSort(inputTargets, successors: { $0.dependencies })

// Separate out the products and targets but maintain their topological order.
var targets: [ResolvedTarget] = []
var products = inputPackages.flatMap({ $0.products })
let rootDependencyProductSet = Set(rootDependencies.flatMap({ $0.products }))
var reachableTargets: Set<ResolvedTarget> = []
var reachableProducts = Set(inputPackages.flatMap({ $0.products }))

for dependency in dependencies {
switch dependency {
case .target(let target):
targets.append(target)
reachableTargets.insert(target)
case .product(let product):
// Avoid re-adding any product from the root dependencies.
if rootDependencyProductSet.contains(product) { continue }
products.append(product)
reachableProducts.insert(product)
}
}

self.targets = targets
self.products = products
self.reachableTargets = reachableTargets
self.reachableProducts = reachableProducts
}
}
2 changes: 1 addition & 1 deletion Sources/Xcodeproj/generate().swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public func generate(
""")
}

for target in graph.targets where target.type == .library || target.type == .test {
for target in graph.reachableTargets where target.type == .library || target.type == .test {
///// For framework targets, generate target.c99Name_Info.plist files in the
///// directory that Xcode project is generated
let name = target.infoPlistFileName
Expand Down
4 changes: 2 additions & 2 deletions Sources/Xcodeproj/pbxproj().swift
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ func xcodeProject(

// Determine the set of targets to generate in the project by excluding
// any system targets.
let targets = graph.targets.filter({ $0.type != .systemModule })
let targets = graph.reachableTargets.filter({ $0.type != .systemModule })

// If we have any external packages, we also add a `Dependencies` group at
// the top level, along with a sources subgroup for each package.
Expand Down Expand Up @@ -599,7 +599,7 @@ func xcodeProject(
// Create an aggregate target for every product for which there isn't already
// a target with the same name.
let targetNames: Set<String> = Set(modulesToTargets.values.map({ $0.name }))
for product in graph.products {
for product in graph.reachableProducts {
// Go on to next product if we already have a target with the same name.
if targetNames.contains(product.name) { continue }
// Otherwise, create an aggreate target.
Expand Down
4 changes: 2 additions & 2 deletions Sources/Xcodeproj/xcscheme().swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func xcscheme(container: String, graph: PackageGraph, codeCoverageEnabled: Bool,
""")

// Create buildable references for non-test targets.
for target in graph.targets where target.type != .test {
for target in graph.reachableTargets where target.type != .test {
// Ignore system targets.
//
// FIXME: We shouldn't need to manually do this here, instead this
Expand Down Expand Up @@ -54,7 +54,7 @@ func xcscheme(container: String, graph: PackageGraph, codeCoverageEnabled: Bool,
""")

// Create testable references.
for target in graph.targets where target.type == .test {
for target in graph.reachableTargets where target.type == .test {
print("""
<TestableReference
skipped = "NO">
Expand Down
Loading