1# Hacking Servo - Quickstart 2 3This guide covers the basic things one needs to know to start hacking Servo. 4It doesn't cover how Servo works (see the [documentation](#documentation) section for that), 5but describes how to setup your environment to compile, run, and debug Servo. For information 6on the [Github Workflow](https://github.com/servo/servo/wiki/Github-workflow) and some helpful 7[Git Tips](https://github.com/servo/servo/wiki/Github-workflow#git-tips), see the 8[Wiki](https://github.com/servo/servo/wiki). 9 10## Building Servo 11 12Building Servo is quite easy. Install the prerequisites described in the [README](../README.md) file, then type: 13 14```shell 15./mach build -d 16``` 17 18*Note: on Mac, you might run into an SSL issue while compiling. You'll find a solution to this problem [here](https://github.com/sfackler/rust-openssl/issues/255).* 19 20The `-d` option means "debug build". You can also build with the `-r` option which means "release build". Building with `-d` will allow you to use a debugger (lldb). A `-r` build is more performant. Release builds are slower to build. 21 22You can use and build a release build and a debug build in parallel. 23 24## Running Servo 25 26The servo binary is located in `target/debug/servo` (or `target/release/servo`). You can directly run this binary, but we recommend using `./mach` instead: 27 28``` shell 29./mach run -d -- http://github.com 30``` 31 32… is equivalent to: 33 34``` shell 35./target/debug/servo http://github.com 36``` 37 38If you build with `-d`, run with `-d`. If you build with `-r`, run with `-r`. 39 40## ./mach 41 42`mach` is a python utility that does plenty of things to make our life easier (build, run, run tests, update dependencies… see `./mach --help`). Beside editing files and git commands, everything else is done via `mach`. 43 44``` shell 45./mach run -d [mach options] -- [servo options] 46``` 47 48The `--` separates `mach` options from `servo` options. This is not required, but we recommend it. `mach` and `servo` have some options with the same name (`--help`, `--debug`), so the `--` makes it clear where options apply. 49 50## Mach and Servo options 51 52This guide only covers the most important options. Be sure to look at all the available mach commands and the servo options: 53 54``` shell 55./mach --help # mach options 56./mach run -- --help # servo options 57``` 58 59## Some basic Rust 60 61Even if you have never seen any Rust code, it's not too hard to read Servo's code. But there are some basics things one must know: 62 63- [Match](https://doc.rust-lang.org/book/first-edition/match.html) and [Patterns](https://doc.rust-lang.org/book/first-edition/patterns.html) 64- [Options](http://rustbyexample.com/std/option.html) 65- [Expression](http://rustbyexample.com/expression.html) 66- [Traits](http://rustbyexample.com/trait.html) 67- That doesn't sound important, but be sure to understand how `println!()` works, especially the [formatting traits](https://doc.rust-lang.org/std/fmt/#formatting-traits) 68 69This won't be enough to do any serious work at first, but if you want to navigate the code and fix basic bugs, that should do it. It's a good starting point, and as you dig into Servo source code, you'll learn more. 70 71For more exhaustive documentation: 72 73- [doc.rust-lang.org](https://doc.rust-lang.org) 74- [rust by example](http://rustbyexample.com) 75 76## Cargo and Crates 77 78A Rust library is called a crate. Servo uses plenty of crates. These crates are dependencies. They are listed in files called `Cargo.toml`. Servo is split into components and ports (see `components` and `ports` directories). Each has its own dependencies, and each has its own `Cargo.toml` file. 79 80`Cargo.toml` files list the dependencies. You can edit this file. 81 82For example, `components/net_traits/Cargo.toml` includes: 83 84``` 85 [dependencies.stb_image] 86 git = "https://github.com/servo/rust-stb-image" 87``` 88 89But because `rust-stb-image` API might change over time, it's not safe to compile against the `HEAD` of `rust-stb-image`. A `Cargo.lock` file is a snapshot of a `Cargo.toml` file which includes a reference to an exact revision, ensuring everybody is always compiling with the same configuration: 90 91``` 92[[package]] 93name = "stb_image" 94source = "git+https://github.com/servo/rust-stb-image#f4c5380cd586bfe16326e05e2518aa044397894b" 95``` 96 97This file should not be edited by hand. In a normal Rust project, to update the git revision, you would use `cargo update -p stb_image`, but in Servo, use `./mach cargo-update -p stb_image`. Other arguments to cargo are also understood, e.g. use --precise '0.2.3' to update that crate to version 0.2.3. 98 99See [Cargo's documentation about Cargo.toml and Cargo.lock files](http://doc.crates.io/guide.html#cargotoml-vs-cargolock). 100 101## Working on a Crate 102 103As explained above, Servo depends on a lot of libraries, which makes it very modular. While working on a bug in Servo, you'll often end up in one of its dependencies. You will then want to compile your own version of the dependency (and maybe compiling against the HEAD of the library will fix the issue!). 104 105For example, I'm trying to bring some cocoa events to Servo. The Servo window on Desktop is constructed with a library named [Glutin](https://github.com/tomaka/glutin). Glutin itself depends on a cocoa library named [cocoa-rs](http://github.com/servo/cocoa-rs). When building Servo, magically, all these dependencies are downloaded and built for you. But because I want to work on this cocoa event feature, I want Servo to use my own version of *glutin* and *cocoa-rs*. 106 107This is how my projects are laid out: 108 109``` 110~/my-projects/servo/ 111~/my-projects/mozjs/ 112``` 113Both folder are git repositories. 114 115To make it so that servo uses `~/my-projects/mozjs/`, first ascertain which version of the crate Servo is using and whether it is a git dependency or one from crates.io. 116 117Both information can be found using, in this example, `/mach cargo pkgid mozjs_sys`(`mozjs_sys` is the actual crate name, which doesn't necessarily match the repo folder name). 118 119If the output is in the format `https://github.com/servo/mozjs#mozjs_sys:0.0.0`, you are dealing with a git dependency and you will have to edit the `~/my-projects/servo/Cargo.toml` file and add at the bottom: 120 121``` toml 122[replace] 123"https://github.com/servo/mozjs#mozjs_sys:0.0.0" = { path = '../mozjs' } 124``` 125 126If the output is in the format `https://github.com/rust-lang/crates.io-index#mozjs_sys#0.0.0`, you are dealing with a crates.io dependency and you will have to edit the `~/my-projects/servo/Cargo.toml` in the following way: 127 128``` toml 129[replace] 130"mozjs_sys:0.0.0" = { path = '../mozjs' } 131``` 132 133Both will tell any cargo project to not use the online version of the dependency, but your local clone. 134 135For more details about overriding dependencies, see [Cargo's documentation](http://doc.crates.io/specifying-dependencies.html#overriding-dependencies). 136 137## Debugging 138 139### Logging: 140 141Before starting the debugger right away, you might want to get some information about what's happening, how, and when. Luckily, Servo comes with plenty of logs that will help us. Type these 2 commands: 142 143``` shell 144./mach run -d -- --help 145./mach run -d -- --debug help 146``` 147 148A typical command might be: 149 150``` shell 151./mach run -d -- -i -y 1 -t 1 --debug dump-layer-tree /tmp/a.html 152``` 153 154… to avoid using too many threads and make things easier to understand. 155 156On OSX, you can add some Cocoa-specific debug options: 157 158``` shell 159./mach run -d -- /tmp/a.html -- -NSShowAllViews YES 160``` 161 162You can also enable some extra logging (warning: verbose!): 163 164``` 165RUST_LOG="debug" ./mach run -d -- /tmp/a.html 166``` 167 168Using `RUST_LOG="debug"` is usually the very first thing you might want to do if you have no idea what to look for. Because this is very verbose, you can combine these with `ts` (`moreutils` package (apt-get, brew)) to add timestamps and `tee` to save the logs (while keeping them in the console): 169 170``` 171RUST_LOG="debug" ./mach run -d -- -i -y 1 -t 1 /tmp/a.html 2>&1 | ts -s "%.S: " | tee /tmp/log.txt 172``` 173 174You can filter by crate or module, for example `RUST_LOG="layout::inline=debug" ./mach run …`. Check the [env_logger](http://doc.rust-lang.org/log/env_logger/index.html) documentation for more details. 175 176Use `RUST_BACKTRACE=1` to dump the backtrace when Servo panics. 177 178### println!() 179 180You will want to add your own logs. Luckily, many structures [implement the `fmt::Debug` trait](https://doc.rust-lang.org/std/fmt/#fmtdisplay-vs-fmtdebug), so adding: 181 182``` rust 183println!("foobar: {:?}", foobar) 184``` 185 186usually just works. If it doesn't, maybe some of foobar's properties don't implement the right trait. 187 188### Debugger 189 190To run the debugger: 191 192``` shell 193./mach run -d --debug -- -y 1 -t 1 /tmp/a.html 194``` 195 196This will start `lldb` on Mac, and `gdb` on Linux. 197 198From here, use: 199 200``` shell 201(lldb) b a_servo_function # add a breakpoint 202(lldb) run # run until breakpoint is reached 203(lldb) bt # see backtrace 204(lldb) frame n # choose the stack frame from the number in the bt 205(lldb) thread list 206(lldb) next / step / … 207(lldb) print varname 208``` 209 210And to search for a function's full name/regex: 211 212```shell 213(lldb) image lookup -r -n <name> #lldb 214(gdb) info functions <name> #gdb 215``` 216 217See this [lldb tutorial](http://lldb.llvm.org/tutorial.html) and this [gdb tutorial](http://www.unknownroad.com/rtfm/gdbtut/gdbtoc.html). 218 219To inspect variables and you are new with lldb, we recommend using the `gui` mode (use left/right to expand variables): 220 221``` 222(lldb) gui 223┌──<Variables>───────────────────────────────────────────────────────────────────────────┐ 224│ ◆─(&mut gfx::paint_task::PaintTask<Box<CompositorProxy>>) self = 0x000070000163a5b0 │ 225│ ├─◆─(msg::constellation_msg::PipelineId) id │ 226│ ├─◆─(url::Url) _url │ 227│ │ ├─◆─(collections::string::String) scheme │ 228│ │ │ └─◆─(collections::vec::Vec<u8>) vec │ 229│ │ ├─◆─(url::SchemeData) scheme_data │ 230│ │ ├─◆─(core::option::Option<collections::string::String>) query │ 231│ │ └─◆─(core::option::Option<collections::string::String>) fragment │ 232│ ├─◆─(std::sync::mpsc::Receiver<gfx::paint_task::LayoutToPaintMsg>) layout_to_paint_port│ 233│ ├─◆─(std::sync::mpsc::Receiver<gfx::paint_task::ChromeToPaintMsg>) chrome_to_paint_port│ 234└────────────────────────────────────────────────────────────────────────────────────────┘ 235``` 236 237If lldb crashes on certain lines involving the `profile()` function, it's not just you. Comment out the profiling code, and only keep the inner function, and that should do it. 238 239## Tests 240 241This is boring. But your PR won't get accepted without a test. Tests are located in the `tests` directory. You'll see that there are a lot of files in there, so finding the proper location for your test is not always obvious. 242 243First, look at the "Testing" section in `./mach --help` to understand the different test categories. You'll also find some `update-*` commands. It's used to update the list of expected results. 244 245To run a test: 246 247``` 248./mach test-wpt tests/wpt/yourtest 249``` 250 251### Updating a test: 252 253In some cases, extensive tests for the feature you're working on already exist under tests/wpt: 254 255- Make a release build 256- run `./mach test-wpt --release --log-raw=/path/to/some/logfile` 257- run [`update-wpt` on it](https://github.com/servo/servo/blob/master/tests/wpt/README.md#updating-test-expectations) 258 259This may create a new commit with changes to expectation ini files. If there are lots of changes, 260it's likely that your feature had tests in wpt already. 261 262Include this commit in your pull request. 263 264### Add a new test: 265 266If you need to create a new test file, it should be located in `tests/wpt/mozilla/tests` or in `tests/wpt/web-platform-tests` if it's something that doesn't depend on servo-only features. You'll then need to update the list of tests and the list of expected results: 267 268``` 269./mach test-wpt --manifest-update 270``` 271 272### Debugging a test 273 274See the [debugging guide](./debugging.md) to get started in how to debug Servo. 275 276## Documentation: 277 278- Servo's directory structure: [ORGANIZATION.md](./ORGANIZATION.md) 279- http://doc.servo.org/servo/index.html 280- https://github.com/servo/servo/wiki 281- http://rustbyexample.com 282- https://doc.rust-lang.org 283- Cargo & crates: http://doc.crates.io/guide.html 284- mach help: `./mach --help` 285- servo options: `./mach run -- --help` 286- servo debug options: `./mach run -- --debug help` 287 288## Ask questions 289 290### IRC 291 292IRC channels (irc.mozilla.org): 293 294- #servo 295- #rust 296- #cargo 297 298### Mailing list 299 300https://lists.mozilla.org/listinfo/dev-servo 301