A host the mesh builds knows what it was built for
novox/hq 04-ISSUES/161. The mesh compiled the host, published it, delivered it, and the launcher started it — and it would have refused the first declaration it was asked to apply, because it asks which system it was built for before applying anything and the answer was empty. The Makefile links that in. The mesh's toolchain deliberately takes nothing from the module, so it linked in nothing. The system is the stated exception, and ADR 0142 says why: the target is a property of the artifact rather than of the recipe, because a compiled binary is per system and a toolchain accepting it from the module would be accepting a build instruction. So the toolchain names the variable it fills and the artifact supplies the value. Named in the toolchain rather than inferred, and empty for a language whose output is not pinned to a system — which is every interpreted one, and a manifest declaring a system for those is already refused.
This commit is contained in:
@@ -877,6 +877,14 @@ func compile(ctx context.Context, run Runner, tree string, chain Toolchain,
|
|||||||
base,
|
base,
|
||||||
}
|
}
|
||||||
invocation = append(invocation, chain.Compile...)
|
invocation = append(invocation, chain.Compile...)
|
||||||
|
// What it was built for, linked in. The compile line carries `-ldflags` already; this appends a
|
||||||
|
// second one, which the Go linker accepts and merges. A host with no system refuses every
|
||||||
|
// declaration before it applies anything (novox/hq 04-ISSUES/161), and it is the artifact that
|
||||||
|
// knows — the target is a property of the artifact rather than of the recipe (ADR 0142).
|
||||||
|
if chain.SystemStamp != "" && strings.TrimSpace(a.System) != "" {
|
||||||
|
invocation = append(invocation, "-ldflags",
|
||||||
|
"-X "+chain.SystemStamp+"="+strings.TrimSpace(a.System))
|
||||||
|
}
|
||||||
if chain.OutputFlag != "" {
|
if chain.OutputFlag != "" {
|
||||||
// A compiler pointed at a package is told the file to write, not the directory: the name a
|
// A compiler pointed at a package is told the file to write, not the directory: the name a
|
||||||
// machine runs it by is not always the name of the package that built it. The host's command
|
// machine runs it by is not always the name of the package that built it. The host's command
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package builder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A host built without knowing its system refuses every declaration before applying anything —
|
||||||
|
// safely, totally, and with nothing reporting it. The mesh built one, delivered it, started it, and
|
||||||
|
// it would have refused the first thing it was asked to do (novox/hq 04-ISSUES/161).
|
||||||
|
|
||||||
|
func TestTheGoToolchainStampsTheArtifactsSystem(t *testing.T) {
|
||||||
|
chain, err := ToolchainFor("go")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if chain.SystemStamp != "main.builtFor" {
|
||||||
|
t.Fatalf("the go toolchain fills %q", chain.SystemStamp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestALanguageWithNoPinnedSystemStampsNothing(t *testing.T) {
|
||||||
|
// Interpreted output is not pinned to a system, and a manifest declaring one for it is already
|
||||||
|
// refused. Nothing to fill.
|
||||||
|
for _, language := range []string{"typescript", "python"} {
|
||||||
|
chain, err := ToolchainFor(language)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if chain.SystemStamp != "" {
|
||||||
|
t.Fatalf("%s fills %q, and its output is not pinned to a system",
|
||||||
|
language, chain.SystemStamp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTheStampIsTheOneThingTakenFromTheArtifact(t *testing.T) {
|
||||||
|
// The toolchain accepts nothing else from the module — anything it could override it would be
|
||||||
|
// writing a Dockerfile to override. The system is the stated exception, because a compiled
|
||||||
|
// binary is per system and the artifact is what declares one (ADR 0142).
|
||||||
|
chain, err := ToolchainFor("go")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
joined := strings.Join(chain.Compile, " ")
|
||||||
|
if strings.Contains(joined, "${") || strings.Contains(joined, "%s") {
|
||||||
|
t.Fatalf("the compile line takes something from the module: %q", joined)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,6 +49,18 @@ type Toolchain struct {
|
|||||||
// is named as it will be FOUND, inside the unpacked bundle, so the source is the same path with
|
// is named as it will be FOUND, inside the unpacked bundle, so the source is the same path with
|
||||||
// the output directory taken off the front and this on the end.
|
// the output directory taken off the front and this on the end.
|
||||||
SourceExt string
|
SourceExt string
|
||||||
|
// SystemStamp is the variable this language's linker fills with the artifact's declared system,
|
||||||
|
// for a language whose binaries are pinned to one at link time (novox/hq ADR 0005).
|
||||||
|
//
|
||||||
|
// **The one thing a toolchain takes from the artifact, and 0142 says why**: the target is a
|
||||||
|
// property of the artifact rather than of the recipe, because a compiled binary is per system
|
||||||
|
// and a toolchain that accepted it from the module would be accepting a build instruction. This
|
||||||
|
// is the narrow exception, named here rather than inferred.
|
||||||
|
//
|
||||||
|
// Empty for a language that compiles to nothing pinned. A host built without it refuses every
|
||||||
|
// declaration before applying anything — safely, totally, and with nothing reporting it
|
||||||
|
// (novox/hq 04-ISSUES/161).
|
||||||
|
SystemStamp string
|
||||||
}
|
}
|
||||||
|
|
||||||
// What a toolchain is pointed at.
|
// What a toolchain is pointed at.
|
||||||
@@ -122,6 +134,9 @@ var toolchains = []Toolchain{
|
|||||||
// directory holding one executable, which is what the delivery mechanism expects
|
// directory holding one executable, which is what the delivery mechanism expects
|
||||||
// (novox/hq ADR 0141).
|
// (novox/hq ADR 0141).
|
||||||
Unit: UnitPackage,
|
Unit: UnitPackage,
|
||||||
|
// The mesh's own Go components read the system they were built for from this variable, and
|
||||||
|
// refuse to touch a machine without one.
|
||||||
|
SystemStamp: "main.builtFor",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Language: "python",
|
Language: "python",
|
||||||
|
|||||||
Reference in New Issue
Block a user