blob: f5468153d3245777cccde48eedda63eb0a171537 [file] [log] [blame] [view]
brettwdbb91a1b2016-08-03 20:14:541# The Chrome Component Build
2
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
8single binary. When you set the GN build variable
9
10 ```python
11 is_component_build = true
12 ```
13
14the build will generate many smaller shared libraries. This speeds up link
15times, and means that many changes only require that the local shared library
16be linked rather than the full executable, but at the expense of program
17load-time performance.
18
19### How to make a component
20
21Defining a component just means using the GN component template instead
22of a shared library, static library, or source set. The template will
23generate a shared library when `is_component_build` is enabled, and a static
24library otherwise.
25
26 ```python
27 component("browser") {
28 output_name = "chrome_browser"
29 sources = ...
30 ...
31 }
32 ```
33
34Shared libraries in GN must have globally unique output names. According to GN
35style, your target should be named something simple and convenient (often
36matching your directory name). If this is non-unique, override it with the
37output_name variable.
38
39### Dependencies between targets
40
41When a component directly or indirectly depends on a static library or source
42set, it will be linked into this component. If other components do the same,
43the static library or source sets code will be duplicated.
44
45In a few cases (for defining some constants) this duplication is OK, but in
46general this is a bad idea. Globals and singletons will get duplicated which
47will wreak havoc. Therefore, you should normally ensure that components only
48depend on other components.
49
50### Component granularity
51
52Creating lots of small components isnt desirable. Some code can easily get
53duplicated, it takes extra time to create the shared libraries themselves, load
54time will get worse, and the build and code can get complicated. On the other
55extreme, very large components negate the benefits of the component build. A
56good rule of thumb is that components should be medium sized, somewhere in the
57range of several dozen to several hundred files.
58
59## Exporting and importing symbols
60
61When a shared library or executable uses a symbol from a shared library, it is
62imported by the user of the symbol, and exported from the shared library
63that defines the symbol. Dont confuse exported symbols with the public API of
64a component. For example, unit tests will often require implementation details
65to be exported. Export symbols to make the build link the way you need it, and
66use GNs public headers and visibility restrictions to define your public API.
67
68### Chrome’s pattern for exports
69
70Write a header with the name <component_name>_export.h. Copy an [existing
71one](https://cs.chromium.org/chromium/src/ipc/ipc_export.h)
72and update the macro names. It will key off of two macros:
73
74 * `COMPONENT_BUILD`: A globally defined preprocessor definition set when the
75 component build is on.
76 * `<component_name>_IMPLEMENTATION`: A macro you define for code inside your
77 component, and leave undefined for code outside of your component. The
78 naming should match your `*_export.h` header.
79
80It will define a macro `<component_name>_EXPORT`. This will use the
81`*_IMPLEMENTATION` macro to know whether code is being compiled inside or outside
82of your component, and the `*_EXPORT` macro will set it to being exported or
83imported, respectively. You should copy an existing file and update the
84`*_EXPORT` macro naming for your component.
85
86When defining the target for your component, set:
87
88 ```python
89 defines = [ "FOO_IMPLEMENTATION" ]
90 ```
91
92In your BUILD.gn file. If you have source sets that also make up your
93component, set this on them also. A good way to share this is to put the
94definition in a GN config:
95
96 ```python
97 config("foo_implementation") {
98 defines = [ "FOO_IMPLEMENTATION" ]
99 }
100 ```
101
102and set the config on the targets that use it:
103
104 ```python
105 configs += [ ":foo_implementation" ]
106 ```
107
108The component build is only reason to use the `*_IMPLEMENTATION` macros. If
109your code is not being compiled into a component, dont define such a macro
110(sometimes people do this by copying other targets without understanding).
111
112### Marking symbols for export
113
114Use the `*_EXPORT` macros on function and class declarations (dont annotate
115the implementations) as follows:
116
117 ```c++
118 #include "yourcomponent/yourcomponent_export.h"
119
120 class YOURCOMPONENT_EXPORT YourClass { ... };
121
122 YOURCOMPONENT_EXPORT void SomeFunction();
123 ```
124
125Sometimes you have an internal helper class used as the base for an exported
126class. Visual C++ will complain if the base class is not exported:
127
128 warning C4275: non dll-interface class 'YourClass' used as base for dll-interface class 'Base'
129
130If you dont use the base class outside of the component, Chrome supplies the NON_EXPORTED_BASE macro in base/compiler_specific.h to disable the warning. For example:
131
132 ```c++
133 class YourClass : public NON_EXPORTED_BASE(Base) { ... };
134 ```
135
136## Creating components from multiple targets
137
138### Static library symbol export issues
139
140Components can be made up of static libraries and GN source sets. A source set
141results in all object files from that compilation being linked into the
142component. But when code is in a static library, only those object files needed
143to define undefined symbols will be pulled in to the link. If an object file is
144not needed to link the component itself, it wont be pulled into the link, even
145though it might have exported symbols needed by other components.
146
147Therefore, all code with exported symbols should be either on the component
148target itself or in source sets it depends on.
149
150### Splitting targets differently in static and component builds
151
152Sometimes you might have something consisting of multiple sub-targets. For
153example: a browser, a renderer, and a common directory, each with their own
154target. In the static build, they would all be linked into different places. In
155the component build, you may want to have these be in a single component for
156performance and sanity reasons. Content is such an example.
157
158The important thing is that the sub-projects not be depended on directly from
159outside of the component in the component build. This will duplicate the code
160and the import/export of symbols will get confused (see Common mistakes
161below).
162
163Generally the way to do this is to create browser and renderer group targets
164that forward to the right place. In static builds these would forward to
165internal targets with the actual code in them. In component builds, these would
166forward to the component.
167
168In the static build the structure will be: `//external/thing` `//foo:browser`
169 `//foo:browser_impl`
170
171In the component build the structure will be: `//external/thing`
172`//foo:browser` `//foo:mycomponent` `//foo:browser_impl`
173
174Set GN visibility so that the targets with the code can only be depended on by
175targets inside your component.
176
177 ```python
178 if (is_component_build) {
179 component("mycomponent") {
180 public_deps = [ ":browser_impl", ":renderer_impl" ]
181 }
182 }
183
184 # External targets always depend on this or the equivalent “renderer” target.
185 group("browser") {
186 if (is_component_build) {
187 public_deps = [ ":mycomponent" ]
188 } else {
189 public_deps = [ ":browser_impl" ]
190 }
191 }
192
193 source_set("browser_impl") {
194 visibility = [ ":*" ] # Prevent accidental dependencies.
195 defines = [ "MYCOMPONENT_IMPLEMENTATION" ]
196 sources = [ ... ]
197 }
198 ```
199
200## Common mistakes
201
202### Forgetting to mark a symbol with `*_EXPORT`
203
204If a function is not marked with your `*_EXPORT` annotation, other components
205wont see the symbol when linking and youll get undefined symbols during
206linking:
207
208 some_file.obj : error LNK2001: unresolved external symbol <some definition>
209
210This will only happen on Windows component builds, which makes the error more
211difficult to debug. However, if you see such an error only for Windows
212component builds, you know its this problem.
213
214### Not defining `*_IMPLEMENTATION` for code in your component
215
216When code is compiled that sees a symbol marked with `__declspec(dllimport)`,
217it will expect to find that symbol in another shared library. If that symbol
218ends up in the same shared library, youll see the error:
219
220 some_file.obj : warning LNK4217: locally defined symbol
221 <horrendous mangled name> imported in function <some definition>
222
223The solution is to make sure your `*_IMPLEMENTATION` define is set consistently
224for all code in the component. If your component links in source sets or static
225libraries, the `*_IMPLEMENTATION` macro must be set on those as well.
226
227### Defining `*_IMPLEMENTATION` for code outside your component
228
229If your `*_IMPLEMENTATION` macro is set for code compiled outside of the
230component, that code will expect the symbol to be in the current shared
231library, but it wont be found. It wont even go looking in other libraries and
232the result will be an undefined symbol:
233
234 some_file.obj : error LNK2001: unresolved external symbol <some definition>
235
236### Depending on a source set or static library from both inside and outside a component
237
238If the source set or static library has any `*_EXPORT` macros and ends up both
239inside and outside of the component boundary, those symbols will fall under the
240cases above where `_IMPLEMENTATION` is inappropriately defined or inappropriately
241undefined. Use GN visibility to make sure callers dont screw up.
242
243### Putting exported symbols in static libraries
244
245As discussed above, exported symbols should not be in static libraries because
246the object file might not be brought into the link. Even if it is brought in
247today, it might not be brought in due to completely unrelated changes in the
248future. The result will be undefined symbol errors from other components. Use
249source sets if your component is made up of more than one target.