Using Eclipse CDT for Mozilla development
Eclipse CDT (C/C++ Development Tools) is an open-source IDE for C and C++ development. It supports projects that have their own build systems and complex project structure, like Mozilla. If you want to use an IDE with code assistance (syntax highlighting, autocomplete, call graph explorer, etc.) for Mozilla C++ development, then Eclipse CDT might be for you.
Some background on code assistance (optional)
Feel free to skip to the next section if you're happy to just follow the steps in the instructions below. This section is just here to explain why those instructions are the way they are for the people who like to understand what it is they're doing.
In order to provide advanced code assistance features, an IDE like Eclipse needs to do some static analysis of the code. The static analysis in turn requires the IDE to know a sane and consistent set of build options for the files in the source tree (these can be different for different files/parts of the tree). Only then, with a valid set of preprocessor defines, pre-include files and include paths for each source file, can a meaningful analysis of the files be carried out. Since real build configurations are often too complex for the user to specify to the IDE by hand (certainly the case for Mozilla), IDEs typically provide tools that can attempt to collect a valid set of configuration options from an actual build configuration. These tools can work in several different ways:
- If the build configuration is "managed" by the IDE, then the IDE knows everything that it needs to know and one of its build configurations can be selected for the analysis. (Mozilla has its own build system separate from any IDE, so this isn't an option for the Mozilla source.)
- The user may be required to have their external build system create debug binaries with all the necessary information in them, and then tells the IDE where to look for those binaries. (Not ideal for people who doesn't want to change their build configuration or the files it outputs just to get their IDE working.)
- The IDE can processes console output from a build, looking for lines where the compiler was invoked. For each such line it can try to figure out which source file the compiler was building, and to resolve any relative include paths. This may not always be possible to do one or both of these things if the build output doesn't identify which directory the compiler was being invoked from, and the source file was specified using a relative path when there are multiple source files with the same name in the tree, or if some of the include paths are relative paths. (This is an issue for a few of Mozilla's modules.) If the IDE can successfully identify which file was being built, then It can also usefully associate any preprocessor defines and preinclude files that were specified on the command line with it.
- The IDE can be used to invoke the external build system, while using something like LD_PRELOAD in order to load its own library into all the executables that are invoked as part of the build process. Its library can then check whether the executable is a compiler instance and, if so, use the processes' current working directory and the arguments that were passed to it to obtain the information it needs for each source file.
The mechanism that Eclipse CDT currently supports for build option discovery is parsing of the console output from the build. (In future it may support the LD_PRELOAD method, which would simplify the instructions below.)
One thing to note about parsing console output is that output from a parallelized build can NOT usefully be parsed (since lines from different threads/processes will overlap and be muddled together). Also, the build output must not be silenced/quietened (for obvious reasons), and it must be set to output information on the directory the compiler is being invoked from.
Since most Mozilla developers parallelize and silence their builds to minimize their build times, and since UN-parallelizing and UN-silencing them will most likely require .mozconfig changes, the instructions below will not set up Eclipse to have its Build action invoke the Mozilla build system so Eclipse can directly parse its output. Instead, the instructions below will get you to log the build output from a clean build run with particular .mozconfig options, and then trick Eclipse into reading that log file when you run its Build step.
The obvious weakness in having Eclipse read a log file is that every so often you have to refresh it and have Eclipse re-read the log in order for Eclipse's code assistance to stay roughly in sync with the code. In practice though, it seems that significant build configuration changes are infrequent enough that this doesn't have to be done too often.
The following instructions are for Linux. If you have experience with Win32, please note it here.
- Install Eclipse IDE for C/C++ Developers.
- Obtain a clean copy of the Mozilla source.
- Make a buildable Mozilla tree with the object files in an objdir. For example, sources in "src/", object files in "src/obj-debug".
- Things will be a little quicker if you delete any unnecessary files from the source directory, e.g., "*~" emacs backups, "*.rej", "*.orig" and ".#*" patch detritus.
- Start Eclipse and when prompted to "Select a workspace" just accept the default - we'll give our Mozilla project(s) their own workspace(s) rather than using this one anyway.
- Fire up Eclipse and do
File > New > C++ Projectto start the C++ Project wizard.
- Enter a meaningful project name identifying the Mozilla source tree this project will manage. (The Eclipse help docs say "do not use spaces or special characters in the project name".)
- Uncheck the "Use default location" box, and enter the path of the directory containing the Mozilla source.
- Pick "Makefile Project"
- Now you can finish the wizard. You should get a Project view in the left with a directory tree you can browse to find files to edit. (Tip: Ctrl-F brings up a fast-find textbox for quick selection of files from opened directories.)
- Doubleclick on a file to edit it. It should be syntax highlighted. There should be an outline in the right-hand pane that shows the gross constructs in the file. (Sometimes elements are missing because the parser was recovering from an error --- this view doesn't parse include files).
- You can tweak preferences to improve your life: menu Window | Preferences
- In the General | Keys page, in the Modify tab, you can select the Emacs scheme if your fingers demand it
- Turn off "Build Automatically" in the Project menu.
Alt-/ emacs-style abbrev completion just works.
In the default key bindings, ctrl-J does incremental search. Some users find that they like to use ctrl-G for "next match" (because Firefox does) but in Eclipse that tries to locate the definition of an identifier, which can be very slow in Mozilla with the CDT. You may want to rebind ctrl-G to "next match" if it isn't already so bound.
You probably need to adjust the CDT editor preferences to always use spaces instead of tabs, and to indent by 2 spaces.
Eclipse doesn't exactly provide the emacs buffers setup, but it does go some way towards it. Press ctrl-E to get a list of active editors; typing into it gives you autocompletion of the editor/buffer name for you to switch to.
Extra CDT features
You can configure CDT to build Mozilla by calling "make". Select the project in the project view, right click, choose Properties, C/C++ Make project, then you can configure how "make" is called. (For example, you may want to input the command for building your project as "make -f client.mk build".) Do a build by choosing Project | Build Project in the menus. It works OK but the obvious approach does a full build which is quite slow in Mozilla (we go through all directories twice, once for exports and once for the real build).
If you're brave you can turn on "auto build on save" in the project properties under "C++ Make Project" in the "Make Builder" tab, and/or by turning on Build Automatically in the project menu. I don't recommend it.
Build output appears in the Console view near the bottom of the window. Eclipse will do an OK job of matching compiler errors/warnings to source lines if you configure your Mozilla build with the gcc option "-fmessage-length=0". Just add the lines
export CFLAGS="-fmessage-length=0" export CPPFLAGS="-fmessage-length=0"
to your .mozconfig. Errors and warnings will be marked in the source when you open an affected file. They will also appear in the "Problems" view near the bottom of the window.
Be aware that Eclipse's parsing of the build output can be screwed up if you use parallel make.
Intelligent Code Navigation
The CDT offers intelligent code completion, and source navigation and indexing. These features are currently too slow and have a few bugs that stop me from using them regularly with Mozilla, but they're fun to play with.
First you have to teach the Eclipse parser which files get built and with what options. If you do a clean build from Eclipse and it doesn't get screwed up, Eclipse will discover all the include files/directories and predefined preprocessor symbols for each file in the project. These are visible via Properties (in the context menu) for each file. Eclipse will also learn which files get built and which don't. Maybe a more reliable way to teach Eclipse this information is to do a clean build, redirect the output to a file, then in project properties "C++ Make Project" Discovery Options you can specify the build log file and choose "Load".
Now you should be able to use intelligent code completion. Start typing an identifier and press ctrl-space. You should get a popup list of the completions that are valid in the current scope --- including global functions and variables, applicable "this" members, members if you're completing a member access, and even macros. Unfortunately in the current CDT this always does a parse of the current translation unit up to the completion point, which takes several seconds for a large Mozilla file with a decent number of includes, so this isn't good.
You should also be able to navigate within the current compilation unit by holding down ctrl and then clicking on an identifier to jump to its definition. Again this requires parsing of the compilation unit.
For really slick navigation you need to index the Mozilla project by turning on the indexer (Project properties, C++ Make Project, C++ indexer). Starting with Eclipse 3.3.2, the indexation process was greatly improved and is worth executing once.
If you like living dangerously, in Preferences | C/C++ you can select "follow #includes when parsing working copies". Eclipse will then follow #includes when it does parsing to update the outline view. This slows things down massively and is not recommended, but without it, many of our macros will not be defined and either cause syntax errors in the quick parser which cause some constructs to be omitted from the outline due to error recovery, or some constructs will not be parsed at all because they're #ifdefed and Eclipse doesn't know that the macro is defined.
You can use Eclipse as a front end to gdb. It simplifies debugging and variable watching.
First, you need to create a debug configuration.
Select Run->Open Debug Dialog… from the Eclipse menu Right-click on C/C++ Local Application and select New Project: firefox (or select your project name) C/C++ Application: (select browse and choose mozilla/../obj-i686-pc-linux-gnu/dist/bin/firefox-bin)
- In the Arguments tab, change the working directory to mozilla/../obj-i686-pc-linux-gnu/dist/bin/
- In the Debugger tab, remove the checkbox on "Stop on startup at:"
- In the Environment tab, create two variables (make sure you trim the names and values)
Name: LD_LIBRARY_PATH Value: .:./plugins:. Name: LIBRARY_PATH Value: .:./components:.
Out of the box, you may/will get GDB connection timeouts. This is because Eclipse is trying to push every subfolder in GDB's environment. The easiest way to resolve this issue is to remove any source entry from the debug configuration (Run->Open Debug Dialog...) in the Source tab. Doing so will unfortunately remove the binding between the binaries and the source code. To keep this feature working, you need to add a "Path Mapping" by clicking "Add..." in the Source tab. Once a "Path Mapping" is created, select "Edit..." and add an entry with these values
Compilation path: / Local file system path: /
This is the only known workaround to bind binaries to source files. It has been tested and works perfectly under Eclipse Europa (3.3.2) with Eclipse-CDT (4.0.3).
You can use distcc and the MOZ_MAKE_FLAGS build variable to distribute the compilation across a network. This greatly improves the speed of the build. The complete informations are available in the Mozilla Team Documentation - distcc.
The CDT has some problems with Mozilla that need to be worked on.
- Code completion and other operations that require a full parse tree are really slow because they parse the compilation unit from scratch every time. This can be fixed -- there is a plan. But it's quite a lot of work.
- The parser has bugs. I get a few internal parser errors when parsing the common Mozilla include files that use slightly tricky constructs (e.g., nsCOMPtr). Some of these bugs should be easy to fix, others maybe not. Apparently these bugs are only in the "old" parser which is deprecated and will be completely replaced by the "new" parser, "soon", so it's not really worth working on these bugs.
- It would be nice if Eclipse could pass information about what files have changed to the build process, which could then decide on a faster way to do the build (e.g., "just make in layout/"). I've actually written a small change to the CDT Make builder that lets you specify that as an option, in which case Eclipse sends the names of all changed files to your build tool. The build tool is a Perl script that figures out if a faster build is possible and if so, does it.
- Some editing operations get sluggish when you have lots of files open (say, more than 20).
- The GDB thread terminates unexpectedly and detaches from the Firefox bin. This is caused by the breakpoints currently set. When you restart your Eclipse environment and face such issue. Delete and readd the breakpoints in your list.