moz.build Files and the Firefox Build System

February 28, 2013 at 07:45 PM | categories: Mozilla, Firefox, build system | View Comments

The next time you update mozilla-central you may notice some significant changes with the build system. That's because this morning we finally landed the start of a massive overhaul of the build system! There are many end goals to this effort. The important ones for most will be faster build times and a build system that is friendlier to make changes to.

Introducing moz.build Files

If you look in the tree, you'll notice that nearly every directory now has a moz.build file.

moz.build files are what we are using to define the build system. Think of them each as a descriptor that describes how to build its own part of the tree. An individual moz.build file will contain the C++ sources to compile, the headers to export, the tests to run, etc. Eventually. Currently, they are limited to directory traversal information.

moz.build files essentially add a level of indirection between the build system definition and how the tree is actually built. Before moz.build files, the same metadata we are now capturing in moz.build files (or plan to capture) was captured in Makefile.in files. We performed simple variable substitution on these Makefile.in files to produce Makefile files in the object directory. These Makefile files were used by GNU Make (or Pymake on Windows) to build the tree.

As I outlined in Improving Mozilla's Build System, Makefile.in are suboptimal for a number of reasons. The important bit is they essentially tie us to the use of make (recursive or otherwise). We are very interested in supporting modern build systems such as Tup (the theory being they will build the tree faster).

Enter moz.build files. Storing our build configuration in moz.build files allows us to decouple the definition of the build system from the tool used to build it.

How moz.build Files Work

At the tail end of configure, the build system invokes the config.status script in the object directory. The role of config.status is to combine the information collected during configure with the build configuration obtained from moz.build files and take the necessary actions to ensure the build backend (make) can build the tree.

Before today, config.status essentially iterated over the source tree and converted Makefile.in files to Makefile in the object directory. Things are now slightly more complicated with moz.build files.

When config.status runs, it starts with the root moz.build from the source tree. It feeds this file into a Python interpreter. It then looks for special variables like DIRS and PARALLEL_DIRS to determine which directories contain additional moz.build files. It then descends into all the referenced directories, reading their moz.build files. While this is happening, we are converting the results of each moz.build file execution into backend.mk files that make knows how to build. It also performs the Makefile.in to Makefile conversion like it always has. When the whole process has finished, the object directory contains a bunch of Makefile and backend.mk files. make runs like it always has. The only real difference is some variables are coming from the moz.build-derived backend.mk files instead of Makefile.

This is just a brief overview, of course. If you want to know more, see the code in /python/mozbuild/mozbuild/frontend and /python/mozbuild/mozbuild/backend.

Build System Future

With the introduction of moz.build files, the intent is to eventually completely eliminate Makefile.in and have all the build definition live in moz.build files.

Doing this all at once would have been next to impossible. So, we decided to eliminate Makefile.in gradually. The first step is what landed today: essentially moving DIRS variables out of Makefile.in and into moz.build files. Next, we will be eliminating empty Makefile.in (bug 844635) and will be moving more parts of the build definition from Makefile.in to moz.build files. The next part to move over will likely be IDLs (bug 818246). After that, it may be exported files (EXPORTS in Makefile.in parlance). And repeat until we have no more Makefile.in in the tree.

Each migration of build definition data to moz.build files will likely occur in two phases:

  1. A largely mechanical move of the variables from Makefile.in to moz.build.
  2. Better build backend integration resulting from the move.

In phase #1, we will essentially cut and paste variable assignments to moz.build files. make will read the same variables it does today and perform the same actions. The only difference is the values in these variables will be defined in moz.build files.

In phase #2, we will leverage the fact that our build definition now has an API. We will change our existing make backend to be more efficient. For example, we should soon be able to compile IDLs and copy exported headers without make traversing through the directory tree at build time. We will be able to do this because the moz.build traversal at pre-build time sees data from all moz.build files and with this complete world view is able to produce more optimal make files than what you would get if you recursed into multiple directories. In short: it will make the build faster.

Once we have a sufficient portion of the build definition moved to moz.build files we will be able to experiment with new build backends (like Tup), look into automatic Visual Studio project generation, and more easily experiment with different ways of building (such as precompiled headers, fewer compiler process invocations, etc). These should all contribute to faster build times.

Frequently Asked Questions

What impact will I see from this change?

If you never touched Makefile.in files in the tree, you should not notice anything different about how the tree builds or how the build system works. You should have nothing to fear.

