I was looking for a small project to do some Rust programming and Servo being written in Rust seemed likely to have tasks that were small enough to do in my spare time yet be useful contributions to the project. This post outlines how I built Servo, found issues to work on, and got them merged.
The Servo README has details on the pre-requisites needed. Installing the pre-requisites and cloning the repository on Ubuntu was:
$ sudo apt-get install curl freeglut3-dev \
libfreetype6-dev libgl1-mesa-dri libglib2.0-dev xorg-dev \
msttcorefonts gperf g++ cmake python-virtualenv \
libssl-dev libbz2-dev libosmesa6-dev
$ git clone https://github.com/servo/servo
The Rust programming language has been fairly volatile in terms of language and library changes. Servo deals with this by requiring a specific git commit of the Rust compiler to build. The Servo source is periodically updated for new Rust versions. The commit id for Rust that is required to build is stored in the rust-snapshot-hash file in the Servo repository.
If the Rust compiler isn't installed already there are two options for building Servo. The first is to build the required version of Rust yourself, as outlined below. The second is to let the Servo build system,
mach, download a binary snapshot and use that. If you wish to do the latter, and it may make things easier when starting out, skip this step to build Rust.
$ cat servo/rust-snapshot-hash
$ git clone https://github.com/rust-lang/rust
$ cd rust
$ git checkout -b servo d3c49d2140fc65e8bb7d7cf25bfe74dda6ce5ecf
$ ./configure --prefix=/home/myuser/rust
$ make install
Note that I configure Rust to be installed in a directory off my home directory. I do this out of preference to enable managing different Rust versions. The build will take a long time and once built you need to add the prefix directories to the
$ export PATH=$PATH:/home/myuser/rust/bin
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/myuser/rust/lib
There is a configuration file used by the Servo build system to store information on what Rust compiler to use, whether to use a system wide Cargo (Rust package manager) install and various paths. This file,
.servobuild, should exist in the root of the Servo source that was cloned. There is a sample file that can be used as a template. The values I used were:
system-rust = true
system-cargo = false
android = false
debug-mozjs = false
If you want to use a downloaded binary snapshot of Rust to build Servo you should set the
system-rust setting to
false. With it set to
true as above it will expect to find a Rust of the correct version in the path.
Servo uses the mach command line interface that is used to build Firefox. Once the
.servobuild is created then Servo can be built with:
$ ./mach build
Servo can be run with:
$ ./mach run http://bluishcoder.co.nz
To run the test suite:
$ ./mach test
Finding something to work on
For my first task I searched for
E-easy issues that were not currently assigned (using the
C-assigned label). I commented in the issue asking if I could work on it and it was then assigned to me by a Servo maintainer.
Submitting the Fix
Fixing the issue involved:
- Fork the Servo repository on github.
- Clone my fork localling and make the changes required to the source in a branch I created for the issue I was working on.
- Commit the changes locally and push them to my fork on github.
- Raise a pull request for my branch.
Raising the pull request runs a couple of automated actions on the Servo repository. The first is an automated response thanking you for the changes followed by a link to the external critic review system.
To address reviews I made the required changes and committed them to my local branch as seperate commits using the
fixup flag to
git commit. This associates the new commit with the original commit that contained the change. It allows easier squashing later.
$ git commit --fixup=<commit id of original commit>
The changes are then pushed to the github fork and the previously made pull request is automatically updated. The Critic review tool also automatically picks up the change and will associate the fix with the relevant lines in the review.
With some back and forth the changes get approved and a request might be made to squash the commits. If
fixup was used to record the review changes then they will be squashed into the correct commits when you rebase:
$ git fetch origin
$ git rebase --autosquash origin/master
Force pushing this to the fork will result in the pull request being updated. When the reviewer marks this as
r+ the merge to master will start automatically, along with a build and test runs. If test failures happen these get added to the pull request and the review process starts again. If tests pass and it merges then it will be closed and the task is done.
A full overview of the process is available on the github wiki under Github and Critic PR handling 101.
The process overhead of committing to Servo is quite low. There are plenty of small tasks that don't require a deep knowledge of Rust. The first task I worked on was basically a search/replace. The second was more involved, implementing view-source protocol and text/plain handling. The latter allows the following to work in Servo:
$ ./mach run view-source:http://bluishcoder.co.nz
$ ./mach run http://cd.pn/plainttext.txt
The main issues I encountered working with Rust and Servo were:
- Compiling Servo is quite slow. Even changing private functions in a module would result in other modules rebuilding. I assume this is due to cross module inlining.
- I'd hoped to get away from intermittent test failures like there are in Gecko but there seems to be the occasional intermittent reftest failure.
The things I liked:
- Very helpful Servo maintainers on IRC and in github/review comments.
- Typechecking in Rust helped find errors early.
- I found it easier comparing Servo code to HTML specifications and following them together than I do in Gecko.
I hope to contribute more as time permits.