@ncalexander

Smoother Landings: How to update and test Fennec feature build flags

Fri 13 February 2015 / tagged: android, build system, fennec

First, have you read my earlier blog post Bumpy Landings? No? Go read it now! I’ll wait.

Good, you’ve read Bumpy Landings, so you’re familiar with adding and updating Fennec build flags.

The first major point: changing build settings — your mozconfig file, the configure.in script, sundry .m4 files, application-specific configuration files like mobile/android/confvars.sh — requires re-configuration to recognize your changes. You need to run:

mach configure

Unfortunately, after re-configuration, you really need to rebuild your entire tree. The issue is that mach configure updates a variety of build settings and information (including your feature flag) and the build system does not track, at a fine-grained level, what is the minimal set of things that need to rebuilt when such settings change. Hence, you need to rebuild everything to be safe, which is termed a clobber build.

The good news is that you can avoid clobber builds in some situations. First, let’s see how to find the value of a build flag. Then we’ll see how to update a value and do a limited re-build of the impacted source code.

Interrogating the build settings

The build system maintains the settings you see in Makefile.in and moz.build files in $OBJDIR/config.status. The only time that config.status is written is by mach configure (which may, on occasion, be triggered by other mach commands, including mach build). So to verify that your config.status is up-to-date, you can always re-run mach configure manually.

The config.status file is just a specially formatted Python script. Open it up and you’ll see there’s no magic:

#!/Users/nalexander/Mozilla/gecko/objdir-droid/_virtualenv/bin/python
# coding=utf-8

import os
import types
topsrcdir = '''/Users/nalexander/Mozilla/gecko'''
if not os.path.isabs(topsrcdir):
    rel = os.path.join(os.path.dirname(__file__), topsrcdir)
    topsrcdir = os.path.abspath(rel)
topsrcdir = os.path.normpath(topsrcdir)

topobjdir = os.path.abspath(os.path.dirname(__file__))

