blob: 88003ed4017f242053357b611135f7c9e336e30c [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
43### Dependencies between targets
44
45When a component directly or indirectly depends on a static library or source
46set, it will be linked into this component. If other components do the same,
47the static library or source set’s code will be duplicated.
48
49In a few cases (for defining some constants) this duplication is OK, but in
50general this is a bad idea. Globals and singletons will get duplicated which
51will wreak havoc. Therefore, you should normally ensure that components only
52depend on other components.
53
54### Component granularity
55
56Creating lots of small components isn’t desirable. Some code can easily get
57duplicated, it takes extra time to create the shared libraries themselves, load
58time will get worse, and the build and code can get complicated. On the other
59extreme, very large components negate the benefits of the component build. A
60good rule of thumb is that components should be medium sized, somewhere in the
61range of several dozen to several hundred files.
62
63## Exporting and importing symbols
64
65When a shared library or executable uses a symbol from a shared library, it is
66“imported” by the user of the symbol, and “exported” from the shared library
67that defines the symbol. Don’t confuse exported symbols with the public API of
68a component. For example, unit tests will often require implementation details
69to be exported. Export symbols to make the build link the way you need it, and
70use GN’s public headers and visibility restrictions to define your public API.
71
Ken Rockotca112b22018-01-25 22:36:3172Component library headers can use the `COMPONENT_EXPORT()` macro defined in
73`base/component_export.h` to annotate symbols which should be exported by
74the component. This macro takes a globally unique component name as an
75argument:
76
77```c++
78#include "base/component_export.h"
79
80class COMPONENT_EXPORT(YOUR_COMPONENT) YourClass { ... };
81
82COMPONENT_EXPORT(YOUR_COMPONENT) void SomeFunction();
83```
84
85When defining the target for your component, set:
86
87```python
88defines = [ "IS_YOUR_COMPONENT_IMPL" ]
89```
90
91This ensures that the corresponding `COMPONENT_EXPORT(YOUR_COMPONENT)`
92invocations result in symbols being marked for export when compiling the
93component target. All other targets which include the component's headers
94will not have defined `IS_YOUR_COMPONENT_IMPL`, so they will have the same
95symbols marked for import instead.
96
97## Chrome’s deprecated pattern for exports
98
99**NOTE**: This section is included for posterity, as many components in the tree
100still use this pattern for exports. New components should use
101`base/component_export.h` as described above.
brettwdbb91a1b2016-08-03 20:14:54102
brettwc6b7b8e2016-08-03 21:04:53103Write a header with the name `<component_name>_export.h`. Copy an [existing
brettwdbb91a1b2016-08-03 20:14:54104one](https://cs.chromium.org/chromium/src/ipc/ipc_export.h)
105and update the macro names. It will key off of two macros:
106
107 * `COMPONENT_BUILD`: A globally defined preprocessor definition set when the
108 component build is on.
109 * `<component_name>_IMPLEMENTATION`: A macro you define for code inside your
110 component, and leave undefined for code outside of your component. The
111 naming should match your `*_export.h` header.
112
113It will define a macro `<component_name>_EXPORT`. This will use the
114`*_IMPLEMENTATION` macro to know whether code is being compiled inside or outside
115of your component, and the `*_EXPORT` macro will set it to being exported or
116imported, respectively. You should copy an existing file and update the
117`*_EXPORT` macro naming for your component.
118
119When defining the target for your component, set:
120
brettwc6b7b8e2016-08-03 21:04:53121```python
122defines = [ "FOO_IMPLEMENTATION" ]
123```
brettwdbb91a1b2016-08-03 20:14:54124
125In your BUILD.gn file. If you have source sets that also make up your
126component, set this on them also. A good way to share this is to put the
127definition in a GN config:
128
brettwc6b7b8e2016-08-03 21:04:53129```python
130config("foo_implementation") {
131 defines = [ "FOO_IMPLEMENTATION" ]
132}
133```
brettwdbb91a1b2016-08-03 20:14:54134
135and set the config on the targets that use it:
136
brettwc6b7b8e2016-08-03 21:04:53137```python
138configs += [ ":foo_implementation" ]
139```
brettwdbb91a1b2016-08-03 20:14:54140
141The component build is only reason to use the `*_IMPLEMENTATION` macros. If
142your code is not being compiled into a component, don’t define such a macro
143(sometimes people do this by copying other targets without understanding).
144
145### Marking symbols for export
146
147Use the `*_EXPORT` macros on function and class declarations (don’t annotate
148the implementations) as follows:
149
brettwc6b7b8e2016-08-03 21:04:53150```c++
151#include "yourcomponent/yourcomponent_export.h"
brettwdbb91a1b2016-08-03 20:14:54152
brettwc6b7b8e2016-08-03 21:04:53153class YOURCOMPONENT_EXPORT YourClass { ... };
brettwdbb91a1b2016-08-03 20:14:54154
brettwc6b7b8e2016-08-03 21:04:53155YOURCOMPONENT_EXPORT void SomeFunction();
156```
brettwdbb91a1b2016-08-03 20:14:54157
brettwdbb91a1b2016-08-03 20:14:54158## Creating components from multiple targets
159
160### Static library symbol export issues
161
162Components can be made up of static libraries and GN source sets. A source set
163results in all object files from that compilation being linked into the
164component. But when code is in a static library, only those object files needed
165to define undefined symbols will be pulled in to the link. If an object file is
166not needed to link the component itself, it won’t be pulled into the link, even
167though it might have exported symbols needed by other components.
168
169Therefore, all code with exported symbols should be either on the component
170target itself or in source sets it depends on.
171
172### Splitting targets differently in static and component builds
173
174Sometimes you might have something consisting of multiple sub-targets. For
175example: a browser, a renderer, and a common directory, each with their own
176target. In the static build, they would all be linked into different places. In
177the component build, you may want to have these be in a single component for
178performance and sanity reasons. Content is such an example.
179
180The important thing is that the sub-projects not be depended on directly from
181outside of the component in the component build. This will duplicate the code
182and the import/export of symbols will get confused (see “Common mistakes”
183below).
184
185Generally the way to do this is to create browser and renderer group targets
186that forward to the right place. In static builds these would forward to
187internal targets with the actual code in them. In component builds, these would
188forward to the component.
189
190In the static build the structure will be: `//external/thing` ➜ `//foo:browser`
191➜ `//foo:browser_impl`
192
193In the component build the structure will be: `//external/thing` ➜
194`//foo:browser` ➜ `//foo:mycomponent` ➜ `//foo:browser_impl`
195
196Set GN visibility so that the targets with the code can only be depended on by
197targets inside your component.
198
brettwc6b7b8e2016-08-03 21:04:53199```python
200if (is_component_build) {
201 component("mycomponent") {
202 public_deps = [ ":browser_impl", ":renderer_impl" ]
203 }
204}
brettwdbb91a1b2016-08-03 20:14:54205
brettwc6b7b8e2016-08-03 21:04:53206# External targets always depend on this or the equivalent “renderer” target.
207group("browser") {
208 if (is_component_build) {
209 public_deps = [ ":mycomponent" ]
210 } else {
211 public_deps = [ ":browser_impl" ]
212 }
213}
brettwdbb91a1b2016-08-03 20:14:54214
brettwc6b7b8e2016-08-03 21:04:53215source_set("browser_impl") {
216 visibility = [ ":*" ] # Prevent accidental dependencies.
Ken Rockotca112b22018-01-25 22:36:31217 defines = [ "IS_MYCOMPONENT_IMPL" ]
brettwc6b7b8e2016-08-03 21:04:53218 sources = [ ... ]
219}
220```
brettwdbb91a1b2016-08-03 20:14:54221
222## Common mistakes
223
Ken Rockotca112b22018-01-25 22:36:31224### Forgetting or misspelling `COMPONENT_EXPORT(*)`
brettwdbb91a1b2016-08-03 20:14:54225
Ken Rockotca112b22018-01-25 22:36:31226If a function is not marked with your `COMPONENT_EXPORT(FOO)` annotation or the
227component name (`FOO`) is misspelled, other components won’t see the symbol when
228linking and you’ll get undefined symbols during linking:
brettwdbb91a1b2016-08-03 20:14:54229
230 some_file.obj : error LNK2001: unresolved external symbol <some definition>
231
232This will only happen on Windows component builds, which makes the error more
233difficult to debug. However, if you see such an error only for Windows
234component builds, you know it’s this problem.
235
Ken Rockotca112b22018-01-25 22:36:31236### Not defining `IS_*_IMPL` for code in your component
brettwdbb91a1b2016-08-03 20:14:54237
238When code is compiled that sees a symbol marked with `__declspec(dllimport)`,
239it will expect to find that symbol in another shared library. If that symbol
240ends up in the same shared library, you’ll see the error:
241
242 some_file.obj : warning LNK4217: locally defined symbol
243 <horrendous mangled name> imported in function <some definition>
244
Ken Rockotca112b22018-01-25 22:36:31245The solution is to make sure your `IS_*_IMPL` define is set consistently for all
246code in the component. If your component links in source sets or static
247libraries, the `IS_*_IMPL` macro must be set on those as well.
brettwdbb91a1b2016-08-03 20:14:54248
Ken Rockotca112b22018-01-25 22:36:31249### Defining `IS_*_IMPL` for code outside your component
brettwdbb91a1b2016-08-03 20:14:54250
Ken Rockotca112b22018-01-25 22:36:31251If your `IS_*_IMPL` macro is set for code compiled outside of the
brettwdbb91a1b2016-08-03 20:14:54252component, that code will expect the symbol to be in the current shared
253library, but it won’t be found. It won’t even go looking in other libraries and
254the result will be an undefined symbol:
255
256 some_file.obj : error LNK2001: unresolved external symbol <some definition>
257
258### Depending on a source set or static library from both inside and outside a component
259
260If the source set or static library has any `*_EXPORT` macros and ends up both
261inside and outside of the component boundary, those symbols will fall under the
Ken Rockotca112b22018-01-25 22:36:31262cases above where `IS_*_IMPL` is inappropriately defined or inappropriately
brettwdbb91a1b2016-08-03 20:14:54263undefined. Use GN visibility to make sure callers don’t screw up.
264
265### Putting exported symbols in static libraries
266
267As discussed above, exported symbols should not be in static libraries because
268the object file might not be brought into the link. Even if it is brought in
269today, it might not be brought in due to completely unrelated changes in the
270future. The result will be undefined symbol errors from other components. Use
271source sets if your component is made up of more than one target.
majidvpe0ee33c2017-02-17 22:41:21272
273### Exporting functions and classes implemented in headers
274
275When you implement a symbol in a header the compiler will put that in every
276necessary translation unit and the linker will pick one. If the symbol is never
277referenced by code in the shared library it's supposed exported from, it will
278never be instantiated and never exported. The result will be undefined external
279symbol errors when linking. Exported symbols should be declared in a header but
280always implemented in a .cc file.