mirror of
https://github.com/apple/container.git
synced 2026-07-15 22:17:01 +00:00
16f2630126
We're working on making a pure swift container image build system that leverages containerization. This PR represents our initial design and initial work towards this goal. The native builder is still in active development and most of the implementation has not been started or completed. We will be opening a series of issues that represent various (but not necessarily all) pieces of work that need to be done here. There are docs included in this PR that describe the overall design of each component and outline some of our goals. The easiest way to view the docs by themselves (since this is a massive PR) is to look at the docs commit in the `Commits` tab. We'd love any feedback! @wlan0 --------- Signed-off-by: Kathryn Baldauf <k_baldauf@apple.com>
112 lines
4.2 KiB
Swift
112 lines
4.2 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
// Copyright © 2025 Apple Inc. and the container project authors. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import ContainerBuildReporting
|
|
import Foundation
|
|
|
|
/// Establishes dependencies between operations in the build graph.
|
|
///
|
|
/// This analyzer is responsible for:
|
|
/// - Setting up intra-stage dependencies (sequential operations within a stage)
|
|
/// - Establishing cross-stage dependencies (COPY --from operations)
|
|
/// - Ensuring operations have proper dependencies for correct execution order
|
|
public struct DependencyAnalyzer: GraphAnalyzer {
|
|
|
|
public init() {}
|
|
|
|
public func analyze(_ graph: BuildGraph, context: AnalysisContext) throws -> BuildGraph {
|
|
var updatedStages: [BuildStage] = []
|
|
|
|
// Process each stage
|
|
for stage in graph.stages {
|
|
let updatedStage = try analyzeStage(stage, allStages: graph.stages)
|
|
updatedStages.append(updatedStage)
|
|
}
|
|
|
|
// Create updated graph with new dependencies
|
|
return try BuildGraph(
|
|
stages: updatedStages,
|
|
buildArgs: graph.buildArgs,
|
|
targetPlatforms: graph.targetPlatforms,
|
|
metadata: graph.metadata
|
|
)
|
|
}
|
|
|
|
private func analyzeStage(_ stage: BuildStage, allStages: [BuildStage]) throws -> BuildStage {
|
|
var updatedNodes: [BuildNode] = []
|
|
var lastNodeId: UUID? = nil
|
|
|
|
// Process each node in the stage
|
|
for (index, node) in stage.nodes.enumerated() {
|
|
var dependencies = node.dependencies
|
|
|
|
// 1. Sequential dependency: each operation depends on the previous one
|
|
if dependencies.isEmpty && index > 0 {
|
|
if let lastId = lastNodeId {
|
|
dependencies.insert(lastId)
|
|
}
|
|
}
|
|
|
|
// 2. Cross-stage dependencies: COPY --from operations
|
|
if let copyOp = node.operation as? FilesystemOperation {
|
|
if case .stage(let stageRef, _) = copyOp.source {
|
|
// Find the referenced stage
|
|
if let sourceStage = resolveStageReference(stageRef, currentStage: stage, allStages: allStages) {
|
|
// COPY --from depends on all operations in the source stage
|
|
if let lastOp = sourceStage.nodes.last {
|
|
dependencies.insert(lastOp.id)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create updated node with dependencies
|
|
let updatedNode = BuildNode(
|
|
id: node.id,
|
|
operation: node.operation,
|
|
dependencies: dependencies
|
|
)
|
|
|
|
updatedNodes.append(updatedNode)
|
|
lastNodeId = updatedNode.id
|
|
}
|
|
|
|
// Return updated stage
|
|
return BuildStage(
|
|
id: stage.id,
|
|
name: stage.name,
|
|
base: stage.base,
|
|
nodes: updatedNodes,
|
|
platform: stage.platform
|
|
)
|
|
}
|
|
|
|
private func resolveStageReference(_ ref: StageReference, currentStage: BuildStage, allStages: [BuildStage]) -> BuildStage? {
|
|
switch ref {
|
|
case .named(let name):
|
|
return allStages.first { $0.name == name }
|
|
case .index(let idx):
|
|
return idx < allStages.count ? allStages[idx] : nil
|
|
case .previous:
|
|
// Find current stage index
|
|
if let currentIndex = allStages.firstIndex(where: { $0.id == currentStage.id }), currentIndex > 0 {
|
|
return allStages[currentIndex - 1]
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
}
|