<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta />
    <article-meta>
      <title-group>
        <article-title>Lessons and Pitfalls in Building Firefox with Tup</article-title>
      </title-group>
      <contrib-group>
        <contrib contrib-type="author">
          <string-name>Guillaume Maudoux</string-name>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Kim Mens UCLouvain (Belgium) Email:</string-name>
        </contrib>
        <contrib contrib-type="author">
          <string-name>guillaume.maudoux</string-name>
        </contrib>
        <contrib contrib-type="author">
          <string-name>kim.mensg@uclouvain.be</string-name>
        </contrib>
      </contrib-group>
      <abstract>
        <p>-Build system implementations are surprisingly numerous for the single common purpose of assembling software. With this variety, picking the right one is a complex task. And even more difficult is the migration to a new build system, with uncertain benefits at the end. Software maintainers and release engineers need better comparisons of build systems and precise catagorisation on which to base an informed decision. As a first step toward that goal, we experimented building Firefox with Tup in replacement of Make. We report here our experience at migrating and comparing the build systems. We also describe interesting features of Tup and we discus Mozilla's Firefox usage as a benchmark for build systems.</p>
      </abstract>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>-</title>
      <p>
        We started investigating Mozilla’s build system a year
ago, in the hope of finding a large code base for testing
different build systems in a realistic setup. We settled on
Mozilla’s Firefox because their build infrastructure is designed
in such a way that different build systems can be plugged in.
This is no coincidence, but an ongoing work at Mozilla to
update their build system. The rationale being that picking
the right replacement for Make would require testing different
alternatives, and each alternative would require a complete
port. With a generic build definition, they can test different
build systems and let experimentations drive the selection
of the next build system. Other considerations, like gaining
control over the build definition, and using a widely known
language to do so were taken into account when discussing
this change [
        <xref ref-type="bibr" rid="ref1 ref2">1, 2</xref>
        ].
      </p>
      <p>We started implementing the Tup backend last in August
2017, when Mozilla’s support for it was minimal. It later
appeared that Tup was also the next target build system for
Mozilla itself, which led to two independent implementations
(ours and theirs). Mozilla’s effort to use Tup has focused on
producing reusable, clean code by fixing one issue at a time.
Our focus was on getting the build to work, regardless of
the code quality. Over the time, both implementations have
converged and as of August 2018, Mozilla’s implementation
should be preferred as it is now complete and is the only one
that will be further maintained and updated.</p>
      <p>This paper presents the insights we gained on three main
areas. With this work, we investigated and gained an in-depth
knowledge of Tup’s capabilities and interals. We also learned a
lot about Firefox’s build system design and their tactics to tame
Fig. 1. Firefox’s build system is split in two phases and can use different
backends to perform builds. Depending on the intended backend, different
build definitions are generated.
their complex codebase. Finally, we accumulated experience
in migrating from one build system to an other, and learned
the hard way that the process is theoretically simple but very
technical in practice.</p>
    </sec>
    <sec id="sec-2">
      <title>II. CONTEXT</title>
      <p>For readers who may not be already familiar with these, we
briefly depict Mozilla’s build system, Firefox itself and the
two build systems considered this article: Make and Tup.</p>
      <p>
        a) Mozilla’s build system: Mozilla’s modular build setup
works in two phases, as depicted in Figure 1. moz.build files
describe the build with a Mozilla-specific python DSL. This
format is parsed and Makefiles are generated in such a way
that the remaining of the build can be handled by Make. As
stated before, this design accepts new emitters, or back-ends
to be used in place of the Makefile generator. This is how the
Tup backend was implemented. Make and Tup are examples
of backends used for compilation, but the same mechanism
is also used to extract information from the build system to
be used by other tools. For example, a backend generates a
compilation database for advanced autocompletion in some
IDEs [
        <xref ref-type="bibr" rid="ref3 ref4">3, 4</xref>
        ]. This flexibility makes it possible to test different
build systems and inspect Mozilla’s Firefox compilation.
b) Firefox codebase: Firefox is a libre, open-source
browser with a large code base. The core application contained
about 4M lines of code in 2013, and was growing steadily [
        <xref ref-type="bibr" rid="ref5">5</xref>
        ].
This does not count tests and other configuration files. At that
time, the full repository contained about 15M lines of code,
and has now reached above 36M lines of code as of 2018 [
        <xref ref-type="bibr" rid="ref6">6</xref>
        ],
which makes it bigger than the Linux kernel and Libre Office
according to Open Hub. It is written in a variety of languages.
C++ forms the core application, supplemented by JavaScript
on top of the core engine. The repository contains also HTML,
C, python, java and has recently seen the introduction of Rust,
Mozilla’s designed systems programming language. A large
code base, with a variety of languages forming an open-source
and well known application. All these features makes it a good
candidate for meaningful build systems comparison.
      </p>
      <p>
        c) Make: Speaking of build systems, Make is the