The most obvious changes to the source tree are:

  1. There is a moz.build file in almost every directory now.
  2. The variables related to directory traversal (those containing DIRS in their name) are now defined in moz.build files instead of Makefile.in.
  3. If your Makefile.in contains a variable that has been moved to moz.build files, make will spew an error when processing that file and the build will fail.

Will this change how I build?

It shouldn't. You should build the tree just like you always have. Most of you won't notice any differences.

If you see something weird, speak up in #build or file a bug if you are really confident it is wrong.

What are the risks to this change?

The migration of variables from Makefile.in to moz.build files is largely mechanical and significant portions are done manually. This can be a mind-numbing and tedious process. Not helping things is the fact that Splinter's review interface for these kinds of patches is hard to read.

This all means that there is a non-trivial risk for transcription errors. All it takes is an inverted conditional block and all of a sudden large parts of the tree are no longer built, for example.

We have established bug 846825 to investigate any oddities from the initial transfer. Developers are encouraged to help with the effort. Please spot check that your directories are still being built, tests run, etc. Pay special attention to changes made in the last 4 months as these parts of Makefile.in would have been bit rotted and more prone to data loss.

Parts of the tree not enabled in standard configurations are more prone to breakage due to less testing. i.e. build configurations not captured by TBPL have a higher chance of breaking.

Will this make the tree build faster?

Not yet. But eventually it will. This initial landing paves the groundwork to making the tree build faster (see the Future section above).

I see a lot of empty moz.build files!

Yes. Sorry about that. The good news is they shouldn't be empty for long. As things move from Makefile.in to moz.build we'll see fewer and fewer empty moz.build files. We'll also see fewer and fewer Makefile.in files once we start deleting empty Makefile.in.

If you want to know why we must have empty files, it's mainly for validation. If we allowed moz.build files to be optional, how would you detect a typo in a directory name? Directory exists? What if that directory exists but isn't supposed to have a moz.build file?

You bitrotted my patches!

Yes. I'm sorry. The transition period to moz.build files could be a little messy. There will be lots of changes to Makefile.in and moz.build files and lots of chances for bit rot. Uplifts could be especially nasty. (Although I don't believe many uplifts involve significant changes to the build configuration.)

This all means there is a strong incentive for us to complete the transition as quickly as possible.

Can I help with the transition to moz.build files?

Yes!

