{"id":2747,"date":"2014-01-02T10:14:42","date_gmt":"2014-01-01T23:14:42","guid":{"rendered":"http:\/\/blog.mozilla.org\/nnethercote\/?p=2747"},"modified":"2014-01-21T12:14:11","modified_gmt":"2014-01-21T01:14:11","slug":"how-i-work-on-mozilla-code","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/nnethercote\/2014\/01\/02\/how-i-work-on-mozilla-code\/","title":{"rendered":"How I work on Mozilla code"},"content":{"rendered":"<p>Most Mozilla developers have their own particular set-ups and work-flows, and over time develop various scripts, shortcuts, and habits to make their lives easier.\u00a0 But we rarely talk about them.<\/p>\n<p>In this post I will describe various interesting aspects of how I work on Mozilla code, in the hope that (a) it will give other people useful ideas, and (b) other people will in turn give me useful ideas.<\/p>\n<h3>Machines<\/h3>\n<p>I have two machines:\u00a0 a\u00a0<a href=\"https:\/\/blog.mozilla.org\/nnethercote\/2013\/09\/12\/bleg-for-a-new-machine-2\/\">quite new and very fast Linux desktop<\/a> machine, on which I do 99% of my coding work, and a two-year old Macbook Pro, on which I do little coding but a lot of browsing and other non-development stuff.\u00a0 In theory my Linux desktop also has a Windows VM on it, though in practice that hasn&#8217;t happened yet!<\/p>\n<p>I use Ubuntu on my Linux machine.\u00a0 I don&#8217;t really enjoy sysadmin-type stuff, so I use the most widely-used, vanilla distribution available.\u00a0 That way, if something goes wrong there&#8217;s a decent chance somebody else will have had the same problem.<\/p>\n<h3>Mercurial Repositories<\/h3>\n<p>I do most of my work on mozilla-inbound.\u00a0 I clone that into a &#8220;master&#8221; repository,\u00a0 that I leave untouched, called <code>ws0<\/code>. Then I have nine local clones of <code>ws0<\/code>, called <code>ws1<\/code>..<code>ws9<\/code>. I don&#8217;t have trouble filling the nine local clones because I usually have multiple different coding tasks in flight at any one time. Indeed, this is a necessity when dealing with the various latencies involved with development, such as compilation and local test runs (minutes), try server runs (hours), and reviews (days).<\/p>\n<h3>Mercurial<\/h3>\n<p>I use Mercurial queues and am quite content with them, though I am looking forward to <a href=\"http:\/\/mercurial.selenic.com\/wiki\/ChangesetEvolution\">changeset evolution<\/a> becoming stable.\u00a0 I tried git once but didn&#8217;t like it much;\u00a0 the CLI is awful, the speed wasn&#8217;t noticeably better than Mercurial (and this was before I upgraded from Mercurial 1.8 to 2.7, which is much faster), and it was inconvenient to have to move patches over to a Mercurial repo before landing.<\/p>\n<p>One problem I had with Mercurial queues was that I would often type <code>hg qref patchname<\/code> when I meant <code>hg qnew patchname<\/code>. This can lead to surprising and annoying mix-ups with patches, so I wrote a pre-hook for <code>hg qref<\/code> &#8212; if I give it an argument that isn&#8217;t <code>-e<\/code> or <code>-u<\/code>, I almost certainly meant <code>hg qnew<\/code>, so it aborts with a reminder. This has saved me some hassle on numerous occasions.<\/p>\n<p>Some Mercurial extensions that I particularly like are <a href=\"http:\/\/mercurial.selenic.com\/wiki\/ColorExtension\">color<\/a> (colour output), <a href=\"http:\/\/mercurial.selenic.com\/wiki\/ProgressExtension\">progress<\/a> (progress bars for slow operations), <a href=\"http:\/\/mercurial.selenic.com\/wiki\/RecordExtension\">record<\/a> (commit part of a change) and <a href=\"https:\/\/bitbucket.org\/sfink\/bzexport\/\">bzexport<\/a> (upload patches to Bugzilla from the command line).<\/p>\n<h3>Try Server<\/h3>\n<p>Even though I do most of my work with mozilla-inbound, pushing to try from mozilla-inbound isn&#8217;t a great idea, because every so often you&#8217;ll catch some test breakage caused by someone else&#8217;s patch and then you have to work out if it&#8217;s your fault or was pre-existing.\u00a0 So recently I took RyanVM&#8217;s advice and wrote a script that transplants the patch queue from a mozilla-inbound repo to a mozilla-central repo, and then pushes to try.\u00a0 This avoids the test bustage problem, but occasionally the patches don&#8217;t apply cleanly.\u00a0 In that case I just push from the mozilla-inbound repo and deal with the risk of test bustage.<\/p>\n<h3>Compiling<\/h3>\n<p>I mostly use Clang, though I also sometimes use GCC.\u00a0 Clang is substantially faster than GCC, and its error messages are much better (though GCC&#8217;s have improved recently).\u00a0 Clang&#8217;s generated code is slightly worse (~5&#8211;10% slower), but that&#8217;s not much of an issue for me while developing.\u00a0 In fact, faster compilation time is important enough that my most common build configuration has the following line:<\/p>\n<pre>ac_add_options --enable-optimize='-O0'  # worse code, but faster builds<\/pre>\n<p>Last time I measured (which was before unified builds were introduced) this shaved a couple of minutes off build times, as compared to a vanilla <code>--enable-optimize<\/code> build.<\/p>\n<h3>Mozconfigs<\/h3>\n<p>I have a lot of mozconfig files.\u00a0 I switch frequently between different kinds of builds, so much so that all my custom short-cut commands (e.g. for building and running the browser<code><\/code>) have a mandatory argument that specifies the relevant mozconfig.\u00a0 As a result, the mozconfig names I use are much shorter than the default names.\u00a0 For example, the following is a selection of some of my more commonly-used mozconfigs for desktop Firefox.<\/p>\n<ul>\n<li>d64: debug 64-bit build with clang<\/li>\n<li>o64: optimized 64-bit build with clang<\/li>\n<li>gd64: debug 64-bit build with GCC<\/li>\n<li>go64: optimized 64-bit build with GCC<\/li>\n<li>cd64: debug 64-bit build with clang and ccache<\/li>\n<li>co64: optimized 64-bit build with clang and ccache<\/li>\n<li>o64v: optimized 64-bit build with clang and &#8211;enable-valgrind<\/li>\n<li>o64dmd: optimized 64-bit build with clang and &#8211;enable-dmd<\/li>\n<\/ul>\n<p>Although I never do 32-bit browser builds, I do sometimes do 32-bit JS shell builds, so the &#8217;64&#8217; isn&#8217;t entirely redundant!<\/p>\n<h3>Building<\/h3>\n<p>I have a script called <code>mmq<\/code> that I use to build the browser.\u00a0 I invoke it like this:<\/p>\n<pre>mmq o64<\/pre>\n<p>The argument is the mozconfig\/objdir name.\u00a0 This script invokes the build and redirects the output to an <code>errors.err<\/code> file (for use with <em>quickfix<\/em>, see below).\u00a0 Once compilation finishes, the script also does some hacky grepping to reprint the first five compilation errors, if they exist, once compilation finishes.\u00a0 I do this to make it easier to find the errors &#8212; sometimes they get swamped by the subsequent output.\u00a0 (My use of Quickfix has made this feature less important than it once was, though it&#8217;s still a good thing to have.)<\/p>\n<h3>Profiles<\/h3>\n<p>I have multiple profiles.<\/p>\n<ul>\n<li><code>default<\/code>: used for my normal browsing.<\/li>\n<li><code>dev<\/code>: my standard development profile.<\/li>\n<li><code>dev2<\/code>: a second development profile, mostly used if I already am using the <code>dev<\/code> profile in another browser invocation.<\/li>\n<li><code>e10s<\/code>: a profile with Electrolysis enabled.<\/li>\n<\/ul>\n<h3>Starting Firefox<\/h3>\n<p>I have a script called <code>ff<\/code> with which I start Firefox like the following.<\/p>\n<pre>ff o64 dev<\/pre>\n<p>The first argument is the mozconfig, and the second is the profile.\u00a0 Much of the time, this invokes Firefox in the usual way, e.g.:<\/p>\n<pre>o64\/dist\/bin\/firefox -P dev -no-remote<\/pre>\n<p>However, this script knows about my mozconfigs and it automatically does more elaborate invocations when necessary, e.g. for DMD (which requires the setting of some environment variables).\u00a0 I also wrote it so that I can tack on <code>gdb<\/code> as a third argument and it&#8217;ll run under GDB.<\/p>\n<h3>Virtual desktop and window layout<\/h3>\n<p>I use a 2&#215;2 virtual desktop layout.<\/p>\n<p>In each of the top-left and bottom-left screens I have three xterms &#8212; a full-height one on the left side in which I do editing, and two half-height ones on the right side in which I invoke builds, hg commands, and sundry other stuff.<\/p>\n<p>In the top-right screen I have Firefox open for general use.<\/p>\n<p>In the bottom-right screen I have a Chatzilla window open.<\/p>\n<h3>Text Editing<\/h3>\n<p>I use Vim.\u00a0 This is largely due to path dependence;\u00a0 it&#8217;s what they taught in my introductory programming classes at university, and I&#8217;ve happily used it ever since.\u00a0 My setup isn&#8217;t particularly advanced, and most of it isn&#8217;t worth writing about.\u00a0 However, I have done a few things that I think are worth mentioning.<\/p>\n<h4>Tags<\/h4>\n<p>Tags are fantastic &#8212; they let you jump immediately to the definition of a function\/type\/macro.\u00a0 I use ctags and I have an alias for the following command.<\/p>\n<pre>ctags -R --langmap=C++:.c.h.cpp.idl --languages=C++ --exclude='*dist\\\/include*' --exclude='*[od]32\/*' --exclude='*[od]64\/*'<\/pre>\n<p>I have to re-run this command periodically to keep up with changes to the codebase.\u00a0 Fortunately, it only takes about 5 seconds on my fast SSD.\u00a0 (My old machine with a mechanical HD took <em>much<\/em> longer).\u00a0 The coverage isn&#8217;t perfect but it&#8217;s good enough, and the specification of .idl files in the &#8211;langmap option was a recent tweak I made that improved coverage quite a bit.<\/p>\n<h4>Quickfix<\/h4>\n<p>I now use <em>quickfix<\/em>, which is a special mode to speed up the edit-compile-edit cycle.\u00a0 The commands I use to build Firefox redirect the output to a special file, and if there are any compile errors, I use Vim&#8217;s quickfix command to quickly jump to their locations.\u00a0 This saves enormous amounts of manual file and line traversal &#8212; I can&#8217;t recommend it highly enough.<\/p>\n<p>In order to use quickfix you need to tell Vim what the syntax of the compile errors is.\u00a0 I have the following command in my .vimrc for this.<\/p>\n<pre>\" Multiple entries (separated by commas):\r\n\" - compile (%f:%l:%c) errors for different levels of file nesting\r\n\" - linker (%f:%l) errors for different levels of file nesting\r\nset efm=..\/..\/..\/..\/..\/%f:%l:%c:\\ error:\\ %m,..\/..\/..\/..\/%f:%l:%c:\\ error:\\ %m,..\/..\/..\/%f:%l:%c:\\ error:\\ %m,..\/..\/%f:%l:%c:\\ error:\\ %m,..\/%f:%l:%c:\\ error:\\ %m,%f:%l:%c:\\ error:\\ %m,..\/..\/..\/..\/..\/%f:%l:\\ error:\\ %m,..\/..\/..\/..\/%f:%l:\\ error:\\ %m,..\/..\/..\/%f:%l:\\ error:\\ %m,..\/..\/%f:%l:\\ error:\\ %m,..\/%f:%l:\\ error:\\ %m,%f:%l:\\ error:\\ %m<\/pre>\n<p>This isn&#8217;t pretty, but it works well for Mozilla code.\u00a0 Then it&#8217;s just a matter of doing :cf to load the new errors file (which also takes me to the first error) and then :cn\/:cp to move forward and backward through the list.\u00a0 Occasionally I get an error in a header file that is present in the objdir and the matching fails, and so I have to navigate to that file manually, but this is rare enough that I haven&#8217;t bothered trying to fix it properly.<\/p>\n<p>One nice thing about quickfix is that it lets me start fixing errors before compilation has finished!\u00a0 As soon as I see the first error message I can do :cf.\u00a0 This means I have to re-do :cf and skip over the already-fixed errors if more errors occur later, but this is still often a win.<\/p>\n<p>If you use Vim, work on Mozilla C++ code, and don&#8217;t use it, you should set it up\u00a0<em>right now<\/em>.\u00a0 There are many additional commands and options, but what I&#8217;ve written above is enough to get you started, and covers 95% of my usage.\u00a0 (In case you&#8217;re curious, the :cnf, :cpf, :copen and :cclose commands cover the other 5%.)<\/p>\n<h4>:grep<\/h4>\n<p>I also set up Vim&#8217;s :grep command for use with Firefox.\u00a0 I put the following script in ~\/bin\/.<\/p>\n<pre>#! \/bin\/sh\r\npattern=$1;\r\nif [ -z \"$pattern\" ]; then\r\n\u00a0\u00a0\u00a0 echo \"usage: $FUNCNAME &lt;pattern&gt;\";\r\n\u00a0\u00a0\u00a0 return 1;\r\nfi;\r\ngrep -n -r -s \\\r\n\u00a0\u00a0\u00a0 --exclude-dir=\"*[od]32*\" \\\r\n\u00a0\u00a0\u00a0 --exclude-dir=\"*[od]64*\" \\\r\n\u00a0\u00a0\u00a0 --exclude-dir=\".hg*\" \\\r\n\u00a0\u00a0\u00a0 --exclude-dir=\".svn*\" \\\r\n\u00a0\u00a0\u00a0 --include=\"*.cpp\" \\\r\n\u00a0\u00a0\u00a0 --include=\"*.h\" \\\r\n\u00a0\u00a0\u00a0 --include=\"*.c\" \\\r\n\u00a0\u00a0\u00a0 --include=\"*.idl\" \\\r\n\u00a0\u00a0\u00a0 --include=\"*.html\" \\\r\n\u00a0\u00a0\u00a0 --include=\"*.xul\" \\\r\n\u00a0\u00a0\u00a0 --include=\"*.js\" \\\r\n\u00a0\u00a0\u00a0 --include=\"*.jsm\" \\\r\n\u00a0\u00a0\u00a0 \"$pattern\"<\/pre>\n<p>It does a recursive grep for a pattern through various kinds of source files, ignoring my objdirs and repository directories.\u00a0 To use it, I just type &#8220;:grep &lt;pattern&gt;&#8221; and Vim runs the script and sends the results to quickfix, so I can again use :cn and :cp to navigate through the matches.\u00a0 (Vim already knows about grep&#8217;s output format, so you don&#8217;t need to configure anything for that.)<\/p>\n<p>I can also use that script from the command line, which is useful when I want to see all the matches at once, rather than stepping through them one at a time in Vim.<\/p>\n<h4>Trailing whitespace detection<\/h4>\n<p>This line in my .vimrc tells Vim to highlight any trailing whitespace in red.<\/p>\n<pre>highlight ExtraWhitespace ctermbg=red guibg=red\r\nautocmd BufWinEnter *.{c,cc,cpp,h,py,js,idl} match ExtraWhitespace \/\\s\\+$\/\r\nautocmd InsertEnter *.{c,cc,cpp,h,py,js,idl} match ExtraWhitespace \/\\s\\+\\%#\\@&lt;!$\/\r\nautocmd InsertLeave *.{c,cc,cpp,h,py,js,idl} match ExtraWhitespace \/\\s\\+$\/\r\nautocmd BufWinLeave *.{c,cc,cpp,h,py,js,idl} call clearmatches()<\/pre>\n<p>[Update: I accidentally omitted the final four lines of this when I first published this post.]<\/p>\n<p>The good thing about this is that I now never submit patches with trailing whitespace.\u00a0 The bad thing is that I can see where other people have left behind trailing whitespace \ud83d\ude42<\/p>\n<h4>Ctrl-P<\/h4>\n<p>Finally, I installed the <a href=\"https:\/\/github.com\/kien\/ctrlp.vim\">Ctrl-P plugin<\/a>, which aims to speed up the opening of files by letting you type in portions of the file&#8217;s path.\u00a0 This is potentially quite useful for Mozilla code, where the directory nesting can get quite deep.\u00a0 However, Ctrl-P has been less of a win than I hoped, for two reasons.<\/p>\n<p>First, it&#8217;s quite slow, even on my very fast desktop with its very fast SSD.\u00a0 While typing, there are often pauses of hundreds of milliseconds after each keystroke, which is annoying.<\/p>\n<p>Second, I find it too eager to find matches. If you type a sequence of characters, it will match against any file that has those characters present in that order, no matter how many other characters separate them, and it will do so case-insensitively.\u00a0 This might work well for some people, but if I&#8217;m opening a file such as <code>content\/base\/src\/nsContentUtils.cpp<\/code>, I always end up typing just the filename in full.\u00a0 By the time I&#8217;ve typed &#8220;nsContentU&#8221; ideally it would be down to the two files in the repository that match that exact string.\u00a0 But instead I get the following.<\/p>\n<pre>&gt; widget\/tests\/window_composition_text_querycontent.xul\r\n&gt; dom\/interfaces\/base\/nsIQueryContentEventResult.idl\r\n&gt; dom\/base\/nsQueryContentEventResult.cpp\r\n&gt; dom\/base\/nsQueryContentEventResult.h\r\n&gt; content\/base\/public\/nsIContentSecurityPolicy.idl\r\n&gt; content\/base\/public\/nsContentCreatorFunctions.h\r\n&gt; dom\/interfaces\/base\/nsIContentURIGrouper.idl\r\n&gt; content\/base\/public\/nsContentPolicyUtils.h\r\n&gt; content\/base\/src\/nsContentUtils.cpp\r\n&gt; content\/base\/public\/nsContentUtils.h<\/pre>\n<p>I wish I could say &#8220;case-insensitive exact matches, please&#8221;, but I don&#8217;t think that&#8217;s possible.\u00a0 As a result, I don&#8217;t use Ctrl-P that much, though it&#8217;s still occasionally useful if I want to open a file for which I know the name but not the path &#8212; it&#8217;s faster for that than dropping back to a shell and using <code>find<\/code>.<\/p>\n<h3>Conclusion<\/h3>\n<p>That&#8217;s everything of note that I can think of right now.\u00a0 Please feel free to steal as many ideas as you wish from this post.<\/p>\n<p>I haven&#8217;t given full details for a lot of the things I mentioned above. I&#8217;m happy to give more details (e.g. what various scripts look like) if people are interested.<\/p>\n<p>Finally, I encourage other developers to write posts like this one explaining how they work on Mozilla code.\u00a0 Thanks!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most Mozilla developers have their own particular set-ups and work-flows, and over time develop various scripts, shortcuts, and habits to make their lives easier.\u00a0 But we rarely talk about them. In this post I will describe various interesting aspects of how I work on Mozilla code, in the hope that (a) it will give other [&hellip;]<\/p>\n","protected":false},"author":139,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[649],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/posts\/2747"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/users\/139"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/comments?post=2747"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/posts\/2747\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/media?parent=2747"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/categories?post=2747"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/tags?post=2747"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}