reference application in that domain. It was designed at Bell Labs
in 1976 and was derived and reimplemented many times since
then [
        <xref ref-type="bibr" rid="ref7 ref8">7, 8</xref>
        ]. Make’s configuration language used in Makefiles
is well known by a large proportion programmers. Over the
years, Make has seen many disussions about its shortcomings,
and even more discussions about good practices and usage.
Most notably, the paper “Recursive Make Considered
Harmful [
        <xref ref-type="bibr" rid="ref9">9</xref>
        ]” discussed the good usage of Make on large software
projects, advocating for efficiency at the expense of
modularity. Strinking the right balance between conflicting goals is still
a challenge for current system implementations [
        <xref ref-type="bibr" rid="ref10">10</xref>
        ]. Make
has always been the build system of Firefox and is tightly
integrated in its code base.
      </p>
      <p>
        d) Tup: From Tup’s home page, we see that “Tup is
a file-based build system for Linux, OSX, and Windows. It
inputs a list of file changes and a directed acyclic graph
(DAG), then processes the DAG to execute the appropriate
commands required to update dependent files. Updates are
performed with very little overhead since Tup implements
powerful build algorithms to avoid doing unnecessary work.
This means you can stay focused on your project rather than
on your build system” [
        <xref ref-type="bibr" rid="ref11">11</xref>
        ]. Besides optimization and careful
implementation, what makes Tup truly special is its ability
to trace command executions to obtain real dependencies
and outputs. Validating the declared dependencies with the
observed file accesses adds a safeguard against imprecise
declaration and potential speedups that we will discuss later.
      </p>
    </sec>
    <sec id="sec-3">
      <title>III. RESEARCH OBJECTIVES</title>
      <p>
        From the above description of Tup, it is clear that Tup is
focussed on performance of updates. Detailed explanations on
“avoid doing unnecessary work” and “stay focussed on your
project” are to be found later on Tup’s home page or in [
        <xref ref-type="bibr" rid="ref12">12</xref>
        ].
In a nutshell, avoiding redundant work is the aim of a minimal
incremental build system. Minimalism ensures that up-to-date
outputs are maintained without running the tasks that produce
them. Tup claims that it is minimal in that sense. “Stay
focused” hides the even more complex idea of correctness.
Some build system can get corrupted, or out of sync and it is
not uncommon for developers to clean their build tree and start
again from scratch. A correct build system is one that always
rebuilds what needs to be rebuilt. When an incremental build
is complete, the binaries reflect the sources exactly, and the
developer need not worry about the build system’s state and
shortcomings. She therefore can “stay focused on her project”.
      </p>
      <p>To summarize, Tup has three claims, each of which we will
try to verify in this paper.</p>
    </sec>
    <sec id="sec-4">
      <title>1) Performance of execution,</title>
      <p>2) Minimal incremental updates,
3) Always correct build outputs.</p>
      <p>
        These features arise from the design and careful implematation
of the algorithms detailed in [
        <xref ref-type="bibr" rid="ref12">12</xref>
        ]. They are hard to demonstrate
by experiment as they should hold by design. We have
nonetheless tested Tup’s efficiency at building Firefox and
gathered the results presented in the next section.
      </p>
      <p>Minimal updates and correctness can be investigated by a
deep understanding of how Tup works and have real impacts
on Tup usage, especially on large software systems like
Firefox. It must first be noted that minimal updates and correctness
somehow conflict. Stricter notions of correctness will require
more rebuilds. While still being minimal, the build system will
rebuild much more than with other build systems, leading to
the impression that it is uselessly recompiling more parts. This
is exactly what happens with Tup.</p>
    </sec>
    <sec id="sec-5">
      <title>IV. BUILDING FIREFOX WITH TUP</title>
      <p>Tup focuses on correctness and speed, at the expense of
expressivity or usability whenever they conflict with the former
aspects. In some sense, Tup is minimalist and tries to do one
thing well: building. The configuration language of Tup is not
very expressive. It allows to specify commands, inputs and
outputs with basic support for variables and conditions. To be
fair, Lua integration is provided for more advanced features
but was not tested in this article.</p>
      <p>We will report here first on our experience with Tup,
separated in two key aspects: correctness and speed. Expressivity
will be discussed later on, in Section VI.</p>
      <sec id="sec-5-1">
        <title>A. Correctness</title>
        <p>
          Correctness is a key feature of build systems. It can be
defined roughly as the property of producing valid products
despite optimizations like incremental compilation [
          <xref ref-type="bibr" rid="ref10 ref13">10, 13</xref>
          ]. In
Tup, correctness is achieved by maintaining the state of the
build in a separate database and by tracing all the file accesses
performed by build commands.
        </p>
        <p>
          Maintaining the state of the build in an external database,
allows Tup to detect changes to the build description.
Whenever a command is added, removed or modified, Tup will
take proper actions to update the build when invoked [
          <xref ref-type="bibr" rid="ref12">12</xref>
          ]. By
