1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
|
# Hacking Servo - Quickstart
This guide covers the basic things one needs to know to start hacking Servo.
It doesn't cover how Servo works (see the [documentation](#documentation) section for that),
but describes how to setup your environment to compile, run, and debug Servo. For information
on the [Github Workflow](https://github.com/servo/servo/wiki/Github-workflow) and some helpful
[Git Tips](https://github.com/servo/servo/wiki/Github-workflow#git-tips), see the
[Wiki](https://github.com/servo/servo/wiki).
## Building Servo
Building Servo is quite easy. Install the prerequisites described in the [README](../README.md) file, then type:
```shell
./mach build -d
```
*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).*
The `-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.
You can use and build a release build and a debug build in parallel.
## Running Servo
The servo binary is located in `target/debug/servo` (or `target/release/servo`). You can directly run this binary, but we recommend using `./mach` instead:
``` shell
./mach run -d -- http://github.com
```
… is equivalent to:
``` shell
./target/debug/servo http://github.com
```
If you build with `-d`, run with `-d`. If you build with `-r`, run with `-r`.
## ./mach
`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`.
``` shell
./mach run -d [mach options] -- [servo options]
```
The `--` 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.
## Mach and Servo options
This guide only covers the most important options. Be sure to look at all the available mach commands and the servo options:
``` shell
./mach --help # mach options
./mach run -- --help # servo options
```
## Some basic Rust
Even 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:
- [Match](https://doc.rust-lang.org/book/first-edition/match.html) and [Patterns](https://doc.rust-lang.org/book/first-edition/patterns.html)
- [Options](http://rustbyexample.com/std/option.html)
- [Expression](http://rustbyexample.com/expression.html)
- [Traits](http://rustbyexample.com/trait.html)
- 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)
This 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.
For more exhaustive documentation:
- [doc.rust-lang.org](https://doc.rust-lang.org)
- [rust by example](http://rustbyexample.com)
## Cargo and Crates
A 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.
`Cargo.toml` files list the dependencies. You can edit this file.
For example, `components/net_traits/Cargo.toml` includes:
```
[dependencies.stb_image]
git = "https://github.com/servo/rust-stb-image"
```
But 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:
```
[[package]]
name = "stb_image"
source = "git+https://github.com/servo/rust-stb-image#f4c5380cd586bfe16326e05e2518aa044397894b"
```
This 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.
See [Cargo's documentation about Cargo.toml and Cargo.lock files](http://doc.crates.io/guide.html#cargotoml-vs-cargolock).
## Working on a Crate
As 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!).
For 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*.
This is how my projects are laid out:
```
~/my-projects/servo/
~/my-projects/mozjs/
```
Both folder are git repositories.
To 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.
Both 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).
If 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:
``` toml
[replace]
"https://github.com/servo/mozjs#mozjs_sys:0.0.0" = { path = '../mozjs' }
```
If 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:
``` toml
[replace]
"mozjs_sys:0.0.0" = { path = '../mozjs' }
```
Both will tell any cargo project to not use the online version of the dependency, but your local clone.
For more details about overriding dependencies, see [Cargo's documentation](http://doc.crates.io/specifying-dependencies.html#overriding-dependencies).
## Debugging
### Logging:
Before 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:
``` shell
./mach run -d -- --help
./mach run -d -- --debug help
```
A typical command might be:
``` shell
./mach run -d -- -i -y 1 -t 1 --debug dump-layer-tree /tmp/a.html
```
… to avoid using too many threads and make things easier to understand.
On macOS, you can add some Cocoa-specific debug options:
``` shell
./mach run -d -- /tmp/a.html -- -NSShowAllViews YES
```
You can also enable some extra logging (warning: verbose!):
```
RUST_LOG="debug" ./mach run -d -- /tmp/a.html
```
Using `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):
```
RUST_LOG="debug" ./mach run -d -- -i -y 1 -t 1 /tmp/a.html 2>&1 | ts -s "%.S: " | tee /tmp/log.txt
```
You 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.
Use `RUST_BACKTRACE=1` to dump the backtrace when Servo panics.
### println!()
You 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:
``` rust
println!("foobar: {:?}", foobar)
```
usually just works. If it doesn't, maybe some of foobar's properties don't implement the right trait.
### Debugger
To run the debugger:
``` shell
./mach run -d --debug -- -y 1 -t 1 /tmp/a.html
```
This will start `lldb` on Mac, and `gdb` on Linux.
From here, use:
``` shell
(lldb) b a_servo_function # add a breakpoint
(lldb) run # run until breakpoint is reached
(lldb) bt # see backtrace
(lldb) frame n # choose the stack frame from the number in the bt
(lldb) thread list
(lldb) next / step / …
(lldb) print varname
```
And to search for a function's full name/regex:
```shell
(lldb) image lookup -r -n <name> #lldb
(gdb) info functions <name> #gdb
```
See this [lldb tutorial](http://lldb.llvm.org/tutorial.html) and this [gdb tutorial](http://www.unknownroad.com/rtfm/gdbtut/gdbtoc.html).
To inspect variables and you are new with lldb, we recommend using the `gui` mode (use left/right to expand variables):
```
(lldb) gui
┌──<Variables>───────────────────────────────────────────────────────────────────────────┐
│ ◆─(&mut gfx::paint_task::PaintTask<Box<CompositorProxy>>) self = 0x000070000163a5b0 │
│ ├─◆─(msg::constellation_msg::PipelineId) id │
│ ├─◆─(url::Url) _url │
│ │ ├─◆─(collections::string::String) scheme │
│ │ │ └─◆─(collections::vec::Vec<u8>) vec │
│ │ ├─◆─(url::SchemeData) scheme_data │
│ │ ├─◆─(core::option::Option<collections::string::String>) query │
│ │ └─◆─(core::option::Option<collections::string::String>) fragment │
│ ├─◆─(std::sync::mpsc::Receiver<gfx::paint_task::LayoutToPaintMsg>) layout_to_paint_port│
│ ├─◆─(std::sync::mpsc::Receiver<gfx::paint_task::ChromeToPaintMsg>) chrome_to_paint_port│
└────────────────────────────────────────────────────────────────────────────────────────┘
```
If 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.
## Tests
This 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.
First, 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.
To run a test:
```
./mach test-wpt tests/wpt/yourtest
```
### Updating a test:
In some cases, extensive tests for the feature you're working on already exist under tests/wpt:
- Make a release build
- run `./mach test-wpt --release --log-raw=/path/to/some/logfile`
- run [`update-wpt` on it](https://github.com/servo/servo/blob/master/tests/wpt/README.md#updating-test-expectations)
This may create a new commit with changes to expectation ini files. If there are lots of changes,
it's likely that your feature had tests in wpt already.
Include this commit in your pull request.
### Add a new test:
If 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:
```
./mach test-wpt --manifest-update
```
### Debugging a test
See the [debugging guide](./debugging.md) to get started in how to debug Servo.
## Documentation:
- Servo's directory structure: [ORGANIZATION.md](./ORGANIZATION.md)
- http://doc.servo.org/servo/index.html
- https://github.com/servo/servo/wiki
- http://rustbyexample.com
- https://doc.rust-lang.org
- Cargo & crates: http://doc.crates.io/guide.html
- mach help: `./mach --help`
- servo options: `./mach run -- --help`
- servo debug options: `./mach run -- --debug help`
## Ask questions
### IRC
IRC channels (irc.mozilla.org):
- #servo
- #rust
- #cargo
### Mailing list
https://lists.mozilla.org/listinfo/dev-servo
|