commit a739960

shrub  ·  2026-11-03 12:52:29 +0000 UTC
parent ac35ae5
add odin support
6 files changed,  +210, -0
M README
M README
+2, -0
1@@ -9,6 +9,8 @@ todo
2 ----
3 [X] core build engine
4 [X] higher level handling for C
5+[X] higher level handling for Odin
6 [ ] higher level handling for Zig
7 [ ] extensions for doing configure script style stuff easily(checking whats installed on the system)
8 [ ] become part of the revo stdlib!
9+
+161, -0
  1@@ -0,0 +1,161 @@
  2+const engine = import "./build.rv"
  3+
  4+const settings = {
  5+  compiler = {"odin"},
  6+  package = ".",
  7+  optimize = "",
  8+  debug = :false,
  9+  target = "",
 10+  flags = {},
 11+  collections = {},
 12+  defines = {},
 13+  deps = {},
 14+}
 15+
 16+type Flags = table<string>
 17+
 18+#* override the global odin settings *#
 19+pub fn configure(opts: table) -> :nil do
 20+  for key in table.keys(opts) do
 21+    if not table.key?(settings, key) do
 22+      panic("unknown Odin setting:", key)
 23+    end
 24+
 25+    settings[key] = opts[key]
 26+  end
 27+
 28+  return :nil
 29+end
 30+
 31+fn combined(opts: table, key: atom) -> table do
 32+  return settings[key]:merge(table.key(opts, key) orelse {})
 33+end
 34+
 35+#* track every .odin file in the entry package, including target-specific files *#
 36+fn sources(dir: string) -> Flags do
 37+  const paths: Flags = {}
 38+
 39+  for entry in fs.readdir(dir):unwrap() do
 40+    if entry.kind == :file and entry.name:ends_with?(".odin") do
 41+      paths:push(dir ~ "/" ~ entry.name)
 42+    end
 43+  end
 44+
 45+  return table.sort(paths)
 46+end
 47+
 48+fn key_name(key: any) -> string do
 49+  const name = string(key)
 50+
 51+  if typeof(key) == :atom do
 52+    return name:sub(1, name:len() - 1)
 53+  end
 54+
 55+  return name
 56+end
 57+
 58+#*
 59+  run a command into a temp path, then move it into place
 60+  the returned command may panic, sorry
 61+*#
 62+fn action(path: string, args: Flags) -> (fn() -> :ok) do
 63+  return fn() do
 64+    const parts = path:split("/")
 65+    parts:pop()
 66+    const dir = table.join(parts, "/")
 67+
 68+    if dir != "" do
 69+      fs.mkdir(dir, :true):unwrap()
 70+    end
 71+
 72+    const temporary = path ~ ".tmp"
 73+
 74+    if fs.exists?(temporary) do
 75+      fs.remove(temporary):unwrap()
 76+    end
 77+
 78+    engine.cmd(args)
 79+
 80+    return fs.rename(temporary, path):unwrap()
 81+  end
 82+end
 83+
 84+fn output_rule(kind: atom, path: string, opts: table) -> string do
 85+  const compiler = table.key(opts, :compiler) orelse settings.compiler
 86+  const package = table.key(opts, :package) orelse settings.package
 87+  const optimize = table.key(opts, :optimize) orelse settings.optimize
 88+  const debug = table.key(opts, :debug) orelse settings.debug
 89+  const target = table.key(opts, :target) orelse settings.target
 90+
 91+  const temporary = path ~ ".tmp"
 92+  const modes = {executable = "exe", static = "static", shared = "shared"}
 93+  const args = compiler:merge({"build", package})
 94+  args:push("-build-mode:" ~ modes[kind], "-out:" ~ temporary)
 95+
 96+  if kind != :executable do
 97+    args:push("-no-entry-point")
 98+  end
 99+
100+  if debug do
101+    args:push("-debug")
102+  end
103+
104+  if optimize != "" do
105+    args:push("-o:" ~ optimize)
106+  end
107+
108+  if target != "" do
109+    args:push("-target:" ~ target)
110+  end
111+
112+  const collections = combined(opts, :collections)
113+
114+  for key in table.keys(collections) do
115+    const name = key_name(key)
116+    args:push("-collection:" ~ name ~ "=" ~ collections[key])
117+  end
118+
119+  const defines = combined(opts, :defines)
120+
121+  for key in table.keys(defines) do
122+    const name = key_name(key)
123+    const define = defines[key]
124+    const value =
125+      if typeof(define) == :string do
126+      define
127+    end else do
128+      json.encode(define):unwrap()
129+    end
130+
131+    args:push("-define:" ~ name ~ "=" ~ value)
132+  end
133+
134+  const command = args:merge(combined(opts, :flags))
135+  const inputs = combined(opts, :deps)
136+                 :merge(sources(package))
137+                 :merge({package, argv[0]})
138+
139+  engine.rule(path, inputs, action(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
146+end
147+
148+#* build an executable from an odin package directory *#
149+pub fn executable(path: string, opts: table) -> string
150+  return output_rule(:executable, path, opts)
151+
152+#* build a static library *#
153+pub fn static_library(path: string, opts: table) -> string
154+  return output_rule(:static, path, opts)
155+
156+#* build a shared library *#
157+pub fn shared_library(path: string, opts: table) -> string
158+  return output_rule(:shared, path, opts)
159+
160+pub fn build(outputs: table<string>) -> :ok do
161+  return engine.run(outputs)
162+end
+1, -0
1@@ -0,0 +1 @@
2+/out/
+17, -0
 1@@ -0,0 +1,17 @@
 2+package main
 3+
 4+add :: proc(a, b: int) -> int {
 5+	return a + b
 6+}
 7+
 8+subtract :: proc(a, b: int) -> int {
 9+	return a - b
10+}
11+
12+multiply :: proc(a, b: int) -> int {
13+	return a * b
14+}
15+
16+divide :: proc(a, b: int) -> int {
17+	return a / b
18+}
+16, -0
 1@@ -0,0 +1,16 @@
 2+package main
 3+
 4+import "core:fmt"
 5+
 6+SHOW_DETAILS :: #config(SHOW_DETAILS, false)
 7+
 8+main :: proc() {
 9+	when SHOW_DETAILS {
10+		fmt.println("HELLO FROM ODIN BUILT WITH RVB!")
11+	}
12+
13+	fmt.printf("6 + 3 = %d\n", add(6, 3))
14+	fmt.printf("6 - 3 = %d\n", subtract(6, 3))
15+	fmt.printf("6 * 3 = %d\n", multiply(6, 3))
16+	fmt.printf("6 / 3 = %d\n", divide(6, 3))
17+}
+13, -0
 1@@ -0,0 +1,13 @@
 2+const odin = import "../../buildodin.rv"
 3+
 4+odin.configure({
 5+  optimize = "speed",
 6+})
 7+
 8+const app = odin.executable("out/calculator", {
 9+  package = ".",
10+  debug = :true,
11+  defines = {SHOW_DETAILS = :true},
12+})
13+
14+odin.build({app})