Tasks as Executables (Example)§

Say, for example, you need to build a compiler or 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("compiler://tools", "input-file", self.path / "output-file")
    ]
)

Running makex run build://project1 will run the compiler Task (in //tools) producing an executable named compiler-binary.

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

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

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 Task using a dependency on the build Task, and the built-in lightweight makex copy mechanism to create individual Tasks for each executable.