blob: b877c89354799beffb8b24ca08a5a7d79099f330 [file] [log] [blame] [view]
brettwc6b7b8e2016-08-03 21:04:531# The Chrome Component Build
brettwdbb91a1b2016-08-03 20:14:542
3## Introduction
4
5Release builds are “static” builds which compile to one executable and
6zero-to-two shared libraries (depending on the platform). This is efficient at
7runtime, but can take a long time to link because so much code goes into a
brettw9fb64176b2016-08-04 19:22:178single binary.
9
10In a component build, many smaller shared libraries will be generated. This
11speeds up link times, and means that many changes only require that the local
12shared library be linked rather than the full executable, but at the expense of
13program load-time performance.
14
15The component build is currently the default for debug non-iOS builds (it
16doesn’t work for iOS). You can force it on for release builds using the
17[GN build arg](https://www.chromium.org/developers/gn-build-configuration):
brettwdbb91a1b2016-08-03 20:14:5418
brettwc6b7b8e2016-08-03 21:04:5319```python
20is_component_build = true
21```
brettwdbb91a1b2016-08-03 20:14:5422
brettwdbb91a1b2016-08-03 20:14:5423### How to make a component
24
25Defining a component just means using the GN “component” template instead
26of a shared library, static library, or source set. The template will
27generate a shared library when `is_component_build` is enabled, and a static
28library otherwise.
29
brettwc6b7b8e2016-08-03 21:04:5330```python
31component("browser") {
32 output_name = "chrome_browser"
33 sources = ...
34 ...
35}
36```
brettwdbb91a1b2016-08-03 20:14:5437
38Shared libraries in GN must have globally unique output names. According to GN
39style, your target should be named something simple and convenient (often
40matching your directory name). If this is non-unique, override it with the
brettwc6b7b8e2016-08-03 21:04:5341`output_name` variable.
brettwdbb91a1b2016-08-03 20:14:5442
Oksana Zhuravlovaf337c282021-03-04 20:01:1543**Note**: for information about `mojom_component` targets, see
44[Mojo documentation](/mojo/public/tools/bindings/README.md#Component-targets).
45
brettwdbb91a1b2016-08-03 20:14:5446### Dependencies between targets
47
48When a component directly or indirectly depends on a static library or source
49set, it will be linked into this component. If other components do the same,
50the static library or source set’s code will be duplicated.
51
52In a few cases (for defining some constants) this duplication is OK, but in
53general this is a bad idea. Globals and singletons will get duplicated which
54will wreak havoc. Therefore, you should normally ensure that components only
55depend on other components.
56
57### Component granularity
58
59Creating lots of small components isn’t desirable. Some code can easily get
60duplicated, it takes extra time to create the shared libraries themselves, load
61time will get worse, and the build and code can get complicated. On the other
62extreme, very large components negate the benefits of the component build. A
63good rule of thumb is that components should be medium sized, somewhere in the
64range of several dozen to several hundred files.
65
66## Exporting and importing symbols
67
68When a shared library or executable uses a symbol from a shared library, it is
69“imported” by the user of the symbol, and “exported” from the shared library
70that defines the symbol. Don’t confuse exported symbols with the public API of
71a component. For example, unit tests will often require implementation details
72to be exported. Export symbols to make the build link the way you need it, and
73use GN’s public headers and visibility restrictions to define your public API.
74
Ken Rockotca112b22018-01-25 22:36:3175Component library headers can use the `COMPONENT_EXPORT()` macro defined in
76`base/component_export.h` to annotate symbols which should be exported by
77the component. This macro takes a globally unique component name as an
78argument:
79
80```c++
81#include "base/component_export.h"
82
83class COMPONENT_EXPORT(YOUR_COMPONENT) YourClass { ... };
84
85COMPONENT_EXPORT(YOUR_COMPONENT) void SomeFunction();
86```
87
88When defining the target for your component, set:
89
90```python
91defines = [ "IS_YOUR_COMPONENT_IMPL" ]
92```
93
94This ensures that the corresponding `COMPONENT_EXPORT(YOUR_COMPONENT)`
95invocations result in symbols being marked for export when compiling the
96component target. All other targets which include the component's headers
97will not have defined `IS_YOUR_COMPONENT_IMPL`, so they will have the same
98symbols marked for import instead.
99
100## Chrome’s deprecated pattern for exports
101
102**NOTE**: This section is included for posterity, as many components in the tree
103still use this pattern for exports. New components should use
104`base/component_export.h` as described above.
brettwdbb91a1b2016-08-03 20:14:54105
brettwc6b7b8e2016-08-03 21:04:53106Write a header with the name `<component_name>_export.h`. Copy an [existing
brettwdbb91a1b2016-08-03 20:14:54107one](https://cs.chromium.org/chromium/src/ipc/ipc_export.h)
108and update the macro names. It will key off of two macros:
109
110 * `COMPONENT_BUILD`: A globally defined preprocessor definition set when the
111 component build is on.
112 * `<component_name>_IMPLEMENTATION`: A macro you define for code inside your
113 component, and leave undefined for code outside of your component. The
114 naming should match your `*_export.h` header.
115
116It will define a macro `<component_name>_EXPORT`. This will use the
117`*_IMPLEMENTATION` macro to know whether code is being compiled inside or outside
118of your component, and the `*_EXPORT` macro will set it to being exported or
119imported, respectively. You should copy an existing file and update the
120`*_EXPORT` macro naming for your component.
121
122When defining the target for your component, set:
123
brettwc6b7b8e2016-08-03 21:04:53124```python
125defines = [ "FOO_IMPLEMENTATION" ]
126```
brettwdbb91a1b2016-08-03 20:14:54127
128In your BUILD.gn file. If you have source sets that also make up your
129component, set this on them also. A good way to share this is to put the
130definition in a GN config:
131
brettwc6b7b8e2016-08-03 21:04:53132```python
133config("foo_implementation") {
134 defines = [ "FOO_IMPLEMENTATION" ]
135}
136```
brettwdbb91a1b2016-08-03 20:14:54137
138and set the config on the targets that use it:
139
brettwc6b7b8e2016-08-03 21:04:53140```python
141configs += [ ":foo_implementation" ]
142```
brettwdbb91a1b2016-08-03 20:14:54143
144The component build is only reason to use the `*_IMPLEMENTATION` macros. If
145your code is not being compiled into a component, don’t define such a macro
146(sometimes people do this by copying other targets without understanding).
147
148### Marking symbols for export
149
150Use the `*_EXPORT` macros on function and class declarations (don’t annotate
151the implementations) as follows:
152
brettwc6b7b8e2016-08-03 21:04:53153```c++
154#include "yourcomponent/yourcomponent_export.h"
brettwdbb91a1b2016-08-03 20:14:54155
brettwc6b7b8e2016-08-03 21:04:53156class YOURCOMPONENT_EXPORT YourClass { ... };
brettwdbb91a1b2016-08-03 20:14:54157
brettwc6b7b8e2016-08-03 21:04:53158YOURCOMPONENT_EXPORT void SomeFunction();
159```
brettwdbb91a1b2016-08-03 20:14:54160
brettwdbb91a1b2016-08-03 20:14:54161## Creating components from multiple targets
162
163### Static library symbol export issues
164
165Components can be made up of static libraries and GN source sets. A source set
166results in all object files from that compilation being linked into the
167component. But when code is in a static library, only those object files needed
168to define undefined symbols will be pulled in to the link. If an object file is
169not needed to link the component itself, it won’t be pulled into the link, even
170though it might have exported symbols needed by other components.
171
172Therefore, all code with exported symbols should be either on the component
173target itself or in source sets it depends on.
174
175### Splitting targets differently in static and component builds
176
177Sometimes you might have something consisting of multiple sub-targets. For
178example: a browser, a renderer, and a common directory, each with their own
179target. In the static build, they would all be linked into different places. In
180the component build, you may want to have these be in a single component for
181performance and sanity reasons. Content is such an example.
182
183The important thing is that the sub-projects not be depended on directly from
184outside of the component in the component build. This will duplicate the code
185and the import/export of symbols will get confused (see “Common mistakes”
186below).
187
188Generally the way to do this is to create browser and renderer group targets
189that forward to the right place. In static builds these would forward to
190internal targets with the actual code in them. In component builds, these would
191forward to the component.
192
193In the static build the structure will be: `//external/thing` ➜ `//foo:browser`
194➜ `//foo:browser_impl`
195
196In the component build the structure will be: `//external/thing` ➜
197`//foo:browser` ➜ `//foo:mycomponent` ➜ `//foo:browser_impl`
198
199Set GN visibility so that the targets with the code can only be depended on by
200targets inside your component.
201
brettwc6b7b8e2016-08-03 21:04:53202```python
203if (is_component_build) {
204 component("mycomponent") {
205 public_deps = [ ":browser_impl", ":renderer_impl" ]
206 }
207}
brettwdbb91a1b2016-08-03 20:14:54208
brettwc6b7b8e2016-08-03 21:04:53209# External targets always depend on this or the equivalent “renderer” target.
210group("browser") {
211 if (is_component_build) {
212 public_deps = [ ":mycomponent" ]
213 } else {
214 public_deps = [ ":browser_impl" ]
215 }
216}
brettwdbb91a1b2016-08-03 20:14:54217
brettwc6b7b8e2016-08-03 21:04:53218source_set("browser_impl") {
219 visibility = [ ":*" ] # Prevent accidental dependencies.
Ken Rockotca112b22018-01-25 22:36:31220 defines = [ "IS_MYCOMPONENT_IMPL" ]
brettwc6b7b8e2016-08-03 21:04:53221 sources = [ ... ]
222}
223```
brettwdbb91a1b2016-08-03 20:14:54224
225## Common mistakes
226
Ken Rockotca112b22018-01-25 22:36:31227### Forgetting or misspelling `COMPONENT_EXPORT(*)`
brettwdbb91a1b2016-08-03 20:14:54228
Ken Rockotca112b22018-01-25 22:36:31229If a function is not marked with your `COMPONENT_EXPORT(FOO)` annotation or the
230component name (`FOO`) is misspelled, other components won’t see the symbol when
231linking and you’ll get undefined symbols during linking:
brettwdbb91a1b2016-08-03 20:14:54232
233 some_file.obj : error LNK2001: unresolved external symbol <some definition>
234
235This will only happen on Windows component builds, which makes the error more
236difficult to debug. However, if you see such an error only for Windows
237component builds, you know it’s this problem.
238
Ken Rockotca112b22018-01-25 22:36:31239### Not defining `IS_*_IMPL` for code in your component
brettwdbb91a1b2016-08-03 20:14:54240
241When code is compiled that sees a symbol marked with `__declspec(dllimport)`,
242it will expect to find that symbol in another shared library. If that symbol
243ends up in the same shared library, you’ll see the error:
244
245 some_file.obj : warning LNK4217: locally defined symbol
246 <horrendous mangled name> imported in function <some definition>
247
Ken Rockotca112b22018-01-25 22:36:31248The solution is to make sure your `IS_*_IMPL` define is set consistently for all
249code in the component. If your component links in source sets or static
250libraries, the `IS_*_IMPL` macro must be set on those as well.
brettwdbb91a1b2016-08-03 20:14:54251
Ken Rockotca112b22018-01-25 22:36:31252### Defining `IS_*_IMPL` for code outside your component
brettwdbb91a1b2016-08-03 20:14:54253
Ken Rockotca112b22018-01-25 22:36:31254If your `IS_*_IMPL` macro is set for code compiled outside of the
brettwdbb91a1b2016-08-03 20:14:54255component, that code will expect the symbol to be in the current shared
256library, but it won’t be found. It won’t even go looking in other libraries and
257the result will be an undefined symbol:
258
259 some_file.obj : error LNK2001: unresolved external symbol <some definition>
260
261### Depending on a source set or static library from both inside and outside a component
262
263If the source set or static library has any `*_EXPORT` macros and ends up both
264inside and outside of the component boundary, those symbols will fall under the
Ken Rockotca112b22018-01-25 22:36:31265cases above where `IS_*_IMPL` is inappropriately defined or inappropriately
brettwdbb91a1b2016-08-03 20:14:54266undefined. Use GN visibility to make sure callers don’t screw up.
267
268### Putting exported symbols in static libraries
269
270As discussed above, exported symbols should not be in static libraries because
271the object file might not be brought into the link. Even if it is brought in
272today, it might not be brought in due to completely unrelated changes in the
273future. The result will be undefined symbol errors from other components. Use
274source sets if your component is made up of more than one target.
majidvpe0ee33c2017-02-17 22:41:21275
276### Exporting functions and classes implemented in headers
277
278When you implement a symbol in a header the compiler will put that in every
279necessary translation unit and the linker will pick one. If the symbol is never
280referenced by code in the shared library it's supposed exported from, it will
281never be instantiated and never exported. The result will be undefined external
282symbol errors when linking. Exported symbols should be declared in a header but
283always implemented in a .cc file.