commit ac35ae5

lung  ·  2026-11-03 02:14:47 +0000 UTC
parent 2357bf2
add c.compile_commands(), make buildc.rv idiomatic revo
2 files changed,  +159, -57
+158, -57
  1@@ -1,110 +1,211 @@
  2 const engine = import "./build.rv"
  3-#we use zig's C toolchain by default.
  4-const settings = { 
  5-  cc = {"zig", "cc"}, ar = {"zig", "ar"}, flags = {}, link_flags = {},
  6-  include_dirs = {}, defines = {}, deps = {},
  7+
  8+const settings = {
  9+  # we use zig's C toolchain by default.
 10+  cc = {"zig", "cc"},
 11+  ar = {"zig", "ar"},
 12+  flags = {},
 13+  link_flags = {},
 14+  include_dirs = {},
 15+  defines = {},
 16+  deps = {},
 17 }
 18 
 19-pub fn configure(opts) do
 20+type Flags = table<string>
 21+  type Compilation = {file: string, output: string, command: Flags}
 22+
 23+#* kept for compile_commands.json *#
 24+const compilations: table<Compilation> = {}
 25+
 26+#* override the global c settings *#
 27+pub fn configure(opts: table) -> :nil do
 28   for key in table.keys(opts) do
 29-    if (not table.key?(settings, key)) panic("unknown C setting:", key)
 30+    if not table.key?(settings, key) do
 31+      panic("unknown C setting:", key)
 32+    end
 33+
 34     settings[key] = opts[key]
 35   end
 36-end
 37-
 38-fn get(opts, key, fallback) do
 39-  if (table.key?(opts, key)) return opts[key]
 40-  return fallback
 41-end
 42 
 43-fn append(args, values) do
 44-  for value in values do
 45-    args:push(value)
 46-  end
 47+  return :nil
 48 end
 49 
 50-fn combined(opts, key) do
 51-  const values = table.copy(settings[key])
 52-  append(values, get(opts, key, {}))
 53-  return values
 54+fn combined(opts: table, key: atom) -> Flags do
 55+  return settings[key]:merge(table.key(opts, key) orelse {})
 56 end
 57 
 58-fn action(path, args) do
 59+#*
 60+  run a command into a temp path, then move it into place
 61+  the returned command may panic, sorry
 62+*#
 63+fn action(path: string, args: Flags) -> (fn() -> :ok) do
 64   return fn() do
 65     const parts = path:split("/")
 66     parts:pop()
 67     const dir = table.join(parts, "/")
 68-    if (dir != "") fs.mkdir(dir, :true):unwrap()
 69+
 70+    if dir != "" do
 71+      fs.mkdir(dir, :true):unwrap()
 72+    end
 73+
 74     const temporary = path ~ ".tmp"
 75-    if (fs.exists?(temporary)) fs.remove(temporary):unwrap()
 76+
 77+    if fs.exists?(temporary) do
 78+      fs.remove(temporary):unwrap()
 79+    end
 80+
 81     engine.cmd(args)
 82-    fs.rename(temporary, path):unwrap()
 83+
 84+    return fs.rename(temporary, path):unwrap()
 85   end
 86 end
 87 
 88-fn output_rule(kind, path, opts) do
 89-  const cc = get(opts, :cc, settings.cc)
 90+fn output_rule(kind: atom, path: string, opts: table) -> string do
 91+  const cc = table.key(opts, :cc) orelse settings.cc
 92   const deps = combined(opts, :deps)
 93+
 94   # editing the manifest invalidates its outputs too.
 95   deps:push(argv[0])
 96-  const sources = get(opts, :sources, {})
 97-  const objects = table.copy(get(opts, :objects, {}))
 98+
 99+  const sources = table.key(opts, :sources) orelse {}
100+  const objects = table.copy(table.key(opts, :objects) orelse {})
101+
102   for source in sources do
103-    const stem = if source:ends_with?(".c") do
104-      source:sub(0, source:len() - 2)
105-    end else do
106-      source
107-    end
108+    const stem =
109+      if (source:ends_with?(".c"))
110+        source:sub(0, source:len() - 2)
111+      else
112+        source
113+
114     const object = path ~ ".obj/" ~ stem ~ ".o"
115-    const args = table.copy(cc)
116-    append(args, combined(opts, :flags))
117+    const args = cc:merge(combined(opts, :flags))
118+
119     for dir in combined(opts, :include_dirs) do
120       args:push("-I", dir)
121     end
122+
123     for define in combined(opts, :defines) do
124       args:push("-D" ~ define)
125     end
126-    if (kind == :shared) args:push("-fPIC")
127+
128+    if kind == :shared do
129+      args:push("-fPIC")
130+    end
131+
132     const temporary = object ~ ".tmp"
133     args:push("-c", source, "-o", temporary)
134-    const inputs = table.copy(deps)
135-    inputs:push(source)
136-    # construct the rule
137+
138+    # push the real one not temp
139+    const command = table.copy(args)
140+    command[table.len(command) - 1] = object
141+    compilations:push({file = source, output = object, command = command})
142+
143+    const inputs = deps:merge({source})
144     engine.rule(object, inputs, action(object, args))
145+
146     const label = "cc " ~ source
147     engine.describe(object, label)
148     objects:push(object)
149   end
150 
151-  const libraries = get(opts, :libraries, {})
152-  const inputs = table.copy(deps)
153-  append(inputs, objects)
154-  append(inputs, libraries)
155-  let args = table.copy(cc)
156+  const libraries = table.key(opts, :libraries) orelse {}
157+  const inputs = deps
158+                 :merge(objects)
159+                 :merge(libraries)
160+
161+  let args = cc
162+
163   if kind == :static do
164-    if (table.len(libraries) > 0) panic("static libraries take objects, not libraries:", path)
165-    args = table.copy(get(opts, :ar, settings.ar))
166+    if not table.empty?(libraries) do
167+      panic("static libraries take objects, not libraries:", path)
168+    end
169+
170     const temporary = path ~ ".tmp"
171-    args:push("rcs", temporary)
172-    append(args, objects)
173+
174+    args = (table.key(opts, :ar) orelse settings.ar):merge({"rcs", temporary})
175+    args = args:merge(objects)
176   end else do
177-    append(args, combined(opts, :flags))
178-    if (kind == :shared) args:push("-shared")
179-    append(args, objects)
180-    append(args, libraries)
181-    append(args, combined(opts, :link_flags))
182+    args = args:merge(combined(opts, :flags))
183+
184+    if kind == :shared do
185+      args:push("-shared")
186+    end
187+
188+    args = args
189+           :merge(objects)
190+           :merge(libraries)
191+           :merge(combined(opts, :link_flags))
192+
193     const temporary = path ~ ".tmp"
194     args:push("-o", temporary)
195   end
196+
197   engine.rule(path, inputs, action(path, args))
198+
199   const prefixes = {executable = "ld ", static = "ar ", shared = "shared "}
200   const link_label = prefixes[kind] ~ path
201   engine.describe(path, link_label)
202+
203   return path
204 end
205 
206-pub fn executable(path, opts) output_rule(:executable, path, opts)
207-pub fn static_library(path, opts) output_rule(:static, path, opts)
208-pub fn shared_library(path, opts) output_rule(:shared, path, opts)
209+#* build an executable *#
210+pub fn executable(path: string, opts: table) -> string
211+  return output_rule(:executable, path, opts)
212+
213+#* build an static library *#
214+pub fn static_library(path: string, opts: table) -> string
215+  return output_rule(:static, path, opts)
216 
217-pub fn build(outputs) engine.run(outputs)
218+#* build an shared library *#
219+pub fn shared_library(path: string, opts: table) -> string
220+  return output_rule(:shared, path, opts)
221+
222+fn quote(arg: string) -> string do
223+  if arg:len() > 0
224+    and not arg:contains?(" ")
225+    and not arg:contains?("\t")
226+    and not arg:contains?("'")
227+    and not arg:contains?("\"") do
228+    return arg
229+  end
230+
231+  return "'" ~ arg:replace("'", "'\\''") ~ "'"
232+end
233+
234+#*
235+  write compile_commands.json into pwd for every source compiled so far
236+  just put this at the end of your script
237+*#
238+pub fn compile_commands() -> :nil do
239+  const directory = cwd()
240+
241+  const entries =
242+    to_iter(compilations)
243+    :map(fn(compilation) do
244+      const quoted =
245+        to_iter(compilation.command)
246+        :map(quote)
247+        :collect()
248+
249+      return {
250+        directory = directory,
251+        file = compilation.file,
252+        output = compilation.output,
253+        command = quoted:join(" "),
254+      }
255+    end)
256+    :collect()
257+
258+  const text = json.encode(entries, :true)?
259+  const file = fs.open("compile_commands.json", "w")?
260+
261+  file:write(text ~ "\n")?
262+  file:close()?
263+
264+  return :nil
265+end
266+
267+pub fn build(outputs: table<string>) -> :ok do
268+  return engine.run(outputs)
269+end
+1, -0
1@@ -20,3 +20,4 @@ const app = c.executable("out/calculator", {
2 )
3 
4 c.build({app})
5+c.compile_commands()