Skip to content
Open
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
2 changes: 1 addition & 1 deletion Sources/swift-bootstrap/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors

add_executable(swift-bootstrap
main.swift)
SwiftBootstrapBuildTool.swift)
target_link_libraries(swift-bootstrap PRIVATE
ArgumentParser
Basics
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import ArgumentParser
import Basics
import SPMBuildCore
import PackageModel

extension AbsolutePath {
public init?(argument: String) {
if let cwd: AbsolutePath = localFileSystem.currentWorkingDirectory {
guard let path = try? AbsolutePath(validating: argument, relativeTo: cwd) else {
return nil
}
self = path
} else {
guard let path = try? AbsolutePath(validating: argument) else {
return nil
}
self = path
}
}

public static var defaultCompletionKind: CompletionKind {
// This type is most commonly used to select a directory, not a file.
// Specify '.file()' in an argument declaration when necessary.
.directory
}
}
extension AbsolutePath: ExpressibleByArgument {}

extension BuildConfiguration {
public init?(argument: String) {
self.init(rawValue: argument)
}
}
extension BuildConfiguration: ExpressibleByArgument {}

extension BuildSystemProvider.Kind: ExpressibleByArgument {}
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,7 @@ import struct TSCBasic.OrderedSet
import enum TSCUtility.Diagnostics
import struct TSCUtility.Version

await { () async in
await SwiftBootstrapBuildTool.main()
}()

@main
struct SwiftBootstrapBuildTool: AsyncParsableCommand {
static let configuration = CommandConfiguration(
commandName: "swift-bootstrap",
Expand Down Expand Up @@ -410,7 +407,7 @@ struct SwiftBootstrapBuildTool: AsyncParsableCommand {

// Compute the transitive closure of available dependencies.
let input = loadedManifests.map { identity, manifest in KeyedPair(manifest, key: identity) }
_ = try await topologicalSort(input) { pair in
_ = try await self.topologicalSort(input) { pair in
// When bootstrapping no special trait build configuration is used
let dependenciesRequired = try pair.item.dependenciesRequired(for: .everything)
let dependenciesToLoad = dependenciesRequired.map{ $0.packageRef }.filter { !loadedManifests.keys.contains($0.identity) }
Expand Down Expand Up @@ -481,82 +478,51 @@ struct SwiftBootstrapBuildTool: AsyncParsableCommand {
delegateQueue: .sharedConcurrent
)
}
}
}

// TODO: move to shared area
extension AbsolutePath {
public init?(argument: String) {
if let cwd: AbsolutePath = localFileSystem.currentWorkingDirectory {
guard let path = try? AbsolutePath(validating: argument, relativeTo: cwd) else {
return nil
}
self = path
} else {
guard let path = try? AbsolutePath(validating: argument) else {
return nil
}
self = path
}
}

public static var defaultCompletionKind: CompletionKind {
// This type is most commonly used to select a directory, not a file.
// Specify '.file()' in an argument declaration when necessary.
.directory
}
}
private func topologicalSort<T: Hashable>(
_ nodes: [T], successors: (T) async throws -> [T]
) async throws -> [T] {
// Implements a topological sort via recursion and reverse postorder DFS.
func visit(_ node: T,
_ stack: inout OrderedSet<T>, _ visited: inout Set<T>, _ result: inout [T],
_ successors: (T) async throws -> [T]) async throws {
// Mark this node as visited -- we are done if it already was.
if !visited.insert(node).inserted {
return
}

extension BuildConfiguration {
public init?(argument: String) {
self.init(rawValue: argument)
}
}
// Otherwise, visit each adjacent node.
for succ in try await successors(node) {
guard stack.append(succ) else {
// If the successor is already in this current stack, we have found a cycle.
//
// FIXME: We could easily include information on the cycle we found here.
throw TSCBasic.GraphError.unexpectedCycle
}
try await visit(succ, &stack, &visited, &result, successors)
let popped = stack.removeLast()
assert(popped == succ)
}

extension AbsolutePath: ExpressibleByArgument {}
extension BuildConfiguration: ExpressibleByArgument {}
extension BuildSystemProvider.Kind: ExpressibleByArgument {}

public func topologicalSort<T: Hashable>(
_ nodes: [T], successors: (T) async throws -> [T]
) async throws -> [T] {
// Implements a topological sort via recursion and reverse postorder DFS.
func visit(_ node: T,
_ stack: inout OrderedSet<T>, _ visited: inout Set<T>, _ result: inout [T],
_ successors: (T) async throws -> [T]) async throws {
// Mark this node as visited -- we are done if it already was.
if !visited.insert(node).inserted {
return
}
// Add to the result.
result.append(node)
}

// Otherwise, visit each adjacent node.
for succ in try await successors(node) {
guard stack.append(succ) else {
// If the successor is already in this current stack, we have found a cycle.
//
// FIXME: We could easily include information on the cycle we found here.
throw TSCBasic.GraphError.unexpectedCycle
// FIXME: This should use a stack not recursion.
var visited = Set<T>()
var result = [T]()
var stack = OrderedSet<T>()
for node in nodes {
precondition(stack.isEmpty)
stack.append(node)
try await visit(node, &stack, &visited, &result, successors)
let popped = stack.removeLast()
assert(popped == node)
}
try await visit(succ, &stack, &visited, &result, successors)
let popped = stack.removeLast()
assert(popped == succ)
}

// Add to the result.
result.append(node)
return result.reversed()
}
}
}

// FIXME: This should use a stack not recursion.
var visited = Set<T>()
var result = [T]()
var stack = OrderedSet<T>()
for node in nodes {
precondition(stack.isEmpty)
stack.append(node)
try await visit(node, &stack, &visited, &result, successors)
let popped = stack.removeLast()
assert(popped == node)
}

return result.reversed()
}