Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(261)

Side by Side Diff: docs/component_build.md

Issue 2206383002: Add component build documentation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « docs/clang.md ('k') | docs/ninja_build.md » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # The Chrome Component Build
2
3 ## Introduction
4
5 Release builds are “static” builds which compile to one executable and
6 zero-to-two shared libraries (depending on the platform). This is efficient at
7 runtime, but can take a long time to link because so much code goes into a
8 single binary. When you set the GN build variable
9
10 ```python
11 is_component_build = true
12 ```
13
14 the build will generate many smaller shared libraries. This speeds up link
15 times, and means that many changes only require that the local shared library
16 be linked rather than the full executable, but at the expense of program
17 load-time performance.
18
19 ### How to make a component
20
21 Defining a component just means using the GN “component” template instead
22 of a shared library, static library, or source set. The template will
23 generate a shared library when `is_component_build` is enabled, and a static
24 library otherwise.
25
26 ```python
27 component("browser") {
28 output_name = "chrome_browser"
29 sources = ...
30 ...
31 }
32 ```
33
34 Shared libraries in GN must have globally unique output names. According to GN
35 style, your target should be named something simple and convenient (often
36 matching your directory name). If this is non-unique, override it with the
37 output_name variable.
38
39 ### Dependencies between targets
40
41 When a component directly or indirectly depends on a static library or source
42 set, it will be linked into this component. If other components do the same,
43 the static library or source set’s code will be duplicated.
44
45 In a few cases (for defining some constants) this duplication is OK, but in
46 general this is a bad idea. Globals and singletons will get duplicated which
47 will wreak havoc. Therefore, you should normally ensure that components only
48 depend on other components.
49
50 ### Component granularity
51
52 Creating lots of small components isn’t desirable. Some code can easily get
53 duplicated, it takes extra time to create the shared libraries themselves, load
54 time will get worse, and the build and code can get complicated. On the other
55 extreme, very large components negate the benefits of the component build. A
56 good rule of thumb is that components should be medium sized, somewhere in the
57 range of several dozen to several hundred files.
58
59 ## Exporting and importing symbols
60
61 When a shared library or executable uses a symbol from a shared library, it is
62 “imported” by the user of the symbol, and “exported” from the shared library
63 that defines the symbol. Don’t confuse exported symbols with the public API of
64 a component. For example, unit tests will often require implementation details
65 to be exported. Export symbols to make the build link the way you need it, and
66 use GN’s public headers and visibility restrictions to define your public API.
67
68 ### Chrome’s pattern for exports
69
70 Write a header with the name <component_name>_export.h. Copy an [existing
71 one](https://cs.chromium.org/chromium/src/ipc/ipc_export.h)
72 and 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
80 It will define a macro `<component_name>_EXPORT`. This will use the
81 `*_IMPLEMENTATION` macro to know whether code is being compiled inside or outsid e
82 of your component, and the `*_EXPORT` macro will set it to being exported or
83 imported, respectively. You should copy an existing file and update the
84 `*_EXPORT` macro naming for your component.
85
86 When defining the target for your component, set:
87
88 ```python
89 defines = [ "FOO_IMPLEMENTATION" ]
90 ```
91
92 In your BUILD.gn file. If you have source sets that also make up your
93 component, set this on them also. A good way to share this is to put the
94 definition in a GN config:
95
96 ```python
97 config("foo_implementation") {
98 defines = [ "FOO_IMPLEMENTATION" ]
99 }
100 ```
101
102 and set the config on the targets that use it:
103
104 ```python
105 configs += [ ":foo_implementation" ]
106 ```
107
108 The component build is only reason to use the `*_IMPLEMENTATION` macros. If
109 your code is not being compiled into a component, don’t define such a macro
110 (sometimes people do this by copying other targets without understanding).
111
112 ### Marking symbols for export
113
114 Use the `*_EXPORT` macros on function and class declarations (don’t annotate
115 the 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
125 Sometimes you have an internal helper class used as the base for an exported
126 class. 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-inte rface class 'Base'
129
130 If you don’t use the base class outside of the component, Chrome supplies the NO N_EXPORTED_BASE macro in base/compiler_specific.h to disable the warning. For ex ample:
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
140 Components can be made up of static libraries and GN source sets. A source set
141 results in all object files from that compilation being linked into the
142 component. But when code is in a static library, only those object files needed
143 to define undefined symbols will be pulled in to the link. If an object file is
144 not needed to link the component itself, it won’t be pulled into the link, even
145 though it might have exported symbols needed by other components.
146
147 Therefore, all code with exported symbols should be either on the component
148 target itself or in source sets it depends on.
149
150 ### Splitting targets differently in static and component builds
151
152 Sometimes you might have something consisting of multiple sub-targets. For
153 example: a browser, a renderer, and a common directory, each with their own
154 target. In the static build, they would all be linked into different places. In
155 the component build, you may want to have these be in a single component for
156 performance and sanity reasons. Content is such an example.
157
158 The important thing is that the sub-projects not be depended on directly from
159 outside of the component in the component build. This will duplicate the code
160 and the import/export of symbols will get confused (see “Common mistakes”
161 below).
162
163 Generally the way to do this is to create browser and renderer group targets
164 that forward to the right place. In static builds these would forward to
165 internal targets with the actual code in them. In component builds, these would
166 forward to the component.
167
168 In the static build the structure will be: `//external/thing` ➜ `//foo:browser`
169 ➜ `//foo:browser_impl`
170
171 In the component build the structure will be: `//external/thing` ➜
172 `//foo:browser` ➜ `//foo:mycomponent` ➜ `//foo:browser_impl`
173
174 Set GN visibility so that the targets with the code can only be depended on by
175 targets 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
204 If a function is not marked with your `*_EXPORT` annotation, other components
205 won’t see the symbol when linking and you’ll get undefined symbols during
206 linking:
207
208 some_file.obj : error LNK2001: unresolved external symbol <some definition>
209
210 This will only happen on Windows component builds, which makes the error more
211 difficult to debug. However, if you see such an error only for Windows
212 component builds, you know it’s this problem.
213
214 ### Not defining `*_IMPLEMENTATION` for code in your component
215
216 When code is compiled that sees a symbol marked with `__declspec(dllimport)`,
217 it will expect to find that symbol in another shared library. If that symbol
218 ends up in the same shared library, you’ll see the error:
219
220 some_file.obj : warning LNK4217: locally defined symbol
221 <horrendous mangled name> imported in function <some definition>
222
223 The solution is to make sure your `*_IMPLEMENTATION` define is set consistently
224 for all code in the component. If your component links in source sets or static
225 libraries, the `*_IMPLEMENTATION` macro must be set on those as well.
226
227 ### Defining `*_IMPLEMENTATION` for code outside your component
228
229 If your `*_IMPLEMENTATION` macro is set for code compiled outside of the
230 component, that code will expect the symbol to be in the current shared
231 library, but it won’t be found. It won’t even go looking in other libraries and
232 the 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 c omponent
237
238 If the source set or static library has any `*_EXPORT` macros and ends up both
239 inside and outside of the component boundary, those symbols will fall under the
240 cases above where `_IMPLEMENTATION` is inappropriately defined or inappropriatel y
241 undefined. Use GN visibility to make sure callers don’t screw up.
242
243 ### Putting exported symbols in static libraries
244
245 As discussed above, exported symbols should not be in static libraries because
246 the object file might not be brought into the link. Even if it is brought in
247 today, it might not be brought in due to completely unrelated changes in the
248 future. The result will be undefined symbol errors from other components. Use
249 source sets if your component is made up of more than one target.
OLDNEW
« no previous file with comments | « docs/clang.md ('k') | docs/ninja_build.md » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698