Wednesday 29 September 2010

muddle and its directories - part 3 - mechanics

The mechanics of the build - the build description

The build description is one or more Python files, which the muddle command runs before doing anything else.

(A few muddle commands do not need a build description, obviously including init, but these are the exception.)

For various reasons, the main build description file is traditionally called src/builds/01.py. muddle automatically adds the directory containing the main build description (the file named in .muddle/Description) to the PYTHONPATH for the build description. Put more simply, the build description can import other Python files in the same directory, which makes it easy to split build descriptions if necessary.

This example build has a fairly simple build description, in src/builds/01.py:

#!  /usr/bin/env python
#
# An example of how to build a cpio archive as a
# deployment - e.g. for a Linux initrd.

import muddled
import muddled.pkgs.make
import muddled.deployments.cpio
import muddled.checkouts.simple

def describe_to(builder):
    # Checkout ..
    muddled.checkouts.simple.relative(builder, "cpio_co")
    muddled.pkgs.make.simple(builder, "pkg_cpio", "x86", "cpio_co")
    muddled.deployments.cpio.deploy(builder, "my_archive.cpio",
                                    {"x86": "/"},
                                    "cpio_dep", [ "x86" ])

    builder.invocation.add_default_role("x86")
    builder.by_default_deploy("cpio_dep")

# End file.

Briefly, muddle looks for a function called describe_to() in the build description, and runs it to define the build.

This particular example is quite old, and I'm not sure we'd write it quite like that any more. Regardless, it says the following things:

  • there is a checkout called "cpio_co", which is checked out from the default repository (as defined in .muddle/RootRepository).
  • the package named pkg_cpio in role x86 is built from that checkout - this will, by default, be done using a Makefile in the checkout directory.
  • when deploying the final results of the build, a CPIO archive shall be created, with the role "x86" going at the root of the filesystem in that CPIO archive. The name of this deployment is "cpio_dep".
  • the default package role to build is "x86"
  • the default thing to deploy is the "cpio_dep" deployment.

Some naming conventions

There are some strong naming conventions associated with muddle build trees:

  • The build description lives in a checkout called src/builds/

  • All packages build out-of-tree (that is, in the obj/ directory, which we shall describe later), so that the src/ directory only contains the checkout files as-checked-out.

  • If there is a checkout containing "helper" makefiles and other associated things, used to build checkouts in other directories, it should be called src/helpers/.

    (For instance, if one is building kbus, and downloading it directly from its google code repository each time, then it is convenient to put the muddle-specific makefile in a different checkout, possibly as src/helpers/Makefile.kbus)

    There was an earlier convention of using the name src/builders/ for such a directory, but this is visually confusing, and does not work well with tab-completion at the bash prompt.

  • Checkout names should reflect the name of the external package being built, and generally also the version. So, for instance screen-1.0.3.

  • Package names should be more general - if a package is built from screen-1.0.3, it would normally be named screen. This allows for a later change in the build to use (for instance) checkout screen-1.0.4 instead.

  • Deployment names should reflect the purpose of the deployment - for instance, firmware versus kernel, and so on. This is very much dependent on the purpose of the build itself.

Note

This particular build is not really following the naming convention for checkouts, packages and deployments as described above. This is because it is trying to make it very clear which name belongs to which label type (thus co_cpio, pkg_cpio and dep_cpio)

It is also moderately traditional to call the main Python file for a build description 01.py (or 02.py if it is version 2, and so on). This is not, however, a strong convention, it just makes the "main" file easy to spot.

Introspection of the dependency tree

muddle does have some ability to display the dependency tree that is generated from the build description, although it is not as user-friendly as we would like (and, ultimately, there should be graphical tools).

The simplest tool for dumping the dependency rules is the depends command (see muddle help depends for more details on what it does).

The following shows the dependency rules described directly by the build description above:

$ m3 depends user-short
-----
checkout:builds/changes_committed <-VcsCheckoutBuilder-- [ checkout:builds/up_to_date[T] ]
checkout:builds/changes_pushed <-VcsCheckoutBuilder-- [ checkout:builds/changes_committed ]
checkout:builds/pulled <-VcsCheckoutBuilder-- [ checkout:builds/checked_out, checkout:builds/up_to_date[T] ]
checkout:builds/up_to_date[T] <-VcsCheckoutBuilder-- [ checkout:builds/checked_out ]
checkout:cpio_co/changes_committed <-VcsCheckoutBuilder-- [ checkout:cpio_co/up_to_date[T] ]
checkout:cpio_co/changes_pushed <-VcsCheckoutBuilder-- [ checkout:cpio_co/changes_committed ]
checkout:cpio_co/pulled <-VcsCheckoutBuilder-- [ checkout:cpio_co/checked_out, checkout:cpio_co/up_to_date[T] ]
checkout:cpio_co/up_to_date[T] <-VcsCheckoutBuilder-- [ checkout:cpio_co/checked_out ]
deployment:cpio_dep/deployed <-CpioDeploymentBuilder-- [ package:*{x86}/postinstalled ]
package:pkg_cpio{x86}/built <-MakeBuilder-- [ package:pkg_cpio{x86}/configured ]
package:pkg_cpio{x86}/configured <-MakeBuilder-- [ package:pkg_cpio{x86}/preconfig ]
package:pkg_cpio{x86}/installed <-MakeBuilder-- [ package:pkg_cpio{x86}/built ]
package:pkg_cpio{x86}/postinstalled <-MakeBuilder-- [ package:pkg_cpio{x86}/installed ]
package:pkg_cpio{x86}/preconfig <-MakeBuilder-- [ checkout:cpio_co/checked_out ]
-----

