v / vlib / build / build.v
180 lines · 160 sloc · 4.03 KB · 74b6e3b5c70bd219db5aa6bc88552c6e0ddd8964
Raw
1module build
2
3import os
4
5@[heap; noinit]
6pub struct BuildContext {
7mut:
8 // should_run caches the result of should_run from tasks.
9 should_run map[string]bool
10 tasks []Task
11pub mut:
12 // default is the default task to run when no others are provided.
13 default ?string
14}
15
16@[heap; noinit]
17pub struct Task {
18 run fn (Task) ! @[required]
19 should_run fn (Task) !bool @[required]
20 // repeatable controls whether or not this task can run multiple times per build cycle
21 repeatable bool
22pub:
23 name string
24 help string
25 depends []string
26mut:
27 did_run bool
28}
29
30@[params]
31pub struct BuildContextParams {
32pub:
33 default ?string
34}
35
36@[params]
37pub struct TaskParams {
38pub:
39 name string @[required]
40 help string
41 depends []string
42 should_run fn (Task) !bool = |self| true
43 run fn (Task) ! @[required]
44 // repeatable controls whether or not this task can run multiple times per build cycle
45 repeatable bool
46}
47
48@[params]
49pub struct ArtifactParams {
50pub:
51 name string @[required]
52 help string
53 depends []string
54 should_run fn (Task) !bool = |self| !os.exists(self.name)
55 run fn (Task) ! @[required]
56 // repeatable controls whether or not this task can run multiple times per build cycle
57 repeatable bool
58}
59
60// context creates an empty BuildContext.
61pub fn context(params BuildContextParams) BuildContext {
62 return BuildContext{
63 default: params.default
64 }
65}
66
67// task creates a task for the given context.
68pub fn (mut context BuildContext) task(config TaskParams) {
69 if context.get_task(config.name) != none {
70 eprintln('error: task already exists with name `${config.name}`')
71 exit(1)
72 }
73 context.tasks << Task{
74 should_run: config.should_run
75 run: config.run
76 name: config.name
77 help: config.help
78 depends: config.depends
79 }
80}
81
82// artifact creates an artifact task for the given context.
83pub fn (mut context BuildContext) artifact(config ArtifactParams) {
84 if context.get_task(config.name) != none {
85 eprintln('error: task already exists with name `${config.name}`')
86 exit(1)
87 }
88 context.tasks << Task{
89 should_run: config.should_run
90 run: config.run
91 name: config.name
92 help: config.help
93 depends: config.depends
94 repeatable: config.repeatable
95 }
96}
97
98// get_task gets the task with the given name.
99pub fn (mut context BuildContext) get_task(name string) ?&Task {
100 for mut task in context.tasks {
101 if task.name == name {
102 return mut task
103 }
104 }
105 return none
106}
107
108// exec executes the task with the given name in the context.
109pub fn (mut context BuildContext) exec(name string) {
110 if mut task := context.get_task(name) {
111 task.exec(mut context)
112 } else {
113 eprintln('error: no such task: ${name}')
114 exit(1)
115 }
116}
117
118// exec runs the given task and its dependencies.
119pub fn (mut task Task) exec(mut context BuildContext) {
120 if task.did_run && !task.repeatable {
121 println(': ${task.name} (skipped)')
122 return
123 }
124
125 if task.name !in context.should_run {
126 context.should_run[task.name] = task.should_run(task) or {
127 eprintln('error: failed to call should_run for task `${task.name}`: ${err}')
128 exit(1)
129 }
130 }
131
132 if !context.should_run[task.name] {
133 println(': ${task.name} (skipped)')
134 return
135 }
136
137 for dep in task.depends {
138 if dep == task.name {
139 eprintln('error: cyclic task dependency detected, `${task.name}` depends on itself')
140 exit(1)
141 }
142
143 context.exec(dep)
144 }
145 println(': ${task.name}')
146 task.did_run = true
147 task.run(task) or {
148 eprintln('error: failed to run task `${task.name}`: ${err}')
149 exit(1)
150 }
151}
152
153// run executes all tasks provided through os.args.
154pub fn (mut context BuildContext) run() {
155 // filter out options
156 mut tasks := os.args[1..].filter(|it| !it.starts_with('-'))
157
158 // check options
159 if '--tasks' in os.args || '-tasks' in os.args {
160 println('Tasks:')
161 for _, task in context.tasks {
162 println('- ${task.name}: ${task.help}')
163 }
164 return
165 }
166
167 if tasks.len == 0 {
168 if context.default != none {
169 tasks << context.default
170 } else {
171 eprintln('error: no task provided, run with `--tasks` for a list')
172 exit(1)
173 }
174 }
175
176 // execute tasks
177 for arg in tasks {
178 context.exec(arg)
179 }
180}
181