design, this is difficult or impossible to achieve with stateless
build systems like Make. In particular, they cannot detect
removed commands as there is no trace of them on the next
invocation. This is not specific to Tup. Most (if not all) recent
build system implementations now rely on persistent storage.
tup error: Unspecified output files - A command is writing to files that you didn't specify in
,! the Tupfile. You should add them so tup knows what to expect.
tup error: Expected to write to file 'libxul.so' from cmd 15846 but didn't
tup error: Missing input dependency - a file was read from, and was not specified as an input
,! link for the command. This is an issue because the file was created from another command,
,! and without the input link the commands may execute out of order. You should add this file
,! as an input, since it is possible this could randomly break in the future.
        </p>
        <p>Unlike Make, Tup detects changes to the build
commands, triggering the required command invocations.</p>
        <p>By tracing file accesses of each and every command in the
build plan (a.k.a. build steps), Tup ensures that dependencies
and outputs are correctly specified. In particular, Tup will
refuse to build a command that uses undeclared dependencies.</p>
        <p>Some typical error messages can be seen on Figure 2. While
this allows to detect hidden dependencies and fix them, we
often stumbled upon this constraint during our migration
because it interrupts the build, and the Tupfiles need to be
generated again. Tup does not allow to bypass these issues.</p>
        <p>Tracing is used as enforcement of the declared dependencies
and for speed optimizations as discussed later. Tup does
not dynamically detect and change the build plan from the
collected information.</p>
        <p>For example, we hit this constraint with fake static libraries
(.a.desc files) built by Mozilla in place of the usual static
libraries (.a files) whenever the static library will only be
linked with other libraries, and not exported or otherwise
directly used. In that case, the .a.desc file contains only a list
of the objects that should be present in the archive. That list
is then parsed and the real objects fed to the linker instead of
the .a archive itself. This avoids the creation of real .a files
that can be quite time and disk-space consuming.</p>
        <p>This optimization conflicts with Tup’s need to know all the
dependencies of a command explicitly. Whenever a command
links against the .a.desc, it implicitly depends on all the objects
listed therein and this lists needs to be explicitly expanded for
Tup when generating Tupfiles.</p>
        <p>Tup strengthens the build description by detecting
undeclared and hidden dependencies. They all must
be declared in the Tupfiles or Tup will error out.</p>
        <p>In Mozilla’s implementation this is now avoided by skipping
fake libraries. They are not generated, and linkers are fed
directly the full list of objects. This is an optimization over
our version. Since the full list of objects needs to be expanded
during configuration anyway, there is no reason not to feed it
directly to the linker.</p>
        <p>In our first implementation, we avoided this issue by using
Tup groups; a feature that allows to some extent to alleviate
the need to list all the required dependencies by providing a
rough super-set of these. By adding all the objects to the same
Tup group linker invocations can depend solely on it, at the
expense of introducing stages in the build, because linking
will have to wait for all the objects to be compiled before
running. The real dependencies are then obtained at execution
by tracing the command. This ensures accurate detection of
updated inputs despite the over-approximation in the Tupfiles,
and therefore avoids pointless rebuilds of all the libraries when
a single object changes.</p>
        <p>Tup’s “groups” feature allows over-approximating the
dependencies of a build step.</p>
        <p>The technical issue of specifying all the dependencies
also arises in a other places such as with unified builds.</p>
        <p>
          The unified builds optimization works by aggregating several
C/C++ source file together. The compiler only needs to parse
the presumably large set of included headers once for the
unified set, reducing compilation time on large rebuilds and
on header changes [
          <xref ref-type="bibr" rid="ref14">14</xref>
          ]. This technique speeds up full rebuilds
significantly at the expense of slower incremental builds for
small changes. In this case, the C/C++ compiler receives a
dummy unified input file that includes the original C/C++
source files. From the point of view of Tup, the command
depends on the dummy unified source file, but also on all the
source files included therein.
        </p>
        <p>This situation highlighted for us a specificity of Tup: source
files are not required to be specified as inputs. In fact, the
set of input files is always an implicit input to all the rules.</p>
        <p>The rationale being that Tup is able to detect the sources
that are accessed by a particular command and source files
have no impact on command ordering. The execution order is
only constrained by generated files, as consumers cannot run
before producers. To relate this to groups, Tup behaves just as
if all the non-generated files belonged to a “sources” group,
automatically added as a dependency of all the commands.</p>
        <p>We experienced this style of never specyfying source files in
rules, and this results in unusual, if not confusing, rules that
have no declared inputs at all.</p>
        <p>With Tup, there is no need to specify source files as
inputs. All the commands implicitly depend on them.</p>
        <p>Tup is also quite pedantic on the outputs that a command
