@ncalexander

How the Android Eclipse build system integration works

Tue 08 July 2014 / tagged: android, build system, eclipse

Firefox for Android (Fennec) can be built with Eclipse, but it’s a delicate dance. This post runs through the technical details of what happens, and when, during an Eclipse build.

This is not intended to be a guide to using Eclipse to build Fennec. For such a guide, see https://wiki.mozilla.org/Mobile/Fennec/Android/Eclipse.

Theory

  1. The RecursiveMakeBackend writes Makefile and backend.mk files into the object directory for every directory in the tree. If the corresponding source directory’s moz.build file includes Eclipse project definitions, then the backend.mk includes special "Eclipse-only" recursive make targets, like:

    ANDROID_ECLIPSE_PROJECT_FennecResourcesBranding: .aapt.deps .locales.deps
      $(call py_action,process_install_manifest,\
        --no-remove --no-remove-all-directory-symlinks --no-remove-empty-directories\
        /Users/nalexander/Mozilla/gecko-dev/objdir-droid/android_eclipse/FennecResourcesBranding\
        /Users/nalexander/Mozilla/gecko-dev/objdir-droid/android_eclipse/FennecResourcesBranding.manifest)
    

    These targets are always written (based on the Eclipse projects defined in the moz.build files).

  2. The AndroidEclipseBackend writes Eclipse project files and support files to the top-level android_eclipse directory of the object directory. The directory layout looks something like:

    android_eclipse
      Fennec.manifest
      Fennec
       .classpath
       .externalToolBuilders
       .project
       .settings
       gen
       lint.xml
       project.properties
    

    At this point, the project files are in place, but things are barren — there are no source files, Android resource files, or Android manifest.

  3. The object directory is built and packaged using mach build && mach package. This prepares the C/C++ layer and writes libraries (.so files) that Fennec requires.

  4. The Eclipse project files written include directions for a special builder plugin [#plugin] to run as the first step of every build. Each time Eclipse requests a build (for example, after a file is modified), the builder plugin takes whatever action is required to prepare the Eclipse project for building.

    Currently, the plugin:

    1. checks if anything (interesting) has changed
    2. runs the single recursive make target ANDROID_ECLIPSE_PROJECT_Project written by the recursive make backend (if necessary)
    3. marks Eclipse resources as needing to be refreshed (if necessary).

    After this, the regular Eclipse/Android build steps happen: processing the Android manifest, packaging Android resources, building Java files, etc.

Practice

Let’s dig in to what the plugin really does. All the integration glue is in the ANDROID_ECLIPSE_PROJECT_Project recursive make target. This target is really an aggregate target that does two things: install files and aggregate dependencies.

Install needed files

The target calls the Python process_install_manifest build action to install needed files.

For each Eclipse project, the eclipse backend writes an install manifest file (named Project.manifest) that contains directions for files to copy and symlink into the project directory. When I noted earlier that the project directory was "barren", we’re seeing it before this target has run, and specifically before this manifest is installed.

To compare, let’s execute:

$ mach build $OBJDIR/mobile/android/base/ANDROID_ECLIPSE_PROJECT_Fennec
/usr/bin/make -C /Users/nalexander/Mozilla/gecko-dev/objdir-droid -j8 -s backend.RecursiveMakeBackend
/usr/bin/make -C mobile/android/base -j8 -s ANDROID_ECLIPSE_PROJECT_Fennec
...
From /Users/nalexander/Mozilla/gecko-dev/objdir-droid/android_eclipse/Fennec:\
  Kept 1 existing; Added/updated 8; Removed 0 files and 0 directories.

That last line is the output from installing the manifest; it has populated the Fennec directory:

android_eclipse
  Fennec.manifest
  Fennec
    .classpath
    .externalToolBuilders
    .project
    .settings
    AndroidManifest.xml
    assets -> /Users/nalexander/Mozilla/gecko-dev/objdir-droid/dist/fennec/assets
    gen
    generated -> /Users/nalexander/Mozilla/gecko-dev/objdir-droid/mobile/android/base/generated
    java -> /Users/nalexander/Mozilla/gecko-dev/mobile/android/search/java
    libs
    lint.xml
    project.properties
    res
    src
    thirdparty -> /Users/nalexander/Mozilla/gecko-dev/mobile/android/thirdparty

Add build system dependencies

The target may depend on additional recursive make targets, as specified in the moz.build file using the recursive_make_targets list. So, for example, looking at mobile/android/base/moz.build, I can see that the FennecResourcesGenerated Eclipse project depends on the targets that capture dependencies on the Android manifest and all resources:

# Captures dependencies on Android manifest and all resources.
generated_recursive_make_targets = ['.aapt.deps', '.locales.deps']
generated = add_android_eclipse_library_project('FennecResourcesGenerated')
generated.recursive_make_targets += generated_recursive_make_targets

These additional recursive make targets are defined deeper in the build system: in this case, in mobile/android/base/Makefile.in.

In this way, the project-specific recursive make target both prepares the project directory, and provides a flexible extension point for additional build system dependencies. The custom Eclipse plugin merely invokes this make target (like we did above, with mach build).

An important note about build stages

It’s important to note that the packaging step must happen before the plugin runs: the per-project recursive make targets depend on the build and package artifacts. In the vanilla build system, the one built on mach and recursive make, there are two build stages:

  • configure and build-backend time
  • build (and incremental build) time
  • package time

Due to the unusual requirements of the Android build system, we add two new stages:

  • configure and build-backend time
  • Android build-backend time
  • build (and incremental build) time
  • package time
  • Eclipse incremental build time

At Android build-backend time, we don’t have the artifacts produced by at package time. That’s why we have the install manifests detailed above, which process package artifacts lazily; and that’s why the Eclipse projects produced by the Android build-backend look barren. There has been a good deal of confusion about this lazy approach (which I think results from not having the Eclipse plugin installed), manifesting as reports like https://mail.mozilla.org/pipermail/mobile-firefox-dev/2014-July/000788.html.

Conclusion

There are a fair number of moving parts:

  • two Python build backends, that work in concert;
  • staged build targets, that can’t be run at build-backend time;
  • coupled make targets and Eclipse project builders, joined by a custom Eclipse plugin.

And yet, it all basically works. Don’t move too quickly or make any loud noises.

Nick Alexander

About Nick Alexander

Mathematician. Mozillian. Runner. Master of Disguise.