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