brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 1 | # The Chrome Component Build |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 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 |
brettw | 9fb64176b | 2016-08-04 19:22:17 | [diff] [blame] | 8 | single binary. |
| 9 | |
| 10 | In a component build, many smaller shared libraries will be generated. This |
| 11 | speeds up link times, and means that many changes only require that the local |
| 12 | shared library be linked rather than the full executable, but at the expense of |
| 13 | program load-time performance. |
| 14 | |
| 15 | The component build is currently the default for debug non-iOS builds (it |
| 16 | doesn’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): |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 18 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 19 | ```python |
| 20 | is_component_build = true |
| 21 | ``` |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 22 | |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 23 | ### How to make a component |
| 24 | |
| 25 | Defining a component just means using the GN “component” template instead |
| 26 | of a shared library, static library, or source set. The template will |
| 27 | generate a shared library when `is_component_build` is enabled, and a static |
| 28 | library otherwise. |
| 29 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 30 | ```python |
| 31 | component("browser") { |
| 32 | output_name = "chrome_browser" |
| 33 | sources = ... |
| 34 | ... |
| 35 | } |
| 36 | ``` |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 37 | |
| 38 | Shared libraries in GN must have globally unique output names. According to GN |
| 39 | style, your target should be named something simple and convenient (often |
| 40 | matching your directory name). If this is non-unique, override it with the |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 41 | `output_name` variable. |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 42 | |
Oksana Zhuravlova | f337c28 | 2021-03-04 20:01:15 | [diff] [blame] | 43 | **Note**: for information about `mojom_component` targets, see |
| 44 | [Mojo documentation](/mojo/public/tools/bindings/README.md#Component-targets). |
| 45 | |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 46 | ### Dependencies between targets |
| 47 | |
| 48 | When a component directly or indirectly depends on a static library or source |
| 49 | set, it will be linked into this component. If other components do the same, |
| 50 | the static library or source set’s code will be duplicated. |
| 51 | |
| 52 | In a few cases (for defining some constants) this duplication is OK, but in |
| 53 | general this is a bad idea. Globals and singletons will get duplicated which |
| 54 | will wreak havoc. Therefore, you should normally ensure that components only |
| 55 | depend on other components. |
| 56 | |
| 57 | ### Component granularity |
| 58 | |
| 59 | Creating lots of small components isn’t desirable. Some code can easily get |
| 60 | duplicated, it takes extra time to create the shared libraries themselves, load |
| 61 | time will get worse, and the build and code can get complicated. On the other |
| 62 | extreme, very large components negate the benefits of the component build. A |
| 63 | good rule of thumb is that components should be medium sized, somewhere in the |
| 64 | range of several dozen to several hundred files. |
| 65 | |
| 66 | ## Exporting and importing symbols |
| 67 | |
| 68 | When 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 |
| 70 | that defines the symbol. Don’t confuse exported symbols with the public API of |
| 71 | a component. For example, unit tests will often require implementation details |
| 72 | to be exported. Export symbols to make the build link the way you need it, and |
| 73 | use GN’s public headers and visibility restrictions to define your public API. |
| 74 | |
Ken Rockot | ca112b2 | 2018-01-25 22:36:31 | [diff] [blame] | 75 | Component library headers can use the `COMPONENT_EXPORT()` macro defined in |
| 76 | `base/component_export.h` to annotate symbols which should be exported by |
| 77 | the component. This macro takes a globally unique component name as an |
| 78 | argument: |
| 79 | |
| 80 | ```c++ |
| 81 | #include "base/component_export.h" |
| 82 | |
| 83 | class COMPONENT_EXPORT(YOUR_COMPONENT) YourClass { ... }; |
| 84 | |
| 85 | COMPONENT_EXPORT(YOUR_COMPONENT) void SomeFunction(); |
| 86 | ``` |
| 87 | |
| 88 | When defining the target for your component, set: |
| 89 | |
| 90 | ```python |
| 91 | defines = [ "IS_YOUR_COMPONENT_IMPL" ] |
| 92 | ``` |
| 93 | |
| 94 | This ensures that the corresponding `COMPONENT_EXPORT(YOUR_COMPONENT)` |
| 95 | invocations result in symbols being marked for export when compiling the |
| 96 | component target. All other targets which include the component's headers |
| 97 | will not have defined `IS_YOUR_COMPONENT_IMPL`, so they will have the same |
| 98 | symbols 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 |
| 103 | still use this pattern for exports. New components should use |
| 104 | `base/component_export.h` as described above. |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 105 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 106 | Write a header with the name `<component_name>_export.h`. Copy an [existing |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 107 | one](https://cs.chromium.org/chromium/src/ipc/ipc_export.h) |
| 108 | and 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 | |
| 116 | It will define a macro `<component_name>_EXPORT`. This will use the |
| 117 | `*_IMPLEMENTATION` macro to know whether code is being compiled inside or outside |
| 118 | of your component, and the `*_EXPORT` macro will set it to being exported or |
| 119 | imported, respectively. You should copy an existing file and update the |
| 120 | `*_EXPORT` macro naming for your component. |
| 121 | |
| 122 | When defining the target for your component, set: |
| 123 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 124 | ```python |
| 125 | defines = [ "FOO_IMPLEMENTATION" ] |
| 126 | ``` |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 127 | |
| 128 | In your BUILD.gn file. If you have source sets that also make up your |
| 129 | component, set this on them also. A good way to share this is to put the |
| 130 | definition in a GN config: |
| 131 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 132 | ```python |
| 133 | config("foo_implementation") { |
| 134 | defines = [ "FOO_IMPLEMENTATION" ] |
| 135 | } |
| 136 | ``` |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 137 | |
| 138 | and set the config on the targets that use it: |
| 139 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 140 | ```python |
| 141 | configs += [ ":foo_implementation" ] |
| 142 | ``` |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 143 | |
| 144 | The component build is only reason to use the `*_IMPLEMENTATION` macros. If |
| 145 | your 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 | |
| 150 | Use the `*_EXPORT` macros on function and class declarations (don’t annotate |
| 151 | the implementations) as follows: |
| 152 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 153 | ```c++ |
| 154 | #include "yourcomponent/yourcomponent_export.h" |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 155 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 156 | class YOURCOMPONENT_EXPORT YourClass { ... }; |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 157 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 158 | YOURCOMPONENT_EXPORT void SomeFunction(); |
| 159 | ``` |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 160 | |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 161 | ## Creating components from multiple targets |
| 162 | |
| 163 | ### Static library symbol export issues |
| 164 | |
| 165 | Components can be made up of static libraries and GN source sets. A source set |
| 166 | results in all object files from that compilation being linked into the |
| 167 | component. But when code is in a static library, only those object files needed |
| 168 | to define undefined symbols will be pulled in to the link. If an object file is |
| 169 | not needed to link the component itself, it won’t be pulled into the link, even |
| 170 | though it might have exported symbols needed by other components. |
| 171 | |
| 172 | Therefore, all code with exported symbols should be either on the component |
| 173 | target itself or in source sets it depends on. |
| 174 | |
| 175 | ### Splitting targets differently in static and component builds |
| 176 | |
| 177 | Sometimes you might have something consisting of multiple sub-targets. For |
| 178 | example: a browser, a renderer, and a common directory, each with their own |
| 179 | target. In the static build, they would all be linked into different places. In |
| 180 | the component build, you may want to have these be in a single component for |
| 181 | performance and sanity reasons. Content is such an example. |
| 182 | |
| 183 | The important thing is that the sub-projects not be depended on directly from |
| 184 | outside of the component in the component build. This will duplicate the code |
| 185 | and the import/export of symbols will get confused (see “Common mistakes” |
| 186 | below). |
| 187 | |
| 188 | Generally the way to do this is to create browser and renderer group targets |
| 189 | that forward to the right place. In static builds these would forward to |
| 190 | internal targets with the actual code in them. In component builds, these would |
| 191 | forward to the component. |
| 192 | |
| 193 | In the static build the structure will be: `//external/thing` ➜ `//foo:browser` |
| 194 | ➜ `//foo:browser_impl` |
| 195 | |
| 196 | In the component build the structure will be: `//external/thing` ➜ |
| 197 | `//foo:browser` ➜ `//foo:mycomponent` ➜ `//foo:browser_impl` |
| 198 | |
| 199 | Set GN visibility so that the targets with the code can only be depended on by |
| 200 | targets inside your component. |
| 201 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 202 | ```python |
| 203 | if (is_component_build) { |
| 204 | component("mycomponent") { |
| 205 | public_deps = [ ":browser_impl", ":renderer_impl" ] |
| 206 | } |
| 207 | } |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 208 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 209 | # External targets always depend on this or the equivalent “renderer” target. |
| 210 | group("browser") { |
| 211 | if (is_component_build) { |
| 212 | public_deps = [ ":mycomponent" ] |
| 213 | } else { |
| 214 | public_deps = [ ":browser_impl" ] |
| 215 | } |
| 216 | } |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 217 | |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 218 | source_set("browser_impl") { |
| 219 | visibility = [ ":*" ] # Prevent accidental dependencies. |
Ken Rockot | ca112b2 | 2018-01-25 22:36:31 | [diff] [blame] | 220 | defines = [ "IS_MYCOMPONENT_IMPL" ] |
brettw | c6b7b8e | 2016-08-03 21:04:53 | [diff] [blame] | 221 | sources = [ ... ] |
| 222 | } |
| 223 | ``` |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 224 | |
| 225 | ## Common mistakes |
| 226 | |
Ken Rockot | ca112b2 | 2018-01-25 22:36:31 | [diff] [blame] | 227 | ### Forgetting or misspelling `COMPONENT_EXPORT(*)` |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 228 | |
Ken Rockot | ca112b2 | 2018-01-25 22:36:31 | [diff] [blame] | 229 | If a function is not marked with your `COMPONENT_EXPORT(FOO)` annotation or the |
| 230 | component name (`FOO`) is misspelled, other components won’t see the symbol when |
| 231 | linking and you’ll get undefined symbols during linking: |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 232 | |
| 233 | some_file.obj : error LNK2001: unresolved external symbol <some definition> |
| 234 | |
| 235 | This will only happen on Windows component builds, which makes the error more |
| 236 | difficult to debug. However, if you see such an error only for Windows |
| 237 | component builds, you know it’s this problem. |
| 238 | |
Ken Rockot | ca112b2 | 2018-01-25 22:36:31 | [diff] [blame] | 239 | ### Not defining `IS_*_IMPL` for code in your component |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 240 | |
| 241 | When code is compiled that sees a symbol marked with `__declspec(dllimport)`, |
| 242 | it will expect to find that symbol in another shared library. If that symbol |
| 243 | ends 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 Rockot | ca112b2 | 2018-01-25 22:36:31 | [diff] [blame] | 248 | The solution is to make sure your `IS_*_IMPL` define is set consistently for all |
| 249 | code in the component. If your component links in source sets or static |
| 250 | libraries, the `IS_*_IMPL` macro must be set on those as well. |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 251 | |
Ken Rockot | ca112b2 | 2018-01-25 22:36:31 | [diff] [blame] | 252 | ### Defining `IS_*_IMPL` for code outside your component |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 253 | |
Ken Rockot | ca112b2 | 2018-01-25 22:36:31 | [diff] [blame] | 254 | If your `IS_*_IMPL` macro is set for code compiled outside of the |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 255 | component, that code will expect the symbol to be in the current shared |
| 256 | library, but it won’t be found. It won’t even go looking in other libraries and |
| 257 | the 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 | |
| 263 | If the source set or static library has any `*_EXPORT` macros and ends up both |
| 264 | inside and outside of the component boundary, those symbols will fall under the |
Ken Rockot | ca112b2 | 2018-01-25 22:36:31 | [diff] [blame] | 265 | cases above where `IS_*_IMPL` is inappropriately defined or inappropriately |
brettw | dbb91a1b | 2016-08-03 20:14:54 | [diff] [blame] | 266 | undefined. Use GN visibility to make sure callers don’t screw up. |
| 267 | |
| 268 | ### Putting exported symbols in static libraries |
| 269 | |
| 270 | As discussed above, exported symbols should not be in static libraries because |
| 271 | the object file might not be brought into the link. Even if it is brought in |
| 272 | today, it might not be brought in due to completely unrelated changes in the |
| 273 | future. The result will be undefined symbol errors from other components. Use |
| 274 | source sets if your component is made up of more than one target. |
majidvp | e0ee33c | 2017-02-17 22:41:21 | [diff] [blame] | 275 | |
| 276 | ### Exporting functions and classes implemented in headers |
| 277 | |
| 278 | When you implement a symbol in a header the compiler will put that in every |
| 279 | necessary translation unit and the linker will pick one. If the symbol is never |
| 280 | referenced by code in the shared library it's supposed exported from, it will |
| 281 | never be instantiated and never exported. The result will be undefined external |
| 282 | symbol errors when linking. Exported symbols should be declared in a header but |
| 283 | always implemented in a .cc file. |