main
buildodin.rv
1const engine = import "./build.rv"
2
3const settings = {
4 compiler = {"odin"},
5 package = ".",
6 optimize = "",
7 debug = :false,
8 target = "",
9 flags = {},
10 collections = {},
11 defines = {},
12 deps = {},
13}
14
15type Flags = table<string>
16
17#* override the global odin settings *#
18pub fn configure(opts: table) -> :nil do
19 for key in table.keys(opts) do
20 if not table.key?(settings, key) do
21 panic("unknown Odin setting:", key)
22 end
23
24 settings[key] = opts[key]
25 end
26
27 return :nil
28end
29
30fn combined(opts: table, key: atom) -> table do
31 return settings[key]:merge(table.key(opts, key) orelse {})
32end
33
34#* track every .odin file in the entry package, including target-specific files *#
35fn sources(dir: string) -> Flags do
36 const paths: Flags = {}
37
38 for entry in fs.readdir(dir):unwrap() do
39 if entry.kind == :file and entry.name:ends_with?(".odin") do
40 paths:push(dir ~ "/" ~ entry.name)
41 end
42 end
43
44 return table.sort(paths)
45end
46
47fn key_name(key: any) -> string do
48 const name = string(key)
49
50 if typeof(key) == :atom do
51 return name:sub(1, name:len() - 1)
52 end
53
54 return name
55end
56
57#*
58 run a command into a temp path, then move it into place
59 the returned command may panic, sorry
60*#
61fn action(path: string, args: Flags) -> (fn() -> :ok) do
62 return fn() do
63 const parts = path:split("/")
64 parts:pop()
65 const dir = table.join(parts, "/")
66
67 if dir != "" do
68 fs.mkdir(dir, :true):unwrap()
69 end
70
71 const temporary = path ~ ".tmp"
72
73 if fs.exists?(temporary) do
74 fs.remove(temporary):unwrap()
75 end
76
77 engine.cmd(args)
78
79 return fs.rename(temporary, path):unwrap()
80 end
81end
82
83fn output_rule(kind: atom, path: string, opts: table) -> string do
84 const compiler = table.key(opts, :compiler) orelse settings.compiler
85 const package = table.key(opts, :package) orelse settings.package
86 const optimize = table.key(opts, :optimize) orelse settings.optimize
87 const debug = table.key(opts, :debug) orelse settings.debug
88 const target = table.key(opts, :target) orelse settings.target
89
90 const temporary = path ~ ".tmp"
91 const modes = {executable = "exe", static = "static", shared = "shared"}
92 const args = compiler:merge({"build", package})
93 args:push("-build-mode:" ~ modes[kind], "-out:" ~ temporary)
94
95 if kind != :executable do
96 args:push("-no-entry-point")
97 end
98
99 if debug do
100 args:push("-debug")
101 end
102
103 if optimize != "" do
104 args:push("-o:" ~ optimize)
105 end
106
107 if target != "" do
108 args:push("-target:" ~ target)
109 end
110
111 const collections = combined(opts, :collections)
112
113 for key in table.keys(collections) do
114 const name = key_name(key)
115 args:push("-collection:" ~ name ~ "=" ~ collections[key])
116 end
117
118 const defines = combined(opts, :defines)
119
120 for key in table.keys(defines) do
121 const name = key_name(key)
122 const define = defines[key]
123 const value =
124 if typeof(define) == :string do
125 define
126 end else do
127 json.encode(define):unwrap()
128 end
129
130 args:push("-define:" ~ name ~ "=" ~ value)
131 end
132
133 const command = args:merge(combined(opts, :flags))
134 const inputs = combined(opts, :deps)
135 :merge(sources(package))
136 :merge({package, argv[0]})
137
138 engine.rule(path, inputs, action(path, command))
139 engine.track(path, command)
140
141 const prefixes = {executable = "odin ", static = "odin static ", shared = "odin shared "}
142 const label = prefixes[kind] ~ path
143 engine.describe(path, label)
144
145 return path
146end
147
148#* build an executable from an odin package directory *#
149pub fn executable(path: string, opts: table) -> string
150 return output_rule(:executable, engine.output(path), opts)
151
152#* build a static library *#
153pub fn static_library(path: string, opts: table) -> string
154 return output_rule(:static, engine.output(path), opts)
155
156#* build a shared library *#
157pub fn shared_library(path: string, opts: table) -> string
158 return output_rule(:shared, engine.output(path), opts)
159
160pub fn build(outputs: table<string>) -> :ok do
161 return engine.run(outputs)
162end