Each line above shows three things:

  1. a target label.
  2. an arrow containing the name of the action to take to "satisfy" or "build" that label (nb: in the current trunk of muddle, this is not present).
  3. a list of the labels that must be "satisfied" or "built" before that action can be performed.

It is sorted by target label, which unfortunately is not too much help, but one can see that, for instance, the deployment:cpio_dep/deployed label depends on package:*{x86}/postinstalled, where the * is a wildcard over all package names in the {x86} role.

A shorter and perhaps more helpful representation of that can be provided using the query deps command:

$ m3 query deps deployment:cpio_dep/deployed
Build order for deployment:cpio_dep/deployed ..
checkout:cpio_co/checked_out
package:pkg_cpio{x86}/preconfig
package:pkg_cpio{x86}/configured
package:pkg_cpio{x86}/built
package:pkg_cpio{x86}/installed
package:pkg_cpio{x86}/postinstalled
deployment:cpio_dep/deployed

This shows all of the labels that must be built before deployment:cpio_dep/deployed, and in what order they will be built.

Checking out the source code

Normally, after the init command, one would just use a "bare" muddle command to perform the rest of the checkouts, build them (as they are checked out), and do any other steps indicated by the build description. However, since I'm interested in the various stages of the build tree, I shall do all of these things one by one.

So we start by checking out the actual source for our build:

$ m3 checkout _all
> Building checkout:cpio_co/checked_out
> svn checkout  http://muddle.googlecode.com/svn/trunk/muddle/examples/cpio/cpio_co cpio_co
A    cpio_co/hello_world.c
A    cpio_co/Makefile
Checked out revision 458.
> Make directory /home/tibs/sw/m3/example/.muddle/tags/checkout/cpio_co

After doing this, we've now gained a new tag in the .muddle/ directory:

`-- .muddle/
    |-- Description
    |-- RootRepository
    `-- tags/
        `-- checkout/
            |-- builds/
            |   `-- checked_out
            `-- cpio_co/
                `-- checked_out

indicating that we have successfully checked out the cpio_co checkout, and the source files for our missing checkout are now present in the src/ directory:

`-- src/
    |-- builds/
    |   |-- 01.py
    |   |-- 01.pyc
    |   `-- .svn/
    `-- cpio_co/
        |-- hello_world.c
        |-- Makefile
        `-- .svn/

(whilst I'm showing the .svn/ directories here, I've removed the listing of their contents).

The mechanics of the build - the makefile

This build only has a single checkout, which produces a single package. As such it also has a single makefile, src/cpio_co/Makefile:

# Makefile for cpio_co
#
# Just dumps some compiled C into the right place.

INSTALL=install

all:
        $(CC) -o $(MUDDLE_OBJ)/hello_world hello_world.c

install:
        if [ ! -d $(MUDDLE_INSTALL)/bin ]; then mkdir $(MUDDLE_INSTALL)/bin; fi
        $(INSTALL) -m 0755 $(MUDDLE_OBJ)/hello_world $(MUDDLE_INSTALL)/bin/hello_world
        $(INSTALL) -m 0644 hello_world.c $(MUDDLE_INSTALL)/hello_world.c

config:
        @echo Nothing to do

clean:
        rm -f $(MUDDLE_OBJ)/hello_world

distclean: clean
        @echo Distclean is just a clean

# end file.

We saw when querying the builds dependencies (in Introspection of the dependency tree) that various dependency rules had MakeBuilder as their action. This action knows what targets in a makefile to call in order to build a particular package: label tag.

The target label tags (the /xxx at the end of a label) correspond to makefile targets as follows:

Target tag Makefile target
/preconfig <none>
/configured config
/built all
/installed install
/postinstalled <none>
Target tag Makefile target
/clean clean
/distclean distclean

It can be useful to think of a package as "progressing" from /preconfig through to /postinstalled.

preconfig does not correspond to a make target, and there is no default action for it. Things that depend on a package existing (but nothing else) will depend on the /preconfig tagged package label.

There is no action defined by default for /preconfig. That doesn't stop a particular build from defining one - for instance, ExpandingMakeBuilder is a MakeBuilder subclass which expands a .tgz file into source files for the later stages to compile and install (see muddled.pkgs.make).

Although there is no make target associated with /postinstalled, it is at this stage that pkg-config files are rewritten (if requested), and this is the tag that should be used when another package depends on something being "fully" built.

No comments:

Post a Comment