# What’s Up With BUILD.gn
This is a transcript of [What's Up With
That](https://www.youtube.com/playlist?list=PL9ioqAuyl6ULIdZQys3fwRxi3G3ns39Hq)
Episode 5, a 2023 video discussion between [Sharon ([email protected])
and Nico ([email protected])](https://www.youtube.com/watch?v=NcvJG3MqquQ).
The transcript was automatically generated by speech-to-text software. It may
contain minor errors.
---
Building Chrome is an integral part of being a Chrome engineer. What actually
happens when you build Chrome, and what exactly happens when you run those
build commands? Today, we have Nico, who was responsible for making Ninja the
Chrome default build system, to tell us more.
Notes:
- https://docs.google.com/document/d/1iDFqA3cZAUo0TUFA69cu5wEKL4HjSoIGfcoLIrH3v4M/edit
---
00:00 SHARON: Hello, and welcome to "What's Up With That," the series that
demystifies all things Chrome. I'm your host, Sharon, and today, we're talking
about building Chrome. How do you go from a bunch of files on your computer to
running a browser? What are all the steps involved? Our special guest today is
Nico. He's responsible for making Ninja, the Chrome default build system, and
he's worked on Clang and all sorts of areas of the Chrome build. If you don't
know what some of those things are, don't worry. We'll get into it. Welcome,
Nico.
00:29 NICO: Hello, Sharon, and hello, internet.
00:29 SHARON: Hello. We have lots to cover, so let's get right into it. If I
want to build Chrome at a really quick overview, what are all the steps that I
need to do?
00:41 NICO: It's very easy. First, you download `depot_tools` and add that to
your path. Then you run fetch Chromium. Then you type `cd source`, run `gclient
sync`, `gn gen out/GN`, and `ninja -C out/GN chrome`. And that's it.
00:53 SHARON: Wow. Sounds so easy. All right. We can wrap that up. See you guys
next time. OK. All right. Let's take it from the start, then, and go over in
more detail what some of those things are. So the first thing you mentioned is
`depot_tools`. What is that?
01:11 NICO: `depot_tools` is just a collection of random utilities for - like,
back in the day, for managing subversion repositories, nowadays for pulling
things from git. It contains Ninja and GN. Just adds a bunch of stuff to your
path that you need for working on Chrome.
01:25 SHARON: OK. Is this a Chrome-specific thing, or is this used elsewhere,
too?
01:33 NICO: In theory, it's fairly flexible. In practice, I think it's mostly
used by Chromium projects.
01:39 SHARON: OK, all right. And there, you mentioned Ninja and GN. And for
people - I think most people who are watching this have built Chrome at some
point. But what is the difference between Ninja and GN? Because you have your
build files, which are generally called Build.gn, and then you run a command
that has Ninja in it. So are those the same thing? Are those related?
01:57 NICO: Yes. So GN is short for Generate Ninja. So Ninja is a build system.
It's similar to Make. It basically gets a list of source files and a list of
build outputs. And then when you run Ninja, Ninja figures out which build steps
do I have to run, and then it runs them. So it's kind of like Make but simpler
and faster. And then GN - and Ninja doesn't have any conditionals or anything,
so GN is - just a built - it describes the build. And then it generates Ninja
files.
02:34 SHARON: OK.
02:34 NICO: So if you want to do, like, add these files only if you're building
for Windows, this is something you can do, say, in GN. But then it only
generates a Windows-specific Ninja file.
02:46 SHARON: All right. And in terms of when you mention OS, so there's a
couple places that you can specify different arguments for how you build
Chrome. So you have your gclient sync - sorry, your gclient file, and then you
have a separate args.gn. And in both of these places, you can specify different
arguments. And for example, the operating system you use - that can be
specified in both places. There's an OS option in both. So what is the purpose
of the gclient file, and what is the purpose of the args.gn file?
03:25 NICO: Yes. So gclient reads the steps file that is at the root of the
directory, and the DEPS file basically specifies dependencies that Chrome pulls
in. It's kind of similar to git submodules, but it predates git, so we don't
use git submodules also for other reasons. And so if you run gclient sync, that
reads the DEPS file, the Chrome root, and that downloads a couple hundred
repositories that Chrome depends on. And then it executes a bunch of so-called
hooks, which are just Python scripts, which also download a bunch of more
stuff. And the hooks and the dependencies are operating system dependent, so
gclient needs to know the operating system. But the build also needs to know
the operating system. And GN args are basic things that are needed for the
builds. So the OS is something that's needed in both places, but many GN args
gclient doesn't need to know about. For example, if you enable DCHECKs, like
Peter discussed a few episodes ago, that's a GN-only thing.
04:26 SHARON: All right. That sounds good. So let's see. When you actually -
OK. So when you run Chrome and you - say you build Chrome, right? A typical
example of a command to do that would be, say, `autoninja -C out/default
content`, right? And let's just go through each part of that and say what each
of those things is doing and what happens there. Because I think that's just an
example from one of the starter docs. That's just the copy and paste command
that they give you. So autoninja seems like it's based on Ninja. What is the
auto they're doing for us?
05:15 NICO: Yeah. So autoninja is also one of the things that's just
`depot_tools`. It's a very - or it used to be a very thin wraparound Ninja. Now
it's maybe a little thicker, but it's optional. You don't have to use autoninja
if you don't want to. But what it does is basically - like, it helps - So
Chrome contains a lot of code. So we have this system called Goma, which can
run all the C++ compilations in a remote data center. And if you do use the
system, then you want to build with a very high build parallelism. You want to,
say, `-j 1000` or what and run, like, a thousand bit processes in parallel. But
if you're building locally, you don't want to do that. So what autoninja
basically does - it looks at your args.gn file, sees if you have enabled Goma,
and if so, it runs Ninja with many processes, and else, it runs it with just
one process per core, or something like that. So that's originally all that
autoninja does. Nowadays, I think it also uploads a bunch of stuff. But you can
just run which autoninja, and that prints some path, and you can just open that
in the editor and read it. I think it's still short enough to fairly quickly
figure out what it does.
06:17 SHARON: OK. What does `-C` do? Because I think I've been using that this
whole time because I copied and pasted it from somewhere, and I've just always
had it.
06:28 NICO: It says - it changes the current directory where Ninja runs, like
in Make. So it basically says, change the current directory to out/GN, or
whatever you build directory is, and then run the build from there. So for
Chrome, the build always - the current directory during the build is always the
build directory. And then Ninja looks for a file called build.ninja in the
current directory, so GN writes build.ninja to out/GN, or whatever you build
directory is. And then Ninja finds it there and reads it and does its thing.
06:57 SHARON: All right. So the next part of this would be out/default, or out
slash something else. So what are out directories, and how do we make use of
them?
07:11 NICO: An out directory - it's just a build directory. That's where all
the build artifacts go to, all the generated objects files, executables, random
things that are generated during the build. So it can be any directory, really.
You can make up any directory name that you like. You can build your Chrome in,
I don't know, fluffy/kitten, or whatever. But I think most people use out just
because it's in the global `.gitignore` file already. Then you want to use
something that's two directories deep so that the path from the directory to
the source is always `../..`. And that makes sure that this is deterministic.
We try to have a so-called deterministic build, where you get exactly the same
binary when you build Chrome at the same revision, independent of the host
machine, more or less. And the path from the build directory to the source file
is something that goes into debug info. So if you want to have the same build
output as everyone else, you want a build directory path that's two directories
deep. And the names of those two directories doesn't really matter. So what
some people do is they use out/debug for the debug builds and out/release for
their release builds. But it's really up to you.
08:26 SHARON: Right. Other common ones are, like, yeah. ASan is a common one,
different -
08:33 NICO: Right.
08:33 SHARON: OSes. Right. So you mentioned having a deterministic build. And
assuming you're on the same version of Chrome, at the same checkout,
tip-of-tree, or whatever as someone else, I would have expected that all of the
builds are just deterministic, but maybe that's because of work that people
like you and the build team have done. But what are things that could cause
that to be nondeterministic? Because you have all the same files. Where is the
actual nondeterminism coming from? Or is it just different configurations and
setups you have on your local machine?
09:09 NICO: Yeah, that's a great question. I always thought this would be very
easy to - but turns out it mostly isn't. We wrote a very long blog post that we
can link to it from the show notes about this. But there's many things that can
go wrong. Like for example, in C++, there's the preprocessor macro `__DATE__`,
which embeds the current date into the build output. So if you do that, then
you're time dependent already. By default, I think you end up with absolute
paths to everything in debug information. So if you build under
`/home/sharon/blah`, then that's already different from all the people who are
not called Sharon. Then there's - we run tools as part of the build that
produce output. For example, the protobuf compiler or whatnot. And so if that
binary iterates over some map, some hash map, and that doesn't have
deterministic iteration order, then the output might be different. And there's
a long, long, long, long, long list of things. Making the build deterministic
was a very big project, and there's still a few open things.
10:08 SHARON: OK, cool. So I guess it's - yeah, it's not true nondeterminism,
maybe, but there's enough factors that go into it that to a typical person
interacting with it, it does seem -
10:21 NICO: Yeah, but there's also true nondeterminism. Like, every now and
then, when we update the compiler, the compiler will write different object
files on every run just because the compiler internally iterates about some -
over some hash map. And then we have to complain upstream, and then they fix
it.
10:34 SHARON: OK. Oh, wow. OK. That's very cool. Well, thank you for dealing
with this kind of stuff so people like us don't have to worry about it. OK. And
the last part of our typical build thing is content. So what is content in this
context? If you want to learn about content more in general, check out
episode 3. But in this case, what does that mean?
10:58 NICO: So just a build target. So I think people - at least I usually
build some executable. I usually build, I don't know, `base_unittests` or
`unit_tests` or Chrome or content shell or what. And it's just - so in the
Ninja files, there's basically - there's many, many lines that go, if you want
to build this file, you need to have these inputs and then run this command. If
you want to build this file, instead, you need these other files. You need to
run this other command. So for example, if you want to build `base_unittests`,
you need a couple thousand object files, and then you need to run the linkers,
what's in there. And so if you tell Ninja - the last thing you give it -
basically, it tells Ninja, what do you want to build? So if you say, `ninja -C
out/GN content_shell` or what, then Ninja is like, let's look at the line that
says `content_shell`. And then it checks - I need these files, so it builds all
the prerequisites, which usually means compiling a whole bunch of files. And
then it runs the final command and runs the linker. So Ninja basically decides
what it needs to do and then invokes other commands to do the actual work.
12:08 SHARON: OK, makes sense. So say I run the build - so say I built the
target Chrome, which is the one that actually is an executable, and that's
what - if you run that, the browser is built from it. So say I've built the
Chrome build target. How do I run that now?
12:31 NICO: Well, it's written - so normally, the thing you give to Ninja is
actually a file name. And the `-C` change current directory. So if you say, `-C
out/release chrome`, then this creates the file `out/release/chrome`. It just
creates that file in the out directory. So to run that, you just run
`out/release/chrome`, and hopefully it'll start up and work.
12:54 SHARON: Great. Sounds so easy. So you mentioned earlier something called
Goma, which had remote data centers and stuff. Is this something that's
available to people who don't work at Google, or is this one of the
Google-specific things? Because I think so far, everything mentioned is anyone,
anywhere can do all this. Is that the case with Goma, also?
13:14 NICO: Yeah. For the other things - so Ninja is actually something that
started in Chrome land, but that's been fairly widely adopted across the world.
Like, that's used by many projects. But yeah, Goma - I think it's kind of like
distcc. Like, it's a distributed compiler thing. I think the source code for
both the client and the server are open source. And we can link to that. But
the access to the service, I think, isn't public. So they have to work at
Google or at a partner company. I think we hand out access to a few partners.
And as far as I know, there's a few independent implementations of the
protocol, so other people also use something like Goma. But as far as I know,
these other services also aren't public.
13:53 SHARON: OK. Right. Yeah, because I think one of the main things is - I
mean, as someone who did an internship on Chrome, after, I was like, I'll
finish some of these remaining to do items once I go back to school, right? And
then I started to build Chrome on my laptop, just a decent laptop, but still a
laptop, and I was like, no, I guess I won't be doing that.
14:17 NICO: No, it's doable. You just need to be patient and strategic. Like, I
used to do that every now and then. You have to start the build at night, and
then when you get up, it's done. And if you only change one or two CC files,
it's reasonably fast. It's just, full builds take a very long time.
14:29 SHARON: Yeah, well, yeah. There was enough stuff going on that I was
like, OK. We maybe won't do this. Right. Going back to another thing you
mentioned is the compiler and Clang. So can you tell us a bit more about Clang
and how compiling fits into the build process?
14:50 NICO: Yeah, sure. I mean, compiling just means - almost all of Chrome
currently is written in C++, and compiling just means taking a CC file, like a
C++ file, and turning it into - turning that into an object file. And there are
a whole bunch of C++ compilers. And back in the day, we used to use many, many
different C++ compilers, and they're all slightly different, so that was a
little bit painful. And then the C++ language started changing more frequently,
like with C++ 11, 14, 17, 20, and so on. And so that was a huge drain on
productivity. Updating compilers was always a year-long project, and we had to
update, like, seven different compilers, one on Android, iOS, Windows, macOS,
Android, Fuchsia, whatnot. So over time, we moved to - we moved from using
basically the system compiler to using a hermetically built Clang that we
download as a gclient DEPS hook. So when you run gclient sync, that downloads a
prebuilt Clang binary. And we use that Clang binary to build Chrome on all
operating systems. So if one file builds for you on your OS, then chances are
it'll build on all the other OSes because it's built by the same compiler. And
that also enables things like cross builds, so you can build Chrome for Windows
on Linux if you want to because your compiler is right there.
16:11 SHARON: Oh, cool. All right. I didn't know that. Is there any reason,
historically, that Clang beat out these other compilers as the compiler of
choice?
16:24 NICO: Yes. So it's basically - I think when we looked at this - so Clang
is basically the native compiler on macOS and iOS, and GCC is kind of the
system compiler on Linux, I suppose. But Clang has always had very good GCC
compatibility. And then on Windows, the default choice is Visual Studio. And we
still want to link against the normal Microsoft library, so we need a compiler
that's ABI-compatible with the Microsoft ABI. And GCC couldn't do that. And
Clang also couldn't do that, but we thought if we teach Clang to do that, then
Clang basically can target all the operating systems we care about. And so we
made Clang work on Windows, also with others. But there was a team funded by
Chrome that worked on that for a few years. And also, Clang has pretty good
tooling interface. So for code search, we also use Clang. So we now use the
same code to compile Chrome and to index Chrome for code search.
17:28 SHARON: Oh, cool. I didn't know that either, so very interesting. OK.
We're just going to keep going back. And as you mention more things, we'll
cover that, and then go back to something you previously mentioned. So next on
the list is gclient sync. So I think for everyone who's ever worked on Chrome,
ever, especially at the start, you're like, I'll build Chrome. You build your
target, and you get these weird errors. And you look at it, and you think, oh,
this isn't some random weird spot that I definitely didn't change. What's going
on? And you ask a senior team member, and they say to you, did you run gclient
sync? And you're like, oh, I did not. And then you run it, and suddenly, things
are OK. So what else is going - you mentioned a couple of things that happen.
So what exactly does gclient sync do?
18:13 NICO: Yeah. So as I - that's this file at the source root called DEPS,
D-E-P-S, all capital letters. And when you update - if you git pull the Chrome
repository, then that also updates the DEPS file. And then this DEPS file
contains a long list of revisions of dependent projects. And then when you run
gclient sync, it basically syncs all these other git repositories that are
mentioned in the DEPS file. And after that, it runs so-called hooks, which like
do things download a new Clang compiler and download a bunch of other binaries
from something called the CIPD, for example, GN. But yeah, basically makes sure
that all the dependencies that are in Chrome but that aren't in the Chrome
repository are also up to date. That's what it does.
19:06 SHARON: OK. Do you have a rough ballpark guess of how many dependencies
that includes?
19:13 NICO: Its operating system dependent. I think on Android we have way
more, but it's on the order of 200. Like, 150 to 250.
19:25 SHARON: Sounds like a lot. Very cool. OK. In terms of - speaking of other
dependencies, one of the top-level directories in Chrome is `//third_party`,
and that seems in the same kind of direction. So how does stuff in
`//third_party` work in terms of building? Can you just build them as targets?
What kind of stuff is in there? What can you and can you not build? Like, for
example, Blink is one of the things in `//third_party`, and lots of people -
that's a big part of it, right? But a lot of things in there are less active
and probably less big of a part of Chrome. So does `//third_party` just build
anything else, or what's different about it?
20:09 NICO: And that's a great question. So Blink being in `//third_party` is a
bit of a historical artifact. Like, most things - almost all of the things in
`//third_party` is basically code that's third-party code. That's code that we
didn't write ourselves. And Chrome's secret mission is to depend on every other
library out there in the world. No, we depend on things like libpng for reading
PNG files, libjpeg for reading all of - libjpeg-turbo these days, I guess, for
reading JPEG files, libxml for reading XML, and so on. And, well, that's many
dependencies. I won't list them all. And some of these third-party dependencies
are just listed in the DEPS file that we talked about. And so they basically -
like, when gclient sync runs, it pulls the code from some git repository that
contains the third-party code and puts it into your source tree. And for other
third-party code, we actually check in the code into the Chrome main repository
instead of DEPSing it in. There are trade-offs, which approach to choose. We do
both from time to time. But yeah. Almost no third-party dependency has a GN
file upstream, so usually what you do is you have to write your own BUILD.gn
file for the third-party dependency you want to add. And then after that, it's
fairly normal. So for a library, if you want to add a dependency on libfoo,
usually what we do is you add - you create third-party libfoo, and you put
BUILD.gn in there. And then you add a DEPS entry that syncs the actual code to
a third-party libfoo source or something. Yes.
21:37 SHARON: All right. Sounds good. Again, you mentioned BUILD.gn files, and
that's, as, expected a big part of how building works. And that's probably the
part that most people have interacted more with, outside of just actually
running whatever command it is to build Chrome. Because if you create, delete,
rename any files, you have to update it in some BUILD.gn file. So can you walk
us through the different things contained in a BUILD.gn file? What are all the
different parts?
22:12 NICO: Sure. So there's a great presentation by Brett, who wrote GN, that
we can link to. But in a summary, it's - BUILD.gn contains build targets, and
the build target normally is like - it doesn't have to be, but usually, it's a
list of CC files that belong together and that either make up a static library
or a shared library on executable. So those are the main target types for CC
code. But then you can also have custom build actions that run just arbitrary
Python code, which, for example, if you compile a protobuf - proto files into
CC and H - into C++ and header files, then we basically have a Python script
that runs protoc, the proto compiler, to produce those. And so in that case,
the action generates C++ files, and then those get built. But the other, simple
answer is libraries or executables.
23:11 SHARON: OK. One part of GN files that has caused me personally some
confusion and difficulty - which I think is maybe, depending on the part of
Chrome you work on, less of an issue - is DEPS. So you have DEPS in your GN
files, and there's also something called external DEPS. And then you have
separate DEPS files that are just called capital D-E-P-S.
23:30 NICO: Yes. Yes, there, that's some redundant - that's, again, I guess for
historical reasons. So in gclient, DEPS just means to build this target, you
first have to build these other targets. Like, this target depends - uses this
other code. And in different contexts, it kind of means different things. So
for example - I think if an executable depends on some other target, then that
external executable is linked - that other target is also linked in. If base
unit test depends on the base library, which in a normal build is a static
library - like in a normal build? Like in a release build, by default, it's a
static library. And so if base unit test is built, it first creates a static
library and then links to it. And then base itself might depend on a bunch of
third-party things, libraries, which means when base unit tests is linked, it
links base, but then it also links against basis dependencies. So that's one
meaning of DEPS. Another meaning, like these capital DEPS files, that's
completely distinct. Has nothing to do with GN, I'm sad to say. And that's just
for enforcing layering. Those predate GN, and they are for enforcing layering
at a fairly coarse level. They say, code in this directory may include code
from this other directory but not from this third directory. For example, a
third - like, Blink must not - may include stuff from base, but must not
include anything from, I don't know, the Chrome layer or something.
25:18 SHARON: Right, the classic content Chrome layering, where Chrome -
25:18 NICO: Right. And I think -
25:18 SHARON: content, but -
25:18 NICO: Right. And there's a step called check-deps, and that checks the
capital DEPS files.
25:24 SHARON: OK. Yeah, because before, I worked on some Fuchsia stuff, and
because we're adding a lot of new things, you're messing around with different
DEPS and stuff a lot more than I think if you worked in a typical part. Like,
now, I mostly just work within content. Unlikely that you're changing any
dependencies. But that was always a bit unclear because, for the most part, the
targets have very similar names - not exactly the same, but very similar. And
if you miss one, you get all these weird errors. And it was, yeah, generally
quite confusing.
25:55 NICO: Yeah, that's pretty confusing. One thing of the capital DEPS things
that they can do that the GN DEPS can't is if someone adds a DEPS on your
library and they add an entry to their DEPS file, that means that now at code
review time, you need to approve that they depend on you. And that's not
something we can do at the GN level. And the advantage there is, I don't know,
if you have some library and then 50 teams start depending on it without
telling you, and now you're on the hook for keeping all these 50 things
working, then with this system, you at least have to approve every time someone
adds a dependency on you, you have to say, this is fine with me. Or you can
say, actually, this is - we don't want this to be used by anyone else.
26:45 SHARON: Is there an ideal state where we don't have these DEPS files and
maybe that functionality is built into the BUILD.gn files, or is this something
that's probably going to be sticking around for a while?
26:52 NICO: That's a great question. I don't know. It seems weird, right? It's
redundant. So I think the current system isn't ideal, but it's also not
horrible enough that we have to fix it immediately. So maybe one day we'll get
around to it.
27:10 SHARON: Yeah. I think I've mostly just worked on Chrome, so I've gotten
pretty used to it. But a common complaint is people who work in Google internal
things or other, bigger - the main build system of whatever company they work
on, they come to Chrome and they're like, oh, everything's so confusing. But if
you - you just got to get used to it, but -
27:27 NICO: Right. I think if you're confused by anything, it's great if you
come to us and complain. Because you kind of become blind to these problems,
right? I've been doing this for a long time. I'm used to all the foot guns. I
know how to dodge them. And yeah. So if you're confused by anything, please
tell me personally. And then if enough people complain about something, maybe
we'll fix it.
27:55 SHARON: All right. Yeah. That's what you said. The outcome of that -
we'll see. We'll see how that goes. We'll see how many complaints you suddenly
get. Right. OK. So another thing I was interested in is right now there's a lot
of work around Rust, getting more Rust things, introducing that, memory safety,
that's good. We like it. What is involved from a build perspective for getting
a whole other language into Chrome and into the build? Because we have most of
the things C++. There's some Java in all of the Android stuff. And in some
areas, you see - you'll see a list of - you'll see a file name, and then you'll
see file name underscore and then all the different operating systems, right?
And most those are some version of C++. The Mac ones are .mm. And you have Java
ones for Android. But if you want to add an entirely different language and
still be able to build Chrome, at a high level, what goes into that?
29:00 NICO: Yeah, there's also some Swift on iOS. It's many different things.
So at first, you have to teach GN how to generate Ninja files for that
language. So when a CC file is built, then basically the compiler writes out a
file that says, here are all the header files I depend on. So if one of them
gets touched, the compiler - or Ninja knows how to rebuild those. So you need
to figure out how the Rust compiler or the Swift compiler track dependencies.
You need to get that information out of the compiler into the build system
somehow. And C++ is fairly easy to build. It's like a per-file basis. I think
most languages are more on a module or package base, where you build a few
files as a unit. Then you might want to think about, how can I make this work
with Goma so that the compilation can work remotely instead of locally? So
that's the build system part. Then also, especially for us, we want to use this
for some performance critical things, so it needs to be very fast. And we use a
bunch of toolchain optimization techniques to make Chrome very fast with
three-letter acronyms, such as PGO and LTO and whatnot. And LTO in particular,
that means a Link Time Optimization. That means the C++ or the Rust code is
built - is compiled into something called "bitcode." And then all the bitcode
files at link time are analyzed together so you can do cross-file in-lining and
whatnot. And for that work, the bitcodes - all the bitcode versions need to be
compatible, which means Clang and Rust need to be built against the same
version of LLVM, which is some - it's some internal compiler machinery that
defines the bitcode. So that means you have to - if you want to do
cross-language LTO, you have to update your C++ compiler and your Rust compiler
at the same time. And you have to build them at the same time. And when you
update your LLVM revision, it must break neither the C++ compiler nor the Rust
compiler. Yeah. And then you kind of want to build the Rust library from
source, so you have bit code for all of that. So it's a fairly involved - but
yeah, we've been doing a lot of work on that. Not me, but other people.
31:24 SHARON: Right. Sounds hard. And what does LTO stand for, since you used
it?
31:30 NICO: Link Time Optimization.
31:30 SHARON: All right.
31:30 NICO: And there's a blog post on the Chromium blog about this that we can
link to in the show notes that has a fairly understandable explanation what
this does.
31:43 SHARON: Yeah, all right. That sounds good. So linking, that was my next
question. As you build stuff, you sort out all of your just compile errors, you
got all your spelling mistakes out. The next type of error you might get is
linking error. So how does - can you tell us a bit more about linking in
general and how that fits into the build process?
32:01 NICO: I mean, linking - like, for C++, the compiler basically produces
one object file for every CC file. And then the linker takes, like, about
50,000 to 100,000 object files and produces a single executable. And every
object file has a list of functions that are defined in that object file and a
list of functions that are undefined in that object file that it calls that are
needed from elsewhere. And then the linker basically makes one long list of all
the functions it finds. And at the end, all of them should be defined, and all
the non-inline ones should be defined in exactly one object file. And if
they're not - if that doesn't happen, then it emits an error, and else, it
emits a binary. And the linker is kind of interesting because the only thing
you really care about is that it does its job very quickly. But it has to read
through gigabytes of data before it writes the executable. And currently, we
use a linker called `ld`, which was also written by people on the Chrome team,
and which is also fairly popular outside of Chrome nowadays. And so we wrote on
ELF linker, which is the file format used on Linux and Android, and on COFF
linker, which is the file system used on Windows, and our own Mach-O linker,
which is the file system on Apple - macOS and iOS. And our linkers are way,
way, way faster than the things that they replace. On Windows, we were, like,
10 times faster than the Windows linker. And on Mac, we're, like, four times
faster than the system linker and whatnot. The other linker vendors have caught
up a little bit, but we - I feel like Chrome has really advanced the state and
performance of linking binaries across the industry, which I think is really
cool.
33:44 SHARON: Yeah, that is really cool. And in a kind of similar vein to the
different OSes and all that kind of stuff is 32- versus 64-bit. There's some
stuff happening. I've seen people talk about it. It seems pretty important. Can
you just tell us a bit more about this in general?
34:04 NICO: Well, I guess most processors sold in the last decade or so are
64-bit. So I think on some platforms, we only support 64-bit binaries, like -
and the bit just means how wide is a pointer and has some implications on which
instructions can the compiler use. But it's fairly transparent too, I think, at
the C++ level. You don't have to worry about it all that much. On macOS, we
only support 64-bit builds. Same on iOS. On Windows, we still have 32-bit and
64-bit builds. On Linux, we don't publicly support 32-bit, but I think some
people try to build it. But it's really on Windows where you have both 32-bit
and 64-bit builds. But the default bits is 64-bit, and you can say, if you say
target CPU equals x86, I think, in your args.gn, then you get a 32-bit build.
But it should be fairly transparent to you as a developer, unless you write
assembly.
35:02 SHARON: How big of an effort would it be to get rid of 32-bit on Windows?
Because Windows is probably the biggest Chrome-using platform, and also,
there's a lot of versions out there, right? So -
35:15 NICO: Oh, yeah.
35:15 SHARON: How doable?
35:15 NICO: I think that the biggest platform is probably Android. But yeah,
Android is also 32-bit, at least on some devices at the moment. That's true. I
don't know. I think we've looked into it and decided that we don't want to do
that at the moment. But I don't know details.
35:33 SHARON: And you mentioned ARM. So is there any - how much does the Chrome
build team - are they concerned with the architecture of these processors? Is
that something that, at the level that you and the build team have to worry
about, or is it far enough - a few layers down that that's -
35:47 NICO: It's something we have to worry about at the toolchain team. So we
update the scaling compiler every two weeks or so, which means we pull in all -
around 1,000 changes from upstream contributors that work on LVM spread across
many companies. And we have to make sure this doesn't break from on 32-bit ARM,
64-bit ARM, 32-bit Intel, 64-bit Intel, across seven different operating
systems. And so fairly frequently, when we try to update Clang tests start
failing on, I don't know, 32-bit Windows or on 64-bit iOS or some very specific
configuration. And then we have to go and debug and dissect and figure out
what's going on and work with upstream to get that fixed. So yeah. That's
something we have to deal with at the toolchain team, but hopefully, it's -
hopefully, like the normal Chrome developer is isolated from that for the most
part.
36:45 SHARON: I think so. It's not - if I weren't asking all these other
questions, it's something that almost never crosses my mind, right? So that
means you're all doing a very good job of that. Thank you very much. Much
appreciated. And jumping way back, you mentioned earlier indexing the code
base, code search. So I make a change. I submit it. I upload it. It eventually
ends up in code search. So how does that process work? And what goes into
indexing? Because before, when I was working on Fuchsia all the Fuchsia code
wasn't indexed, so you couldn't do the handy thing of clicking a thing and
seeing where it was defined. You had to actually look it up. And once you got
that, it was like, oh my gosh, so much better. So can you just tell us a bit
more about that process?
37:30 NICO: Sure, yeah. The Chrome has a pretty good code search feature, I
think, codesearch.chromium.org or cs.chromium.org. Basically, we have a bot
that runs, I think, every six hours or so, pulls the latest code, bundles it
up, sends it to some indexer service that then also uses Clang to analyze the
code. Like, for C++, I think we also index Java. We probably don't index Rust
yet, but eventually we will. And then it generates - for every word, it
generates metadata that says, this is a class. This is an identifier. And so if
you click on it, if you click on a function, you have the option of jumping to
the definition of the function, to the declaration, to all the calls, all the
overrides, and so on. And that updates ideally several times a day and is
fairly up to date. And we built the index, I think, for most operating systems.
So you can see this is called here on Linux, here on Windows, and what not.
38:32 SHARON: OK. Sounds good. Very useful stuff. And I don't know if this is
part of the build team's jurisdiction, but when you are working on things
locally, you have some git commands, and then you have some git-cl commands.
38:43 NICO: Mm-hmm.
38:48 SHARON: So the git commands are your typical ones - git pull, git rebase,
git stash, that kind of thing. And then you have git-cl commands, which relate
more to your actual CL in Gerrit. So git-cl upload, git-cl status. That'll show
you all your local branches and if they have a Gerrit change associated with
them. So what's the difference between git and git-cl commands?
39:18 NICO: I'm sorry. So this is basically a git feature. If you call git-foo,
then git looks for git-foo on your path. So you can add arbitrary commands to
git if you want to. And git-cl is just something that's in `depot_tools`.
Again, there's git-cl in `depot_tools`, and you can open that and see what it
does. And it'll redirect to `git_cl.py`, I think, which is a fairly long and
hairy Python script. But yeah. It's basically Gerrit integration, as you say.
So you can use that to send try jobs, `git cl try`. To upload, as you say, you
can use `git cl issue` to associate your current branch with a remote Gerrit
review, `git cl patch` to get a patch off Gerrit and patch it into your local
thing, `git cl web` to open the current thing in a web browser. Yeah, git-cl is
basically - git-cl help to see all the git-cl commands, or - yeah. If you have
a change that touches, like, 1,000 files, you can run `git cl split`, and it'll
upload 500 reviews. But that's usually too granular, and I wouldn't recommend
doing that. But it's possible.
40:25 SHARON: Right. Do you have a - [DOORBELL DINGS]
40:25 NICO: Oops, sorry.
40:25 SHARON: commonly - yeah.
40:30 NICO: Oh, sorry. There was - the door just rang. Maybe you didn't hear
it. Sorry.
40:30 SHARON: All right. It's all good. Do you have a lesser known git or
git-cl command that you use a lot or -
40:41 NICO: Well, I -
40:41 SHARON: is your favorite? [LAUGHS]
40:46 NICO: It's not lesser known to me, so I wouldn't know. I don't know. I
use `git cl upload` a lot.
40:53 SHARON: Right. Well, you have to use `git cl upload`, right?
40:53 NICO: I use -
40:53 SHARON: Well, you don't - maybe not but -
40:53 NICO: `git cl try` to send try jobs from my terminal, `git cl web` to see
what's going on, `git cl patch` a lot to patch stuff in locally. If I'm doing a
code review and I want to play with it, I patch it in, build a local, and see
how things are working.
41:12 SHARON: Yeah. When I patch in a thing, I go from the cl page on Gerrit
and then click the down patch thing, but -
41:21 NICO: No, even `git cl patch -b` and then some branch name, and then you
just patch - paste the Gerrit review URL.
41:28 SHARON: Oh, cool.
41:28 NICO: So it's just, yeah, Control-L to focus the URL bar. Control-C
Alt-Tab `git cl patch -b blah`, Paste, Enter, and then you have a local branch
with the thing.
41:36 SHARON: All right. Yeah, a lot of these things, once you learn about
them - at first you're like, whoa, and then you use them, and then they're not
lesser known to you, but you tell other people also a common - so another one
would be `git cl archive`, which will -
41:47 NICO: Oh, yeah, yeah.
41:47 SHARON: get rid of any local branches associated with a closed Gerrit
branch, so that's very handy, too.
41:53 NICO: Yes.
41:53 SHARON: So it's always fun to learn about things like that.
41:59 NICO: Are you fairly tidy with your branches? How many open branches do
you usually have?
41:59 SHARON: [LAUGHS] I used to be more tidy. When I tried to do a cleanup
thing, I had more branches. I think right now I've got around 20-something
branches. I like having not very many. I think to some people, that's a lot. To
some people, that's not very many. I mean, ideally, I have under five, right?
[LAUGHS] But -
42:18 NICO: I don't know. I usually have a couple 10, sometimes. Have a bunch
of machines. I think on some of them it's over 100, but yeah. Every now and
then, I run `git cl archive` and it removes half of them, but -
42:29 SHARON: Yes. All right, cool. Is there anything that we didn't cover so
far that you would like to share? So things that maybe you get asked all the
time, things that people could do better when it comes to build-related things?
Things that you can do that make the build better or don't make it worse, that
kind of thing? Or just anything else you would like to get out there?
42:58 NICO: I guess one thing that's maybe implicitly stated, but currently not
explicitly documented, as far as I know, but I'm hoping to change that, is - so
Chrome tries to have a quiet build. Like, if you build this zero build output,
except that one Ninja file, Ninja line that's changing, right? There's, well,
another code basis - I think it's fairly common - that there's many screenfulls
of warning that scroll by. And we very explicitly try not to do that because if
the build emits lots of warnings, then people just learn to ignore warnings. So
we think something should either be a serious problem that people need to know
about, then it should be an error, or it should be not interesting. Then it
should be just quiet. So if you add a build step that adds a random script, the
script shouldn't print anything, just about progress. Shouldn't say, doing
this, doing this, doing this. Should either print something and say something's
wrong and fail those build step or not say anything. So that's one thing.
43:51 SHARON: That's - yeah, that's true.
43:51 NICO: And the other thing -
43:51 SHARON: Like, you only really get a bunch of terminal output if you have
a compile or a linker error, whatever.
43:57 NICO: Right.
43:57 SHARON: I hadn't ever considered that. If you build something and it
works, you get very few lines of output. And I hadn't ever thought that was
intentional before, but you're right in that if it was a ton, you would just
not look at any of it. So yeah, that's very cool.
44:09 NICO: Yeah. And on that same note, we don't do deprecation warnings
because we don't do any warnings. So if people - like, people like deprecating
things, but people don't like tidying up calls to deprecated functions. So if
you want to deprecate something in Chrome, the idea is basically, you remove
all callers, and then you remove the deprecated thing. And we don't allow you
to say - to add a warning that tells everyone, hey, please, everyone, remove
your calls. The onus is on the person who wants to deprecate something instead
of punting that to everyone else.
44:46 SHARON: Yeah, I mean, the thing that I was working on has a deprecating
effect, so removing callers, which is why I have so many branches. But I've
also seen presubmit warnings for if you include something deprecated. So - oh,
yeah, and there's presubmit, too. OK, we'll get to that also. [LAUGHS] Tell us
more about all of this.
45:05 NICO: About presubmits? Yeah, presubmits - presubmits are terrible.
That's the short summary. So if you run a `git cl presubmit`, it'll look at a
file called presubmit.py, I think, in the current directory, and maybe in all
the directories of files - of directories that contain files you touched or
something like that. But you can just open the top-level presubmit.py file, and
there's a couple thousand lines of Python where basically everyone can add
anything they want without much oversight, so it's a fairly long - at least
historically, that used to be the case. I don't know if that's still the case
nowadays. But yeah, it's basically like a long list of things that random
people thought are good if they - like, presubmits are something that are run
before you upload, also, implicitly. And so you're supposed to clean them up.
And [INAUDIBLE] many useful things. For example, nowadays we require most code
to be autoformatted so that people don't argue about where semicolons should go
or something silly like that. So one of the things it checks is, did you run
`git cl format`, which runs, I guess, Clang format for C++ code and a bunch of
custom Python scripts for other files. But it's also - presubmits have grown
organically, and there isn't - they're kind of unowned and they're very, very
slow. And I think some people have tried to improve them recently, and they're
better than they used to be, but I don't love presubmits, I guess is the
summary. But yeah, it's another thing to check invariants that we would like to
be true about our code base.
46:48 SHARON: Yeah. I mean, I think - yes, spelling is something I think it
also checks.
46:54 NICO: It checks spelling? OK.
46:54 SHARON: Or maybe that's a separate bot in Gerrit.
46:59 NICO: Oh, yeah, yeah, yeah, yeah. Like, there's this thing called -
what's its name?
47:06 SHARON: Trucium? Tricium?
47:06 NICO: Tricium, yeah. Tricium, right. Tricium is something that adds
comments to your - automatically adds comments to your change list when you
upload it. And Tricuium can do spelling correction, but it can also - it runs
something called Clang Tidy, which is basically a static analysis engine which
has quite a few false positives, so sometimes it complains about something
that - but it's actually incorrect, and so we don't put that into the compiler
itself. So we've added a whole bunch of warnings to the compiler for things
that we think are fairly buggy. But Clang Tidy is - but these warnings have to
be - they have to have a very low false positive rate. Like, if they complain,
they should almost always be right. But sometimes, for static analysis, it's
hard to be right. Like, you can say this might be wrong. Please be sure. But
this is not something the compiler can say, so we have this other system called
Clang Tidy which also adds a comment to your C++ code which says, well, maybe
this should be a reference instead of a copy, and things like that.
48:04 SHARON: Yeah. And I think it - I've seen it - it checks for unused
variables and other - there's been useful stuff that's come from comments from
there, so definitely. All right. Very cool. So if people are interested in all
this build "infra-y" kind of stuff and they want to get more into it, what can
they do?
48:32 NICO: We have a public [email protected] mailing list. It's very low
volume, but if you want to reach out, you can send an email there and a few of
us will see your email and interact with you. And there's also I think the tech
build on crbug. So you can just look for build bugs and fix all our bugs for
us. That'd be cool.
48:51 SHARON: [LAUGHS]
48:51 NICO: And if there's anything specific, just talk to local OWNERS. Or if
you feel this is just something you're generally interested in and you're
looking for a project, you can talk to me, and I probably have a long list of -
I do have a long list of somewhat beginner-friendly projects that people could
help out with, I guess.
49:15 SHARON: Yeah. I mean, I think being able to - if you're looking for a
20%y kind of project or something else. But knowing how things actually get put
together is always a good skill and definitely applicable to other things. It's
the kind of thing where the more low level-knowledge you have, the more - it
works - it applies to things higher up, but not necessarily the other way
around, right?
49:34 NICO: Mm-hmm.
49:34 SHARON: So having that kind of understanding is definitely a good thing.
All right. Any last things you'd like to mention or shout out or cool things
that you want people to know about? [LAUGHS]
49:48 NICO: I guess -
49:48 SHARON: Or what - yeah, quickly, what is the future of the whole build
thing? Like, what's the ideal situation if -
49:55 NICO: Ideally, it'll all be way faster, I guess is the main thing. But
yeah, yeah, I think build speed is a big problem. And I'm not sure we have the
best handle on that. We're working on many things, but - not many. A bunch of
things. But it's - like, people keep adding all that much code, so if y'all
could delete some code, too, that would help us a lot. I mean, having -
supporting more languages is something we have to - this is something that's
happening. Like, Rust is happening. We are also on iOS also using Swift.
Currently, we can't LTO Swift with the rest because that's on a different OEM
version. There's this - in C++ - we keep upgrading C++ versions. So Peter
Kasting is working on moving us to C++20. And then 23, we'll have them, and so
on. There's maybe C++ modules at some point, which may or may not help with
build speed. And there's a bunch of tech debt that we need to clean up, but
that's not super interesting.
51:24 SHARON: I don't know. I think people in Chrome in general are more
interested and care about reducing tech debt in general, right? A lot of people
I know would be happy to just do tech debt clean-up things only, right?
Unfortunately, it doesn't really work out for job reasons. But a lot of people,
I think, are interested in, I think, in higher proportions than maybe other
places.
51:47 NICO: It depends on the tech debt. Some of it might work out for job
reasons. But, yeah.
51:54 SHARON: Yeah. I mean, some of it is easier than others, too, right? Some
of it is like, yeah, so, OK, well, go delete some code. Go clean up some
deprecated calls. [LAUGHS] All that.
52:08 NICO: Yeah, and again, I think finishing migrations is way harder than
starting them, so finish more migrations, start fewer migrations. That'd be
also cool.
52:16 SHARON: All right. I am sure everyone listening will go and do that right
away.
52:21 NICO: Yep.
52:21 SHARON: And things will immediately be better.
52:27 NICO: They've just been waiting to hear that from me, and now they're
like, ah, yeah, right. That makes sense.
52:27 SHARON: Yeah, yeah. All right. Well, you all heard it here first. Go do
that. Things will be better, et cetera. So all right. Well, thank you very
much, Nico, for being here answering all these questions. I learned a lot. A
lot of this is stuff that - everyone who works on Chrome builds Chrome, right?
But you can get by with a very minimal understanding of how these things are.
Like, you see your - you follow the Intro to Building Chrome doc. You copy the
things. You're like, OK, this works. And then you just keep doing that until
you have a problem. And depending on where you work, you might not have
problems. So it's very easy to know very little about this. But obviously, it's
so important because if we didn't have any of this infrastructure, nothing
would work. So one, I guess, thank you for doing all the stuff behind the
scenes, determinism, OSes, all that, making it a lot easier for everyone else,
but also thank you for sharing about it so people understand what's actually
going on when they run the commands they do every day.
53:31 NICO: Sure. Anytime. Thanks for having me. And it's good to hear that
it's possible to work on Chrome without knowing much about the build because
that's the goal, right? It should just work.
53:44 SHARON: Yeah.
53:44 NICO: Sometimes it does.
53:44 SHARON: [LAUGHS] Yeah. Well, thank you for all of it, and see you next
time.
53:51 NICO: Yeah. See you on the internet. Bye.
54:03 SHARON: OK. So we will stop recording -
54:03 NICO: Wee. Time for the second take.
54:03 SHARON: [LAUGHS] Let's do that, yeah, all over again.
54:11 NICO: Let's do it.
54:11 SHARON: I will stop recording.