The transition is largely mechanical (at least phase #1). If you are interested in moving a variable or set of variables, hop in #build on IRC and speak up!

You said moz.build files are actually Python files?!

Yes they are! However, they are executed in a very tightly controlled sandbox. You can't import modules, open files, etc. UPPERCASE variable names are reserved and only a few functions are exposed. If you attempt to assign to an unknown UPPERCASE variable or assign an invalid value, an error will occur. This is already much better than Makefile because we can now detect errors earlier in the build process (rather than 15 minutes into a build).

What variables and functions are available in moz.build files?

If you run |./mach mozbuild-reference| you will see a print-out of all the variables, functions, and symbols that are exposed to the Python sandbox that moz.build files execute in. There are even tests that will fail the build if the sandbox contains symbols not in this output!

The output should be valid reSTructuredText (in case you want to convert to HTML for reading in your browser).

What if a moz.build file contains an error?

The build will break.

A lot of work has gone into making the output of moz.build errors human friendly and actionable. If you do something wrong, it won't just complain: it will tell you how to fix it!

Besides build times, how else will this improve the build system?

There are several ways!

As mentioned above, moz.build are more strict about what data is allowed to be defined. If you assign to an UPPERCASE variable, that variable must be known to the sandbox or else the assignment will error. This means that if you assign to an UPPERCASE variable, you know it has a side-effect. No more cargo culting of old, meaningless variables!

To change the behavior of moz.build files (add new variables or functions, change how makefile generation works, etc) will require changes to the code in /python/mozbuild. This code belongs squarely to the build module and requires appropriate review. A problem with Makefiles is that they have lots of foot guns by default and its easy for self-inflicted wounds to land in the tree without explicit build peer review. This problem largely goes away with moz.build files because the sandbox takes away all of make's foot guns.

The output of a moz.build execution is essentially a static data structure. It's easy to validate them for conformance. If we discover bad practices in our build definition, we can centrally add tests for them and enforce best practices.

We will also see user experience wins by moving data to moz.build files. Take mochitests for an example. We currently have several flavors (plain, browser, chrome, etc). Sometimes you cannot distinguish the flavor by the filename alone. With moz.build files, it will be easier to answer questions like "what mochitest flavor is this file?" mach could hook into this so you can type |mach mochitest path/to/file.html| instead of |mach mochitest-plain path/to/file.html|. Even better, you should just be able to type |mach path/to/test.html| and mach knows from the build definition that path/to/test.html is a plain mochitest file and assumes you want to run it. There are dozens of small development workflow wins to be gained here!

If I change a moz.build file, what happens?

If you change a moz.build file, then make should detect that it has changed and it will update the dynamically generated backend.mk file and reinvoke the requested build action. This should all happen automatically (just like Makefile.in to Makefile conversion works automatically).

My build seems to pause for a few seconds before starting!

A change to any moz.build file will cause a full traversal of the entire moz.build tree. On modern machines, this should only take 1-3 seconds. If your source tree is not in the page cache (and you need to load moz.build files from disk) or if you are on older hardware, this could be a little slower.

This is an unfortunate side-effect of having a whole world view of the build definition. The build delay incurred by these full scans should eventually be cancelled out by build backend optimizations resulting from having this whole world view, however.

The good news is this full scan should only occur if a mozbuild file changes. And, if you are performing make recursion, it should only happen once (not in every directory). If you notice multiple moz.build scanning-related pauses during builds, please file a bug in Core :: Build Config!

Finally, we are performing the reads on a single thread currently. We can throw more cores at the task if someone codes up a patch.

What happened to allmakefiles.sh?

It has been sacked. allmakefiles.sh was an optimization to perform all the Makefile.in to Makefile conversion in one go. The directory traversal performed by moz.build reading effectively replaces the role of allmakefiles.sh. Not only that, but the moz.build build definition is always up to date! allmakefiles.sh was typically out of sync with reality and was a burden to maintain.

Did we just invent our own build system?

Kinda. We invented a generic Python sandboxing infrastructure. Then we hooked up code to populate it with variables from our build system and told it how to perform file traversal by reading specific variables set during file execution. Then we hooked up code for taking the evaluated results of all those sandboxes and convert them into make files.

Conceptually, what we invented is like GYP but with a different config file format. We have dabbled with the idea of converting the parsed build definition into GYP classes and then leveraging GYP to produce Makefiles, Ninja files, Visual Studio Projects, etc. This would an interesting experiment!

If you want to call it a build system, call it a build system. However, it is currently tightly coupled to Mozilla's needs, so you can't just use it anywhere. The concept might be worth exploring, however.

Is there anything else you'd like to share?

I think we set the record for most parts in a bug: 61. Although, they are numbered 1-17, 19-20. Part 18 has 30+ sub-parts using letters from the English and Greek alphabet for identifiers. Part 61 uses the infinity symbol as its number. See the pushlog.

Finally, I'd like to thank everyone who helped with this effort. The bug itself was only 6 months old and had active development off and on for a lot of it. Ted Mielczarek and Mike Hommey provided invaluable feedback on the core build system patches. A number of module owners stepped in to lend eyes to the mechanical conversion of their files. Last but not least, Ms2ger provided invaluable review aid on many of the patches. The work was so good that we all agreed that an Ms2ger f+ was good enough for a build peer rs! If reviewing the patches wasn't enough, Ms2ger also oversaw the tree closure and merging of the landing. I don't know how I will repay this debt.

Any more questions?

If you have more questions, drop in #build on irc.mozilla.org and ask away.

Read and Post Comments

Changes to How mach Loads mozconfigs

December 05, 2012 at 10:30 AM | categories: Mozilla, Firefox | View Comments

If you use mach to build Firefox, there is a chance you may run into some errors from a change that landed on mozilla-central Wednesday.

I reimplemented the mozconfig finding and loading logic in Python and with a nice shell wrapper that lets us more easily inspect side-effects of execution. The overall goal is to speed up mozconfig loading (fewer shell invocations) as well as to have a strong API for interacting with mozconfigs so we can do things like better validation. As a side effect, we now have test coverage of mozconfig behavior!

Anyway, because the logic for mozconfig finding and loading is going through a completely new mechanism, you may notice some weird behavior. Until a few minutes ago, $topsrcdir wasn't defined in the execution environment. This is now fixed. I think most complaints stemmed from this.

Another significant change is that mozconfig shell scripts are now executed in a shell that has set -e. This means that failing commands in your mozconfig will now abort execution. Before, execution would have silently continued unless you checked the exit code in your mozconfig. I think this is a change for the better, but it may break your mozconfigs.

If you encounter an issue, please follow the instructions in mach's output to file a bug. Please attach or link to your mozconfig.

Read and Post Comments

Firefox Build System Presentation

November 30, 2012 at 02:00 PM | categories: Mozilla, Firefox, build system | View Comments

In case you missed it, I gave a presentation on the state of Firefox's build system yesterday.

You can watch it and view the slides online.

If you build Firefox from source regularly, you should definitely at least skim through the slide deck.

I'm not an HTML expert, so my apogolies for bad UI on the interactive slides. You may need to press enter to select items in dropdown menus. Also, the interactive slides are a bit resource intensive. If the slide deck is really slow, turn off those elements. I've also only tested the slides in Firefox 19 and 20. My apologies if they don't work everywhere.

Read and Post Comments

Mach Has Landed

September 26, 2012 at 05:30 PM | categories: Mozilla, Firefox | View Comments

Hacking on Firefox is hard. Compiling the source tree for the first time is a daunting experience. If you manage to do that, you still need to figure out how to submit patches, write and run tests, etc. There is no shortage of points where people can get confused, make mistakes, or just give up because the barrier is too high.

I have strong convictions about making the overall developer experience better (developers are users too, after all). The easier you make the developer experience, the greater the chances of retaining developers. And retaining developers means more contributions: more features and fewer bugs. This translates to a better state for the Mozilla Project. This makes the world a better place.

Since practically my first day working at Mozilla, I've been experimenting with ways to make contributing to Firefox easier by improving the build system or interaction with it.

With a lot of help, I've finally succeeded in landing something into the Firefox source tree that I think will ultimately lead to a much better developer experience.

It's called mach (German for do) and if you pull the latest version of mozilla-central (Firefox's main source code repository), you can run mach today.

