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