Tasks as Executables (Example)

Say, for example, you need to build a compiler/code generator to build another part of your source code.

We shall use two separate Makex files to demonstrate this pattern.

The first Makex file defines a compiler tool and how to build it (//tools/makexfile):

task(
    name="compiler",
    steps=[
        # steps to build compiler
        ...  
    ],
    outputs="compiler-binary"
)

The second Makex file uses the compiler tool as part of its build process (//project1/makexfile):

task(
    name="build",
    steps=[
        execute("//tools:compiler", "input-file", self.path / "output-file")
    ]
)

Running mx //project1:build will run the //tools:compiler task producing an executable named compiler-binary.

The compiler-binary will be used in the //project1:build Task to translate an input file to an output file.

Since compiler-binary is the only output of //tools:compiler task, it shall be unambiguously used as the binary.

Note

In a future implementation of Makex, a task may produce multiple [named] outputs, and one of outputs may be selected as executable (in the cases of large compiler toolkits, such as clang).

We’re also exploring a concept we’re calling first-class executables.

At the moment, if you need to work with such multiple-output task patterns, you must create separate tasks using a dependency on the build task, and the built-in lightweight makex copy mechanism to create individual tasks for each executable.