Running Mach

You can run Mach by simply executing it from the root directory in the repository:

$ ./mach

Ideally, I shouldn't have to tell you anything else: mach's output should provide you all the instruction you need to use it. If it doesn't, that is a bug and it should be fixed.

Realistically, mach is still in early development and its user experience still leaves a lot to be desired.

Because technical usage docs belong in a medium that is easily discoverable and where the community can improve on them (not a post on a personal blog), you can find additional usage information in the mach article on MDN. The mach README holds more technical information for people wanting to poke at the inner workings.

Mach does require Python 2.7. The build system will likely soon require Python 2.7 as well. So, if you don't have Python 2.7, you should upgrade now before you lose the ability to build the tree. Conveniently, the tree now has a bootstrap script which covers the installation of Python. So, Python 2.7 should just be a simple command away.

Features

Why would you use mach? Good question! Compared to the existing out-of-the-box experience, mach adds:

  • Ability to run xpcshell and mochitest tests from the source directory. This means you can tab-complete test filenames in your shell and it just works.
  • Parsing of compiler warnings (currently supports Clang 3.1 and MSVC formats) into a unified warnings database (actually a JSON file). After builds you can run ./mach warnings-list or ./mach warnings-summary to get a concise list without having to look at build logs.
  • A single command-line interface where you can easily discover new functionality. Just run ./mach help (try doing that with make!).

Naysayers will say this is a paltry list. They are correct. I have bigger plans. But, you have to start somewhere.

Goals and Future Ideas

The overall goal of mach is to improve the developer experience of developing Firefox and other Gecko applications. It does this by providing a convenient, central command in the root directory of the repository that acts as both an oracle to discover new commands and functionality (./mach help) as well as a proxy to execute them. You don't need to worry about environment variables, working directories, or finding some random script hidden in the bowells of the source tree. You just run a single command and the world is brought to you. No build documentation. No outdated wikis. No copying commands into your shell. No having to scour random blogs for useful tools. You just clone the repository, run a command, see what you can do, and get stuff done. Your shell-literate grandmother could do it.

Mach should be your springboard to in-tree developer tools and increased productivity. You shouldn't need anything except a copy of the source tree and mach.

Like Git and Mercurial, mach is powered by the concept of sub-commands/actions. So, one simply needs to register a new sub-command with the mach driver. This involves writing a Python class. Once you do that, people can discover and use that functionality. All batteries are included with a copy of mozilla-central.