is allowed to produce. The command must write to all its
declared outputs, and nowhere else. The issue occurred with
Rust code. Due to a lack of integration at the moment, Rust
libraries must be compiled in a single step, and therefore
discard all outputs outside of the expected output library. This
prevents Rust from doing caching of builds, or requires to
build Rust libraries outside of the build and outside of Tup
dependency tracking. Rust also suffixes libraries with a hash
of their content. To work around these issues, we had to wrap
Rust invocations with a cleanup script that removes extra files
and renames the generated library.</p>
        <p>Tup requires commands to produce the exact list of
declared outputs, refusing commands that generate
more, less or custom files on different invocations.</p>
        <p>
          Even though these limitations sometimes prevent building
with Tup, it is still possible to trick it in several ways. First, one
can invoke some build steps before running Tup. The produced
files are seen as plain source files by Tup. It however removes
Tup’s ability to detect changes and rebuild these files. In the
absence of other change detection mechanisms, these files need
to be rebuilt on each invocation. The other option is to build
outside of the source tree, because Tup does not track files
there (unless configured to). The required outputs can then be
moved to the source tree once the command finishes. With this
technique, complex commands can maintain a cache across
builds. This is otherwise not possible with Tup constraints. The
potential pitfall is that build correctness relies completely in
the sub-command, as Tup has no knowledge of what happens
outside of it’s monitoring. Reaching that point means that Tup
gets in the way more than it should. Commands such as Rust
incovations require structural modifications to work properly
with Tup. Mozilla’s implementation was defered until they
updated the Rust toolchain to better support Tup [
          <xref ref-type="bibr" rid="ref15">15</xref>
          ]. This
was made possible because Rust already intended to support
that usage [16], but may not be feasible with other projects or
affordable to any Tup user.
        </p>
        <p>Some tools are structurally conflicting with Tup. They
have to be modified or Tup needs to be bypassed.</p>
        <p>The complications induced by the strict enforcement of
policies by Tup ultimately provide strong guarantees on the
validity of the build results. While it is possible to
inadvertently omit a dependency in Make, it is nearly imposible to
do so in Tup. This makes building with Tup more robust to
configuration errors or undetected interferences. In the long
term, the balance leans in favor of strict build systems like
Tup because migrating to Tup requires solving tricky issues
only once, while an incorrect build system can produce subtle
inconsistencies between the source code and the build products
during any build invocation.</p>
        <p>Tup’s strong correctness guarantees come at the cost
of strict constraints on invoked commands and on the
build description. There is no free lunch.</p>
      </sec>
      <sec id="sec-5-2">
        <title>B. Tup performance</title>
        <p>
          Tup uses a specific data model to achieve high speeds with
carefully crafted algorithms [
          <xref ref-type="bibr" rid="ref12">12</xref>
          ]. We first take a closer look at
the three different aspects of Tup targeting performance, and
then describe our benchmark on Firefox.
        </p>
        <p>First, Tup comes with a monitor that can listen to filesystem
events, collecting source changes on the fly. This optimization
is nowadays used by all the build systems targetting large code
bases, like Bazel, Gradle or Pants. The monitor removes the
need to walk the source tree searching for changes on each
build system invocation. This optimization is most noticeable
for builds in an up-to-date workspace (a.k.a. null builds). In
our tests, it reduced Tup execution time from one second to
less than a millisecond. This result probably also applies to
the build systems cited here-above.</p>
        <p>When everything is up-to-date and Tup’s monitor is
enabled, Tup runs in less than a millisecond.</p>
        <p>
          Also, Tup maintains the build graph in such a way that
changes and rebuilds can be propagated upwards, without
loading the whole dependency DAG [
          <xref ref-type="bibr" rid="ref12">12</xref>
          ]. Theoretically, this
makes Tup very efficient at small rebuilds because the
complexity of the algorithm depends mostly on the size of the
update, and less on the size of the whole repository as is the
case with Make.
        </p>
        <p>Finally, Tup’s build graph is enriched with exact
dependencies detected by tracing the commands durig their execution.
This ensures that build steps do not depend on files they do
not need, and that there is no spurious rebuilds when such a
file changes. That being said, tracing has also a performance
penalty, and can reduce Tup’s speed on filesystem-intensive
tasks like linking Firefox’s largest binary [17].</p>
        <p>We ran a small-scale experiment to compare build speed
with Make and Tup. We selected 47 consecutive pushes to
Firefox1 and compiled these changes incrementally in merge
order. The pushes were taken from mozilla-inbound, the
integration branch at Mozilla. A push is a set of commits
that are added together, and tested as one atomic change
by Mozilla’s continuous integration system. The number of
commits is limited to 47 because it is the largest range
of consecutive commits that appeared to build correctly in
the benchmarking enviroment. Building more commits would
have required to use different build environments, or looking
at more commit ranges. With 47 commits, we were able
to measure the duration of 46 different incremental builds.
Figure 3 shows the incremental build times of Make and Tup
1The full list of 47 pushes is accessible at https://hg.mozilla.org/integration/
mozilla-inbound/pushloghtml?startID=102811&amp;endID=102858
for each of these 47 commits. Three runs were made for each
build system to give an idea of the variance. The first measure
represents a full rebuild from clean sources, and shows how
much time is needed for a full build of Mozilla’s Firefox. With
the default Make backend, a bit more than 1h 15m are needed
to do so on our Intel Core i5-4310U which is a fairly recent
2 cores/4 threads CPU. All the builds were performed on the
same machine, with the same SSD drive.</p>
        <p>On the graph, we observe a lot of short builds, thanks to