defines = [(name[1:-1], value[1:-1]) for name, value in [
    (''' ANDROID ''', ' 1 '),
...
] ]
substs = [(name[1:-1], value[1:-1] if isinstance(value, types.StringTypes) else value) for name, value in [
    (''' SHELL ''', r''' /bin/sh '''),
...
]

__all__ = ['topobjdir', 'topsrcdir', 'defines', 'non_global_defines', 'substs']
if __name__ == '__main__':
    args = dict([(name, globals()[name]) for name in __all__])
    from mozbuild.config_status import config_status
    config_status(**args)

The second major point: you can always determine what the build system thinks a build setting by extracting values from either the defines or the substs list of $OBJDIR/config.status. The substs list corresponds to the CONFIG dictionary in moz.build files. (It’s less obvious how defines works.)

Tweaking a build setting

I’m going to take a work-in-progress patch from Bug 1132185 as my example. I’ve applied the patch locally and pushed it to the review mercurial repository. You can view the commit or fetch it with:

hg pull https://reviewboard-hg.mozilla.org/review -r 006d3619a5da

The changeset itself is small and self-contained — exactly how we like build system changes. The patch:

  • declares a new flag, MOZ_ANDROID_TAB_QUEUE, in configure.in;
  • exposes that flag to the preprocessor in mobile/android/base/moz.build;
  • consumes the flag in the preprocessed source input mobile/android/base/AppConstants.java.in;
  • and turns the flag on, for all builds and all trains, in mobile/android/confvars.sh.

There’s a weirdness here in the way that mozconfig options are interpolated into configure.in which means that setting a default value does not do what you would expect. So let’s remove the default in configure.in:

diff --git a/configure.in b/configure.in
--- a/configure.in
+++ b/configure.in
@@ -3938,17 +3938,16 @@ MOZ_WEBSMS_BACKEND=
 MOZ_ANDROID_NEW_TABLET_UI=
-MOZ_ANDROID_TAB_QUEUE=
 ACCESSIBILITY=1

For the purposes of testing, that last is not desirable. So let’s go into mobile/android/confvars.sh and comment out those lines:

diff --git a/mobile/android/confvars.sh b/mobile/android/confvars.sh
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -75,17 +75,17 @@ MOZ_WEBGL_CONFORMANT=1

 # Enable the tab queue. This will go away in Bug 1132507.
-MOZ_ANDROID_TAB_QUEUE=1
+# MOZ_ANDROID_TAB_QUEUE=1

Now, let’s run mach configure and check the build settings in config.status:

chocho:gecko nalexander$ ./mach configure && grep MOZ_ANDROID_TAB_QUEUE objdir-chrome/config.status
0:00.23 /usr/bin/make -f client.mk -s configure
0:01.00 Generating /Users/nalexander/Mozilla/gecko/configure using autoconf
0:01.28 cd /Users/nalexander/Mozilla/gecko/objdir-chrome
0:01.28 /Users/nalexander/Mozilla/gecko/configure
0:01.64 Adding configure options from /Users/nalexander/Mozilla/gecko/mozconfig-chrome
0:01.64   --target=arm-linux-androideabi
0:01.64   --enable-application=mobile/android
0:01.64   --with-android-ndk=/usr/local/Cellar/android-ndk/r8e-darwin-x86_64
0:01.64   --with-android-sdk=/usr/local/Cellar/android-sdk/22.3/platforms/android-21
0:01.77 loading cache ./config.cache
...
(''' MOZ_ANDROID_TAB_QUEUE ''', r'''  '''),

Observe that we see a match for MOZ_ANDROID_TAB_QUEUE, but there’s an empty value. This match is in the substs list — it means the variable is known, but not set. When we test for existence, we’ll look for a non-empty value.

Now let’s add it to our local mozconfig [1]:

diff --git a/mozconfig b/mozconfig
--- a/mozconfig       2015-02-13 12:29:09.000000000 -0800
+++ b/mozconfig       2015-02-13 12:29:05.000000000 -0800
@@ -8,2 +8,4 @@

 mk_add_options MOZ_OBJDIR=./objdir-chrome
+
+MOZ_ANDROID_TAB_QUEUE=1

Re-run configure, and you should see the variable have a value:

chocho:gecko nalexander$ ./mach configure && grep MOZ_ANDROID_TAB_QUEUE objdir-chrome/config.status
...
(''' MOZ_ANDROID_TAB_QUEUE ''', ' 1 '),
(''' MOZ_ANDROID_TAB_QUEUE ''', r''' 1 '''),

(The first value is in the substs list; the second in the defines list. There’s a technical difference that I won’t get into here.)

Selective rebuilds in mobile/android

For the purposes of this section, I’m going to assume that we’re experimenting with a new build flag that is Firefox for Android (Fennec) only. That means we expect all impacts of the new build flag to be localized to the mobile/android directory, and in turn that lets us rebuild less of the tree.

In the mobile/android directory, we’re very careful to preprocess as little as possible, and we maintain our build dependencies quite actively, which makes testing mobile/android-only changes generally straight-forward. That brings us to the third major point: you should always see your build changes after running mach build mobile/android.

You can verify by manually inspecting the following generated files in the object directory:

  • $(OBJDIR)/mobile/android/base/AndroidManifest.xml
  • $(OBJDIR)/mobile/android/base/res/values/strings.xml
  • $(OBJDIR)/mobile/android/base/generated/preprocessed/org/mozilla/gecko/AppConstants.java
  • $(OBJDIR)/dist/bin/modules/AppConstants.jsm

For example, adding and removing the MOZ_ANDROID_TAB_QUEUE=1 line in my mozconfig toggles the boolean in the following block of AppConstants.java for me:

    public static final boolean MOZ_ANDROID_TAB_QUEUE =
//@line 178 "/Users/nalexander/Mozilla/gecko/mobile/android/base/AppConstants.java.in"
    true;
//@line 182 "/Users/nalexander/Mozilla/gecko/mobile/android/base/AppConstants.java.in"

Conclusion

The Firefox for Android team is always making things better for contributors — including shining light on the dark corners of the build system. Get involved with Firefox for Android or help build and test Firefox for iOS.

Discussion is best conducted on the mobile-firefox-dev mailing list and I’m nalexander on irc.mozilla.org and @ncalexander on Twitter.

Changes

  • Mon 16 February 2015: Thanks to @MartynHaigh for pointing out that I showed an incorrect mozconfig snippet using mk_add_option. I’ve corrected the text and included a small discussion [1].
[1](1, 2) Not specifying a default value in configure.in lets you set the value in your mozconfig easily. You can either specify MOZ_ANDROID_TAB_QUEUE=1 or export MOZ_ANDROID_TAB_QUEUE=1. Nota bene: using mk_add_option form does not work! (That’s for exporting settings to client.mk, which is a different code path. Confusing, I know.)
Nick Alexander

About Nick Alexander

Mathematician. Mozillian. Runner. Master of Disguise.