One module, several languages, each bundle packed alone
A module is one piece of software and may still carry a daemon in one language, tools in another and a package in a third. The first cut compiled every bundle into the toolchain's single output directory, so two of them would have overwritten each other and then been packed together — one artifact containing both, published twice. So output is a property of the artifact, not of the toolchain, and the toolchain says how it is told where to write rather than where it writes. Under a directory named for the build rather than beside the source, so a pack never sweeps up the module's own working files. A second toolchain is declared so the multi-language path is exercised rather than asserted — a list with one entry cannot fail the way a list with four will. And the fake compiler in the tests now writes where it was TOLD to. One that always wrote to a fixed place would have passed whether or not each artifact got its own directory, which is the whole of what these tests are for. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -461,6 +461,12 @@ func compile(ctx context.Context, run Runner, tree string, chain Toolchain,
|
||||
// rather than configurable: a module that could move this would be describing its own build.
|
||||
const within = "/app/modules/module"
|
||||
|
||||
// **Its own output directory, because a module may be several languages at once.** One module
|
||||
// is one piece of software and can still carry a daemon in one language, tools in another and
|
||||
// a package in a third (ADR 0040). Compiling them all into one place would have them overwrite
|
||||
// each other and then be packed together, so each bundle compiles and packs alone.
|
||||
out := Out(a.Name)
|
||||
|
||||
invocation := []string{
|
||||
"run", "--rm",
|
||||
"--volume", tree + ":" + within,
|
||||
@@ -468,15 +474,18 @@ func compile(ctx context.Context, run Runner, tree string, chain Toolchain,
|
||||
base,
|
||||
}
|
||||
invocation = append(invocation, chain.Compile...)
|
||||
if chain.OutputFlag != "" {
|
||||
invocation = append(invocation, chain.OutputFlag, out)
|
||||
}
|
||||
// What to compile. Named by the module rather than discovered, so adding a file does not
|
||||
// silently change what a build produces.
|
||||
if len(a.Entrypoints) > 0 {
|
||||
invocation = append(invocation, sourcesFor(a.Entrypoints)...)
|
||||
invocation = append(invocation, sourcesFor(a.Entrypoints, out)...)
|
||||
}
|
||||
if _, err := run(ctx, tree, "docker", invocation...); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(tree, chain.Output), nil
|
||||
return filepath.Join(tree, out), nil
|
||||
}
|
||||
|
||||
// sourcesFor turns compiled entrypoints back into what to compile.
|
||||
@@ -484,10 +493,14 @@ func compile(ctx context.Context, run Runner, tree string, chain Toolchain,
|
||||
// A module names what a tool host should LOAD — compiled paths under the bundle's root — because
|
||||
// that is the thing anything else needs to know. What to compile is the same list with the
|
||||
// language's own extension, which is the toolchain's business rather than the module's.
|
||||
func sourcesFor(entrypoints []string) []string {
|
||||
out := make([]string, 0, len(entrypoints))
|
||||
func sourcesFor(entrypoints []string, out string) []string {
|
||||
sources := make([]string, 0, len(entrypoints))
|
||||
for _, e := range entrypoints {
|
||||
out = append(out, strings.TrimSuffix(e, filepath.Ext(e))+".ts")
|
||||
// An entrypoint is named as it will be FOUND — a path inside the unpacked bundle — so the
|
||||
// source is the same path with the output directory taken off the front and the language's
|
||||
// own extension on the end.
|
||||
at := strings.TrimPrefix(strings.TrimPrefix(e, out), "/")
|
||||
sources = append(sources, strings.TrimSuffix(at, filepath.Ext(at))+".ts")
|
||||
}
|
||||
return out
|
||||
return sources
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user