the incremental optimizations performed by Tup and Make.
Also, the data series have the same shape, which shows that
both backends compile about the same things. That being
said, Make builds are generally slower than Tup ones on big
rebuilds. Thanks to the three measures, we can affirm that
it is no random artifact of the build environment. The most
probable cause is that the Make build performs more steps
than the Tup build. In some places, the Tup build avoids calls
to wrappers and other fixtures used by Make. But it could also
come from commands that are only described in Makefiles and
not visible to the mozbuild frontend.</p>
        <p>We get a more detailed view of Make and Tup’s relative
performance on Figure 4. Average build times with Make
(on the left) are compared with average build times with
Tup (on the right) for each of the 47 commits. The graph
is in logarithmic vertical scale, so it is difficult to get precise
values, but we see that the builds are indeed faster by a similar
proportion for most large builds, with Tup being slightly faster.</p>
        <p>Looking at smaller rebuilds, we see that they get faster with
Tup, and sometimes much faster. This high speedup arises
when Tup detects that there is nothing to do. In that case, Tup
takes one second to search for modifications (and possibly
even faster with its monitor running, as explained above), and
then stops after deciding that no modifications means nothing
to compile. Make on the other hand needs to recurse into all
the subdirectories and build the whole dependency graph to
detect the absence of anything to do. It further appears that
Make executes some build steps on each invocation, making it
impossible to measure the execution time of the Make process
itself without the tasks being run. Unconditionally running
tasks means that some were forced to run, most probably to
avoid issues with incomplete dependency handling in Make.
From that point of view, Make’s lack of correctness guarantees
translates into an execution overhead.</p>
        <p>Tup is at least as good as Make on average, and much
faster for small rebuilds.</p>
      </sec>
    </sec>
    <sec id="sec-6">
      <title>V. GRASPING MOZILLA’S BUILD DESIGN</title>
      <p>Another feature of Tup is that it allows easy extraction of
the build graph. Tup contains in its database the whole build
graph that was just built. It also provides a tool to draw the
graph.</p>
      <p>The complete graph is not easy to understand, as it contains
thousands of nodes, one for each command and for each
file. We took some steps to simplify the graph based on
command labels and relative depth in the build tree to obtain
the simplified version presented in figure 5.</p>
      <p>LINK
7 commands
7 files</p>
      <p>python
dependentlibs.list</p>
      <p>LINK
21 commands</p>
      <p>21 files</p>
      <p>AR
libxpcomglue.a</p>
      <p>LINK
3 commands
3 files</p>
      <p>AR
2 commands
2 files</p>
      <p>LINK
4 commands
4 files</p>
      <p>LINK
libnspr4.so</p>
      <p>CXX
1670 commands
1670 files</p>
      <p>RUSTC
libgkrust.a</p>
      <p>CC
1144 commands
1144 files</p>
      <p>AS
127 commands
127 files
python
1 command
2 files</p>
      <p>WebIDL
1 command
1457 files</p>
      <p>XPIDL
xptdata.cpp</p>
      <p>AS
4 commands</p>
      <p>4 files
python
1 command
2 files</p>
      <p>python
5 commands
11 files</p>
      <p>XPIDL
152 commands
2633 files</p>
      <p>IPDL
1 command
966 files
preprocess
109 commands
109 files</p>
      <p>python
70 commands
1261 files
sources
247373 files</p>
      <p>Tup stores the whole build plan (the build graph) in
a database in an explicit form that can be queried
easily.</p>
      <p>It is interesting to discover that the build can be summarized Fig. 6. Size of the main parts of our workspace after a complete build with
in a handful of nodes, and divides itself naturally in stages. Tup in a clean checkout.</p>
      <p>There is a preprocessing and generation stage where symlinks
are created, where files are generated with python and other
custom transformations happen. Afterwards, we observe the stage, where shared objects depend on other shared objects.
classical two-stage process of compiling (AS, CC, CXX) to The graphs could be improved through better projections and
object files, and then linking into programs, static libs and simplifications. We intend to work further on these aspects to
shared libs (LINK and AR). The ultimate stage is an oddity at improve on this work and generalize our results.
Mozilla, where a dependentlibs.list is generated that lists all Extracting that graph would not have been easy with
the libraries that libxul.so depends upon. This file is used to recursive Makefiles. There have been attempts at extracting
preload all these libraries at startup, possibly for performance Makefile-based build system structure but they require to
reasons. actually run Make in verbose debug mode and parse its</p>
      <p>This graph was the best result obtained after several at- output [18]. Tup provides a much more reliable way to access
