@@ -3,34 +3,48 @@ import Foundation
33/// A simple build system
44struct MiniMake {
55 /// Attributes of a task
6- enum TaskAttribute {
6+ enum TaskAttribute : String , Codable {
77 /// Task is phony, meaning it must be built even if its inputs are up to date
88 case phony
99 /// Don't print anything when building this task
1010 case silent
1111 }
12- /// A task to build
13- struct Task {
14- /// Key of the task
15- let key : TaskKey
16- /// Display name of the task
17- let displayName : String
12+
13+ /// Information about a task enough to capture build
14+ /// graph changes
15+ struct TaskInfo : Codable {
1816 /// Input tasks not yet built
19- var wants : Set < TaskKey >
17+ let wants : [ TaskKey ]
2018 /// Set of files that must be built before this task
2119 let inputs : [ String ]
2220 /// Output task name
2321 let output : String
2422 /// Attributes of the task
23+ let attributes : [ TaskAttribute ]
24+ }
25+
26+ /// A task to build
27+ struct Task {
28+ let info : TaskInfo
29+ /// Input tasks not yet built
30+ let wants : Set < TaskKey >
31+ /// Attributes of the task
2532 let attributes : Set < TaskAttribute >
33+ /// Display name of the task
34+ let displayName : String
35+ /// Key of the task
36+ let key : TaskKey
2637 /// Build operation
2738 let build : ( Task ) throws -> Void
2839 /// Whether the task is done
2940 var isDone : Bool
41+
42+ var inputs : [ String ] { self . info. inputs }
43+ var output : String { self . info. output }
3044 }
3145
3246 /// A task key
33- struct TaskKey : Hashable , Comparable , CustomStringConvertible {
47+ struct TaskKey : Codable , Hashable , Comparable , CustomStringConvertible {
3448 let id : String
3549 var description : String { self . id }
3650
@@ -41,7 +55,9 @@ struct MiniMake {
4155 static func < ( lhs: TaskKey , rhs: TaskKey ) -> Bool { lhs. id < rhs. id }
4256 }
4357
58+ /// All tasks in the build system
4459 private var tasks : [ TaskKey : Task ]
60+ /// Whether to explain why tasks are built
4561 private var shouldExplain : Bool
4662 /// Current working directory at the time the build started
4763 private let buildCwd : String
@@ -52,13 +68,26 @@ struct MiniMake {
5268 self . buildCwd = FileManager . default. currentDirectoryPath
5369 }
5470
55- mutating func addTask( inputFiles: [ String ] = [ ] , inputTasks: [ TaskKey ] = [ ] , output: String , attributes: Set < TaskAttribute > = [ ] , build: @escaping ( Task ) throws -> Void ) -> TaskKey {
71+ /// Adds a task to the build system
72+ mutating func addTask( inputFiles: [ String ] = [ ] , inputTasks: [ TaskKey ] = [ ] , output: String , attributes: [ TaskAttribute ] = [ ] , build: @escaping ( Task ) throws -> Void ) -> TaskKey {
5673 let displayName = output. hasPrefix ( self . buildCwd) ? String ( output. dropFirst ( self . buildCwd. count + 1 ) ) : output
5774 let taskKey = TaskKey ( id: output)
58- self . tasks [ taskKey] = Task ( key: taskKey, displayName: displayName, wants: Set ( inputTasks) , inputs: inputFiles, output: output, attributes: attributes, build: build, isDone: false )
75+ let info = TaskInfo ( wants: inputTasks, inputs: inputFiles, output: output, attributes: attributes)
76+ self . tasks [ taskKey] = Task ( info: info, wants: Set ( inputTasks) , attributes: Set ( attributes) , displayName: displayName, key: taskKey, build: build, isDone: false )
5977 return taskKey
6078 }
6179
80+ /// Computes a stable fingerprint of the build graph
81+ ///
82+ /// This fingerprint must be stable across builds and must change
83+ /// if the build graph changes in any way.
84+ func computeFingerprint( root: TaskKey ) throws -> Data {
85+ let encoder = JSONEncoder ( )
86+ encoder. outputFormatting = . sortedKeys
87+ let tasks = self . tasks. sorted { $0. key < $1. key } . map { $0. value. info }
88+ return try encoder. encode ( tasks)
89+ }
90+
6291 private func explain( _ message: @autoclosure ( ) -> String ) {
6392 if self . shouldExplain {
6493 print ( message ( ) )
@@ -100,7 +129,8 @@ struct MiniMake {
100129 }
101130 }
102131
103- private func computeTotalTasks( task: Task ) -> Int {
132+ /// Computes the total number of tasks to build used for progress display
133+ private func computeTotalTasksForDisplay( task: Task ) -> Int {
104134 var visited = Set < TaskKey > ( )
105135 func visit( task: Task ) -> Int {
106136 guard !visited. contains ( task. key) else { return 0 }
@@ -114,6 +144,14 @@ struct MiniMake {
114144 return visit ( task: task)
115145 }
116146
147+ /// Cleans all outputs of all tasks
148+ func cleanEverything( ) {
149+ for task in self . tasks. values {
150+ try ? FileManager . default. removeItem ( atPath: task. output)
151+ }
152+ }
153+
154+ /// Starts building
117155 mutating func build( output: TaskKey ) throws {
118156 /// Returns true if any of the task's inputs have a modification date later than the task's output
119157 func shouldBuild( task: Task ) -> Bool {
@@ -143,7 +181,7 @@ struct MiniMake {
143181 return shouldBuild
144182 }
145183 }
146- var progressPrinter = ProgressPrinter ( total: self . computeTotalTasks ( task: self . tasks [ output] !) )
184+ var progressPrinter = ProgressPrinter ( total: self . computeTotalTasksForDisplay ( task: self . tasks [ output] !) )
147185
148186 func runTask( taskKey: TaskKey ) throws {
149187 guard var task = self . tasks [ taskKey] else {
0 commit comments