Writing your own build
built-in
There are two different types of built-ins for the build phase. The first category consists of the built-ins
relying on the command()
built-in (such as, for example, the apt
built-in). These are usually wrappers around existing commands, allowing to use them more conveniently in Quixote.
The second category is more complex, and used for lower-level built-ins.
Writing a command()
-based built-in
For this part, we will pick the dpkg command as a case study, and see how we can wrap it as a built-in.
Note
dpkg
is a rather complex command, so our wrapper will only provide basic actions, such as installing
or removing packages.
We want our module to be used in a way similar to the apt
module, like shown below:
import dpkg
dpkg.install("path/to/my_package.deb", "my_other_package.deb")
dpkg.remove("yet_another_package")
So first, in order to start, we will create a Python module and call it dpkg
, and import the
command()
function:
from quixote.build.shell import command
Next, we will create functions for the pieces of dpkg
we want to wrap. As said earlier, we will wrap
package installation (which matches the -i
switch) and package removal (which matches the -r
switch).
def install():
pass
def remove():
pass
So far, these functions do nothing, but we will now fill them up, starting with the install
function.
In order to implement it correctly, we need to look at the corresponding section in the man page for dpkg
.
-i, --install package-file...
Install the package. If --recursive or -R option is specified, package-file must
refer to a directory instead.
With this information, we know that the command accepts an arbitrary number of paths to package files, and therefore that our function must accept an arbitrary number of strings to represent those.
def install(*args: str):
pass
Now that our function accepts parameters, we need to transfer them to the actual dpkg
command. This is where
the command()
built-in comes into play.
def install(*args: str):
return shell.command("dpkg -i " + ' '.join(args))
Are we done yet? No, as the man page still mentions the -R
option, to install packages contained in a directory.
In order to handle that option in our wrapper, we will simply add a recursive
parameter to our function.
By default, it will be set to False
, but if the caller sets it to True
, we will add a -R
to our
invocation of the dpkg
command:
def install(*args: str, recursive=False):
if recursive is True:
return command("dpkg -R -i " + ' '.join(args))
else:
return command("dpkg -i " + ' '.join(args))
And we’re done! Of course, we haven’t implemented the remove
function yet, but this is left as an
exercise for you to practice.
As always, the full dpkg
module (including the solution) can still be downloaded
here
.
Writing an advanced built-in
Built-ins for the build
phase are somewhat special, in the way that they constitute a small
DSL allowing Quixote to construct
the specified environment. In order to do that, Quixote translates from that DSL to the language
of the desired environment builder (e.g. Docker or bash scripts).
Warning
This tutorial is still work-in-progress!