tempts. It groups commands by the maximal depth of their that graph.
inputs. Files are merged with the command that produces Concerning that graph, it is worth noting that it requires
them. This kind of transformation does not preserve the a large amount of disk space. On our machine, after a fresh
acyclicity of the graph, as a loop appears in the linking build of Firefox, we observed the memory usage described in</p>
      <p>VI. FIREFOX AS A BENCHMARK FOR BUILD SYSTEMS</p>
      <p>As was already hinted before, Firefox is our candidate
benchmark for comparing build systems. In this section, we
explain in further details the structure of Mozilla’s build
system and the features that catched our attention while looking
for a benchmarking project for build systems.</p>
      <p>Mozilla’s build system is based on simplified python files
named moz.build. Examples of these files can be seen in
Figure 7 and Figure 8. They list the source files that must
be compiled, the name of the libraries or programs that must
be produced and various options like compiler flags, headers
to be exported and such. The ad-hoc nature of this data
format allows Mozilla to encode other kind of information
like in Figure 8 where information allows to relate files in the
source tree with Firefox components. That example also shows
with Files('**'):</p>
      <p>BUG_COMPONENT = ('Toolkit', 'App Update')
DIRS += ['src']
if CONFIG['MOZ_ENABLE_SIGNMAR']:</p>
      <p>DIRS += ['sign', 'verify']</p>
      <p>TEST_DIRS += ['tests']
elif CONFIG['MOZ_VERIFY_MAR_SIGNATURE']:</p>
      <p>DIRS += ['verify']
# If we are building ./sign and ./verify,
# then ./tool must come after it
DIRS += ['tool']
Fig. 8. A more advanced moz.build file, with conditionals, subdirectories and
bindings between source files and Firefox modules.
that the build options can depend on configuration values
passed in the CONFIG dictionary. These values are computed
at configuration time, and allow tweaking the build depending
on the platform and various other parameters.</p>
      <p>
        The Python format used in moz.build files was picked
because it is easy to read and Python is well known among
developers, at Mozilla and in general [
        <xref ref-type="bibr" rid="ref2">2</xref>
        ]. This input format is
parsed by the mozbuild toolchain that generates the right build
files according to the selected backend (Makefiles for make,
etc.) but it can also perform some complex operations on its
own. For example, the backend generates boilerplate unified
C++ files, and lists of inputs in separate files to circumvent
command-line length limits. The Tup backend performs more
operations to work around Tup limitations. For example, it
parses some input files to pre-compute the output file names
that cannot be simply deduced. We call this step preprocessing,
and distinguish it from the parsing and generation done by
mozbuild. Preprocessing is mostly needed to work around
limitations and restrictions in the build backend.
      </p>
      <p>With its custom build description format in Python, Mozilla
is independent from a build system in particular. Because
Python is a widely known and general purpose programming
language, it is possible to adapt it to multiple purposes and
in particular to generate instructions for different backends as
we did with Tup. As more backends are added, the difficulty
to generalize the mozbuild pipeline should lower. Also, in the
process of implementing a backend for a build system, features
and limitations of said build system appear more clearly and
can be collated. With several build systems executing on the
same code base, it becomes possible to produce the meaningful
comparisons that are not yet available. Our future work will
therefore target more build systems and will try to elicit
distinctive features for each of them.</p>
      <p>Mozilla’s custom build framework can be easily
adapted to different build systems, and could be used
to compare them.</p>
      <p>VII. RELATED WORK</p>
      <p>
        Andrey Mokhov et Al. distinguish different kinds of build
systems [
        <xref ref-type="bibr" rid="ref13">13</xref>
        ]. In Mozilla at the moment, there is a recursive
memo/dumb configuration and preprocessing phase. This one
is followed by a minimalist build system in the common and
strict sense. While not perfect, combining different kinds of
build systems provides the ability to work now, and to be
perfectible later. This is the approach used by Mozilla in their
build system, and makes for a simplified migration, as it allows
maintaining two build systems up-to-date side-by-side.
      </p>
      <p>Gligoric et Al. worked on migrating from one build system
to another [19]. They extracted build traces from the old one
and populated the new build system by factoring out common
patterns. Reusing this approach would not take advantage of
the existing moz.build architecture used at Mozilla. There is
no need to extract traces when the graph is available.</p>
    </sec>
    <sec id="sec-7">
      <title>VIII. REPRODUCIBILITY</title>
      <p>This work was performed with the intent to make it fully
reproducible. The development environment was managed
with the Nix package manager in order to allow a completely
reproducible test environment [20]. All the work is available at
https://github.com/layus/gecko-tup. The results presented here
are all reproducible from the tools provided there.</p>
      <p>This also means that our tests were performed within Nix2,