As stated above, the current set of commands is rather small. But, the sky is the limit. Here are some of my ideas:

  • Ability to upload, download, and apply patches from Bugzilla (Burak Yiğit Kaya, Jeff Hammel, Clint Talbert and I have already talked about this -- progress tracked in bug 774141).
  • Automatically configure Mercurial with optimal settings (ensure user info is set, proper lines of diff context, enable mqext, etc). Tracked bug 794580.
  • Submit Try builds. The trychooser Mercurial extension could easily live as a mach subcommand! Tracked in bug 774137.
  • Identify Bugzilla components and/or reviewers from files touched by patch. It's all in the history of the touched files and the history of the old bugs referenced by those commits! Bug 774145.
  • Interaction with the self-serve build API. That thing on TBPL to control individual builds - we could make a CLI interface for it. (Steve Fink and/or Jeff Hammel already have code for this - it just needs to be integrated). Bug 774147.

If you have an idea for a feature, please file a bug. Please note there are many features on file already. However, some obvious ones such as integration with missing test suites have yet to be filed (at least at the time I wrote this post).

If you wrote an awesome developer tool and would like others to use it (without having to rely on people discovering it by reading a corner of the Internet), add it to mach! Use mach as a wedge to get more exposure and users. File a bug. I will happily r+ patches that add useful developer functionality to the tree.

What this Means / Longer Term Future

While there is no timetable, mach will eventually replace client.mk. client.mk, like mach, is essentially a CLI driver for the build system. Having the driver implemented in Python rather than make has many compelling advantages. I could probably write a whole post on it, but I'll spare the world from my ranting.

Anyway, this all means that you may want to start re-training your muscle memory now. Stop typing make and start typing mach. If you need to type make because mach does not provide a feature, this is a missing feature from mach. File a bug and request a new mach feature!

I want to condition people to stop typing make, especially in the object directory. There are drastic changes to the build system in the works (bug 784841 is the tip of the iceburg). These changes will require the build system to be treated as a black box. So, things like invoking make from the object directory will eventually break. You'll need to go through an intelligent tool to invoke the build system. Mach will be that tool.

Thanks

I would like to single out the following individuals for helping to land mach:

  • Jeff Hammel for doing the bulk of the reviewing. He shares my vision for mach and how it will make the overall developer experience much more pleasant and how this will translate to better things for The Project. In my mind, Jeff deserves as much credit for landing mach as I do.
  • Mike Hommey and Ms2ger for review help. Mike Hommey helped identify a lot of issues with build system integration. Ms2ger provided lots of general feedback on Python issues and API design.
  • Mike Connor (my manager) for allowing me to work on this. It's not related to my job description in any way so he could say I shouldn't be spending time on this. But, he realizes the positive impact this can have and has been supportive of it.

I hope you find mach useful!

Read and Post Comments

Bootstrap Your System to Build Firefox

September 18, 2012 at 05:00 PM | categories: Mozilla, Firefox | View Comments

If you've looked at the build instructions for Firefox lately, you may have noticed something new: support for system bootstrapping!

Now checked in to mozilla-central is a framework for ensuring your system is capable of building mozilla-central and Firefox. You just need to download and run a single Python script and it performs magic.

Kudos go out to a community contributor, kmm (name wasn't revealed) for doing the legwork for tracking down and verifying things worked on all the Linux distros. Richard Newman and Jeff Hammel also helped with code reviews. Just hours after it landed, Landry Breuil contributed support for OpenBSD!

Currently, bootstrapping works for the following:

  • Ubuntu Linux
  • Mint
  • CentOS 6.x
  • Fedora
  • OS X 10.6, 10.7, and 10.8
  • OpenBSD

If you want to add support for an additional OS, please file a Core : Build Config bug. Likewise, if you encounter an issue, please file a bug so others don't get tripped up by it!

Bootstrap support is definitely in its infancy. It still needs features like better prompting and opportunities for user choice (e.g. support MacPorts on OS X - currently it only works with Homebrew). But, I think it is much better than what existed previously, especially on OS X.

I consider this bootstrapping component just one piece in a larger mission to make developing and building Firefox (and other Gecko applications) easier. This should (hopefully) lead to more development involvement. The next component to land will likely be mach. It's (finally) been getting some review love (thanks Jeff Hammel!), so I'm optimistic it will land in the next few weeks.

Read and Post Comments

« Previous Page -- Next Page »