build(Makefile): enable parallel make -j gen with correct dependency graph (#22612)

`make gen` could not run with `-j` because inter-target dependency edges
were missing. Multiple recipes compile `coderd/rbac` (which includes
generated files like `object_gen.go`), and without explicit ordering,
parallel runs produced syntax errors from mid-write reads.

Three main changes:

**Dependency graph fixes** declare the compile-time chain through
`coderd/rbac` so that `object_gen.go` is written before anything that
imports it is compiled. The DB generation targets use a GNU Make 4.3+
grouped target (`&:`) so Make knows `generate.sh` co-produces
`querier.go`, `unique_constraint.go`, `dbmetrics`, and `dbauthz` in a
single invocation. `SKIP_DUMP_SQL=1` avoids re-entrant `make` inside
`generate.sh` when the Makefile already guarantees `dump.sql` is fresh.

**`scripts/atomicwrite` package** replaces `os.WriteFile` in all gen
scripts with a temp-file-in-same-dir + rename pattern, preventing
interrupted runs from leaving partial files.

**`.PRECIOUS` and shell atomic writes** protect git-tracked generated
files from Make's default delete-on-error behavior. Since these files
are committed, deletion is worse than staleness -- `git restore` is the
recovery path.

CI now runs `make -j --output-sync -B gen` (~32s, down from ~85s
serial).

| Scenario                          | Before             | After    |
|-----------------------------------|--------------------|----------|
| `make gen` (serial)               | 95s                | 95s      |
| `make -j gen` (parallel)          | race error         | **22s**  |
| CI `make -j --output-sync -B gen` | forced serial ~85s | **~32s** |
This commit is contained in:
Mathias Fredriksson
2026-03-05 11:58:10 +00:00
committed by GitHub
parent b0e10402c8
commit a6a8fd94d7
15 changed files with 218 additions and 94 deletions
+6 -8
View File
@@ -12,6 +12,7 @@ import (
"github.com/acarl005/stripansi"
"github.com/coder/coder/v2/buildinfo"
"github.com/coder/coder/v2/scripts/atomicwrite"
"github.com/coder/flog"
"github.com/coder/serpent"
)
@@ -125,24 +126,21 @@ func genTree(dir string, cmd *serpent.Command, wroteLog map[string]*serpent.Comm
}
path := filepath.Join(dir, fmtDocFilename(cmd))
// Write out root.
fi, err := os.OpenFile(
path,
os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644,
)
var buf strings.Builder
err := writeCommand(&buf, cmd)
if err != nil {
return err
}
defer fi.Close()
err = writeCommand(fi, cmd)
err = atomicwrite.File(path, []byte(buf.String()))
if err != nil {
return err
}
flog.Successf(
"wrote\t%s",
fi.Name(),
path,
)
wroteLog[path] = cmd
for _, sub := range cmd.Children {
+7 -1
View File
@@ -8,6 +8,7 @@ import (
"strings"
"github.com/coder/coder/v2/enterprise/cli"
"github.com/coder/coder/v2/scripts/atomicwrite"
"github.com/coder/flog"
"github.com/coder/serpent"
)
@@ -94,6 +95,11 @@ func main() {
cliMarkdownDir = filepath.Join(docsDir, "reference/cli")
)
if d := os.Getenv("DOCS_DIR"); d != "" {
docsDir = d
cliMarkdownDir = filepath.Join(docsDir, "reference/cli")
}
cmd, err := root.Command(root.EnterpriseSubcommands())
if err != nil {
flog.Fatalf("creating command: %v", err)
@@ -188,7 +194,7 @@ func main() {
flog.Fatalf("marshaling manifest: %v", err)
}
err = os.WriteFile(manifestPath, manifestByt, 0o600)
err = atomicwrite.File(manifestPath, manifestByt)
if err != nil {
flog.Fatalf("writing manifest: %v", err)
}