which imposes some strong constraints on the usual Linux
builds. Some patches were introduced to circumvent these
constraints. This does not invalidate the final results because
Firefox was compiled just fine. The issues were mostly with
passing custom library paths to a build system that assumes
that all the system libraries comply to Filesystem Hierarchy
Standard (FHS) [21], which is not the case with Nix.</p>
    </sec>
    <sec id="sec-8">
      <title>IX. CONCLUSION</title>
      <p>We have conducted an experiment consisting in building
Firefox with Tup. It has shown that Tup is a valid replacement
for Make on this large real-life software project. While Tup
does not generally bring a significant speedup at building,
its strength resides in correctness and enforcement of build
specifications. We argued that it may be a good investment
in the long term, despite the significant refactoring that its
introduction requires. Tup is however more efficient than Make
at compiling small changesets. It could spare compilation time
if such rebuilds are frequent, as could be expected during
development on a developer’s machine. Beyond its usage as a
build system, Tup also provides a straightforward data model
that allows in-depth inspection of the build. In the future, we
intend to reuse the knowledge and tooling presented here with
Tup to compare more build systems, and look for relevant
metrics to classify them.</p>
    </sec>
    <sec id="sec-9">
      <title>2https://nixos.org/nix/</title>
    </sec>
    <sec id="sec-10">
      <title>X. ACKNOWLEDGMENTS</title>
      <p>The authors would like to thank Mike Shal for sharing
knowledge about both Tup’s and Mozilla’s build systems as
well as for his careful review of a previous version of this
article. Many thanks too to Marie Champenois for producing
a better looking Figure 1.
[16] Support Producing a ”Build Plan” without Executing
Anything · Issue #3815 · Rust-Lang/Cargo. URL: https:
/ / github. com / rust - lang / cargo / issues / 3815 (visited on
10/18/2018).
[17] Mike Shal. Linking Libxul with Tup and FUSE. Apr.
2013. URL: http://gittup.org/blog/2013/04/3-
linkinglibxul-with-tup-and-fuse/.
[18] Bram Adams et al. “Makao”. In: Software Maintenance,
2007. ICSM 2007. IEEE International Conference On.</p>
      <p>IEEE, 2007, pp. 517–518.
[19] Milos Gligoric et al. “Automated Migration of Build
Scripts Using Dynamic Analysis and Search-Based
Refactoring”. In: ACM SIGPLAN Notices 49 (Dec.
2014), pp. 599–616.
[20] Rok Garbas. Reproducible Development Environments.</p>
      <p>Sept. 2015. URL:
https://garbas.si/2015/reproducibledevelopment-environments.html.
[21] LSB Workgroup, The Linux Foundation. Filesystem
Hierarchy Standard, Version 3.0. Mar. 19, 2015. URL:
http://refspecs.linuxfoundation.org/FHS 3.0/fhs/index.
html.</p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          [1]
          <string-name>
            <given-names>Gregory</given-names>
            <surname>Szorc</surname>
          </string-name>
          .
          <article-title>Moving Away from Makefile's. E-mail</article-title>
          .
          <source>Aug</source>
          .
          <volume>22</volume>
          ,
          <year>2012</year>
          . URL: https://groups.google.com/forum/ #!topic/mozilla.dev.platform/SACOnl-avMs/discussion.
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          [2]
          <string-name>
            <given-names>Gregory</given-names>
            <surname>Szorc</surname>
          </string-name>
          . Moz.
          <article-title>Build Files and the Firefox Build System</article-title>
          .
          <source>Feb</source>
          .
          <year>2013</year>
          . URL: https://gregoryszorc.com/blog/ 2013 / 02 / 28 / moz . build - files
          <string-name>
            <surname>-</surname>
          </string-name>
          and
          <string-name>
            <surname>-</surname>
          </string-name>
          the
          <string-name>
            <surname>-</surname>
          </string-name>
          firefox
          <string-name>
            <surname>-</surname>
          </string-name>
          build - system/.
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          [3]
          <string-name>
            <given-names>The</given-names>
            <surname>Clang Team. JSON Compilation</surname>
          </string-name>
          <article-title>Database Format Specification</article-title>
          . URL: https : / / clang . llvm . org / docs / JSONCompilationDatabase . html (visited on 09/21/
          <year>2018</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          [4]
          <string-name>
            <given-names>Joshua</given-names>
            <surname>Cranmer</surname>
          </string-name>
          .
          <article-title>Mach Command to Output Clang Compilation Database</article-title>
          . URL: https : / / bugzilla . mozilla . org/show bug.
          <source>cgi?id=904572 (visited on 09/21/</source>
          <year>2018</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          [5]
          <string-name>
            <given-names>Ali</given-names>
            <surname>Almossawi</surname>
          </string-name>
          .
          <source>How Maintainable Is the Firefox Codebase? May</source>
          <volume>15</volume>
          ,
          <year>2013</year>
          . URL: http : / / almossawi . com / firefox/prose (visited on 10/05/
          <year>2018</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          [6]
          <string-name>
            <given-names>The</given-names>
            <surname>Mozilla Firefox Open Source</surname>
          </string-name>
          <article-title>Project on Open Hub: Languages Page</article-title>
          . URL: https : / / www. openhub . net / p / firefox/analyses/latest/languages summary (visited on 10/05/
          <year>2018</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref7">
        <mixed-citation>
          [7]
          <string-name>
            <surname>Stuart</surname>
            <given-names>I. Feldman.</given-names>
          </string-name>
          “
          <article-title>Make - a Program for Maintaining Computer Programs”</article-title>
          .
          <source>In: Software: Practice and Experience 9.4 (Apr. 1</source>
          ,
          <year>1979</year>
          ), pp.
          <fpage>255</fpage>
          -
          <lpage>265</lpage>
          . ISSN:
          <fpage>1097</fpage>
          -
          <lpage>024X</lpage>
          . DOI:
          <volume>10</volume>
          .1002/spe.4380090402.
        </mixed-citation>
      </ref>
      <ref id="ref8">
        <mixed-citation>
          [8]
          <string-name>
            <surname>Richard</surname>
            <given-names>M.</given-names>
          </string-name>
          <string-name>
            <surname>Stallman</surname>
          </string-name>
          and
          <string-name>
            <surname>Roland McGrath. GNU Make - A Program for Directing Recompilation</surname>
          </string-name>
          .
          <year>1991</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref9">
        <mixed-citation>
          [9]
          <string-name>
            <given-names>Peter</given-names>
            <surname>Miller</surname>
          </string-name>
          .
          <article-title>“Recursive Make Considered Harmful”</article-title>
          .
          <source>In: AUUGN</source>
          . Vol.
          <volume>19</volume>
          .
          <string-name>
            <surname>AUUGN</surname>
          </string-name>
          <article-title>Journal of AUUG Inc 1</article-title>
          . AUUG, Inc.,
          <string-name>
            <surname>Feb</surname>
          </string-name>
          .
          <year>1998</year>
          , pp.
          <fpage>14</fpage>
          -
          <lpage>25</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref10">
        <mixed-citation>
          [10]
          <string-name>
            <given-names>Guillaume</given-names>
            <surname>Maudoux</surname>
          </string-name>
          and
          <string-name>
            <given-names>Kim</given-names>
            <surname>Mens</surname>
          </string-name>
          . “Correct, Efficient, and
          <article-title>Tailored: The Future of Build Systems”</article-title>
          .
          <source>In: IEEE Software 35.2</source>
          (
          <issue>2018</issue>
          ), pp.
          <fpage>32</fpage>
          -
          <lpage>37</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref11">
        <mixed-citation>
          [11]
          <string-name>
            <given-names>Mike</given-names>
            <surname>Shal</surname>
          </string-name>
          .
          <article-title>Tup Build System</article-title>
          . URL: http://gittup.org/ tup/index.html (visited on 05/15/
          <year>2018</year>
          ).
        </mixed-citation>
      </ref>
      <ref id="ref12">
        <mixed-citation>
          [12]
          <string-name>
            <given-names>Mike</given-names>
            <surname>Shal</surname>
          </string-name>
          .
          <source>Build System Rules and Algorithms</source>
          .
          <year>2009</year>
          . URL: http : / / gittup . org
          <article-title>/ tup / build system rules and algorithms</article-title>
          .pdf.
        </mixed-citation>
      </ref>
      <ref id="ref13">
        <mixed-citation>
          [13]
          <string-name>
            <surname>Andrey</surname>
            <given-names>Mokhov</given-names>
          </string-name>
          , Neil Mitchell, and Simon Peyton Jones. “
          <article-title>Build Systems a` La Carte”</article-title>
          . In: ICFP (
          <year>2018</year>
          ). URL: https : / / www . microsoft . com / en - us / research / uploads/prod/2018/03/build-systems.pdf.
        </mixed-citation>
      </ref>
      <ref id="ref14">
        <mixed-citation>
          [14]
          <string-name>
            <given-names>Ehsan</given-names>
            <surname>Akhgari</surname>
          </string-name>
          .
          <source>Unified Builds. Nov</source>
          .
          <year>2013</year>
          . URL: https: / / lists . mozilla . org / pipermail / dev - platform / 2013 - November/
          <year>001998</year>
          .html.
        </mixed-citation>
      </ref>
      <ref id="ref15">
        <mixed-citation>
          [15]
          <string-name>
            <surname>Add -</surname>
          </string-name>
          Build-Plan for 'cargo Build' by Mshal · Pull Request #
          <volume>5301</volume>
          ·
          <string-name>
            <surname>Rust-Lang</surname>
          </string-name>
          /Cargo. URL: https://github. com/rust-lang/cargo/pull/5301 (visited on 10/18/
          <year>2018</year>
          ).
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>