1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283

docs / component_build.md [blame]

# The Chrome Component Build

## Introduction

Release builds are “static” builds which compile to one executable and
zero-to-two shared libraries (depending on the platform). This is efficient at
runtime, but can take a long time to link because so much code goes into a
single binary.

In a component build, many smaller shared libraries will be generated. This
speeds up link times, and means that many changes only require that the local
shared library be linked rather than the full executable, but at the expense of
program load-time performance.

The component build is currently the default for debug non-iOS builds (it
doesn’t work for iOS). You can force it on for release builds using the
[GN build arg](https://www.chromium.org/developers/gn-build-configuration):

```python
is_component_build = true
```

### How to make a component

Defining a component just means using the GN “component” template instead
of a shared library, static library, or source set. The template will
generate a shared library when `is_component_build` is enabled, and a static
library otherwise.

```python
component("browser") {
  output_name = "chrome_browser"
  sources = ...
  ...
}
```

Shared libraries in GN must have globally unique output names. According to GN
style, your target should be named something simple and convenient (often
matching your directory name). If this is non-unique, override it with the
`output_name` variable.

**Note**: for information about `mojom_component` targets, see
[Mojo documentation](/mojo/public/tools/bindings/README.md#Component-targets).

### Dependencies between targets

When a component directly or indirectly depends on a static library or source
set, it will be linked into this component. If other components do the same,
the static library or source set’s code will be duplicated.

In a few cases (for defining some constants) this duplication is OK, but in
general this is a bad idea. Globals and singletons will get duplicated which
will wreak havoc. Therefore, you should normally ensure that components only
depend on other components.

### Component granularity

Creating lots of small components isn’t desirable. Some code can easily get
duplicated, it takes extra time to create the shared libraries themselves, load
time will get worse, and the build and code can get complicated. On the other
extreme, very large components negate the benefits of the component build. A
good rule of thumb is that components should be medium sized, somewhere in the
range of several dozen to several hundred files.

## Exporting and importing symbols

When a shared library or executable uses a symbol from a shared library, it is
“imported” by the user of the symbol, and “exported” from the shared library
that defines the symbol. Don’t confuse exported symbols with the public API of
a component. For example, unit tests will often require implementation details
to be exported. Export symbols to make the build link the way you need it, and
use GN’s public headers and visibility restrictions to define your public API.

Component library headers can use the `COMPONENT_EXPORT()` macro defined in
`base/component_export.h` to annotate symbols which should be exported by
the component. This macro takes a globally unique component name as an
argument:

```c++
#include "base/component_export.h"

class COMPONENT_EXPORT(YOUR_COMPONENT) YourClass { ... };

COMPONENT_EXPORT(YOUR_COMPONENT) void SomeFunction();
```

When defining the target for your component, set:

```python
defines = [ "IS_YOUR_COMPONENT_IMPL" ]
```

This ensures that the corresponding `COMPONENT_EXPORT(YOUR_COMPONENT)`
invocations result in symbols being marked for export when compiling the
component target. All other targets which include the component's headers
will not have defined `IS_YOUR_COMPONENT_IMPL`, so they will have the same
symbols marked for import instead.

## Chrome’s deprecated pattern for exports

**NOTE**: This section is included for posterity, as many components in the tree
still use this pattern for exports. New components should use
`base/component_export.h` as described above.

Write a header with the name `<component_name>_export.h`. Copy an [existing
one](https://cs.chromium.org/chromium/src/ipc/ipc_export.h)
and update the macro names. It will key off of two macros:

  * `COMPONENT_BUILD`: A globally defined preprocessor definition set when the
    component build is on.
  * `<component_name>_IMPLEMENTATION`: A macro you define for code inside your
    component, and leave undefined for code outside of your component. The
    naming should match your `*_export.h` header.

It will define a macro `<component_name>_EXPORT`. This will use the
`*_IMPLEMENTATION` macro to know whether code is being compiled inside or outside
of your component, and the `*_EXPORT` macro will set it to being exported or
imported, respectively. You should copy an existing file and update the
`*_EXPORT` macro naming for your component.

When defining the target for your component, set:

```python
defines = [ "FOO_IMPLEMENTATION" ]
```

In your BUILD.gn file. If you have source sets that also make up your
component, set this on them also. A good way to share this is to put the
definition in a GN config:

```python
config("foo_implementation") {
  defines = [ "FOO_IMPLEMENTATION" ]
}
```

and set the config on the targets that use it:

```python
configs += [ ":foo_implementation" ]
```

The component build is only reason to use the `*_IMPLEMENTATION` macros. If
your code is not being compiled into a component, don’t define such a macro
(sometimes people do this by copying other targets without understanding).

### Marking symbols for export

Use the `*_EXPORT` macros on function and class declarations (don’t annotate
the implementations) as follows:

```c++
#include "yourcomponent/yourcomponent_export.h"

class YOURCOMPONENT_EXPORT YourClass { ... };

YOURCOMPONENT_EXPORT void SomeFunction();
```

## Creating components from multiple targets

### Static library symbol export issues

Components can be made up of static libraries and GN source sets. A source set
results in all object files from that compilation being linked into the
component. But when code is in a static library, only those object files needed
to define undefined symbols will be pulled in to the link. If an object file is
not needed to link the component itself, it won’t be pulled into the link, even
though it might have exported symbols needed by other components.

Therefore, all code with exported symbols should be either on the component
target itself or in source sets it depends on.

### Splitting targets differently in static and component builds

Sometimes you might have something consisting of multiple sub-targets. For
example: a browser, a renderer, and a common directory, each with their own
target. In the static build, they would all be linked into different places. In
the component build, you may want to have these be in a single component for
performance and sanity reasons. Content is such an example.

The important thing is that the sub-projects not be depended on directly from
outside of the component in the component build. This will duplicate the code
and the import/export of symbols will get confused (see “Common mistakes”
below).

Generally the way to do this is to create browser and renderer group targets
that forward to the right place. In static builds these would forward to
internal targets with the actual code in them. In component builds, these would
forward to the component.

In the static build the structure will be: `//external/thing` ➜ `//foo:browser`
➜ `//foo:browser_impl`

In the component build the structure will be: `//external/thing` ➜
`//foo:browser` ➜ `//foo:mycomponent` ➜ `//foo:browser_impl`

Set GN visibility so that the targets with the code can only be depended on by
targets inside your component.

```python
if (is_component_build) {
  component("mycomponent") {
    public_deps = [ ":browser_impl", ":renderer_impl" ]
  }
}

# External targets always depend on this or the equivalent “renderer” target.
group("browser") {
  if (is_component_build) {
    public_deps = [ ":mycomponent" ]
  } else {
    public_deps = [ ":browser_impl" ]
  }
}

source_set("browser_impl") {
  visibility = [ ":*" ]  # Prevent accidental dependencies.
  defines = [ "IS_MYCOMPONENT_IMPL" ]
  sources = [ ... ]
}
```

## Common mistakes

### Forgetting or misspelling `COMPONENT_EXPORT(*)`

If a function is not marked with your `COMPONENT_EXPORT(FOO)` annotation or the
component name (`FOO`) is misspelled, other components won’t see the symbol when
linking and you’ll get undefined symbols during linking:

    some_file.obj : error LNK2001: unresolved external symbol <some definition>

This will only happen on Windows component builds, which makes the error more
difficult to debug. However, if you see such an error only for Windows
component builds, you know it’s this problem.

### Not defining `IS_*_IMPL` for code in your component

When code is compiled that sees a symbol marked with `__declspec(dllimport)`,
it will expect to find that symbol in another shared library. If that symbol
ends up in the same shared library, you’ll see the error:

    some_file.obj : warning LNK4217: locally defined symbol
    <horrendous mangled name> imported in function <some definition>

The solution is to make sure your `IS_*_IMPL` define is set consistently for all
code in the component. If your component links in source sets or static
libraries, the `IS_*_IMPL` macro must be set on those as well.

### Defining `IS_*_IMPL` for code outside your component

If your `IS_*_IMPL` macro is set for code compiled outside of the
component, that code will expect the symbol to be in the current shared
library, but it won’t be found. It won’t even go looking in other libraries and
the result will be an undefined symbol:

    some_file.obj : error LNK2001: unresolved external symbol <some definition>

### Depending on a source set or static library from both inside and outside a component

If the source set or static library has any `*_EXPORT` macros and ends up both
inside and outside of the component boundary, those symbols will fall under the
cases above where `IS_*_IMPL` is inappropriately defined or inappropriately
undefined. Use GN visibility to make sure callers don’t screw up.

### Putting exported symbols in static libraries

As discussed above, exported symbols should not be in static libraries because
the object file might not be brought into the link. Even if it is brought in
today, it might not be brought in due to completely unrelated changes in the
future. The result will be undefined symbol errors from other components. Use
source sets if your component is made up of more than one target.

### Exporting functions and classes implemented in headers

When you implement a symbol in a header the compiler will put that in every
necessary translation unit and the linker will pick one. If the symbol is never
referenced by code in the shared library it's supposed exported from, it will
never be instantiated and never exported. The result will be undefined external
symbol errors when linking. Exported symbols should be declared in a header but
always implemented in a .cc file.