OLD | NEW |
(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. |
OLD | NEW |