| Index: docs/component_build.md
|
| diff --git a/docs/component_build.md b/docs/component_build.md
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..427c49c8cc3ed18ebcafd9d4b696d871e5261a65
|
| --- /dev/null
|
| +++ b/docs/component_build.md
|
| @@ -0,0 +1,249 @@
|
| +# 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. When you set the GN build variable
|
| +
|
| + ```python
|
| + is_component_build = true
|
| + ```
|
| +
|
| +the build will generate many smaller shared libraries. 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.
|
| +
|
| +### 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.
|
| +
|
| +### 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.
|
| +
|
| +### Chrome’s pattern for exports
|
| +
|
| +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();
|
| + ```
|
| +
|
| +Sometimes you have an internal helper class used as the base for an exported
|
| +class. Visual C++ will complain if the base class is not exported:
|
| +
|
| + warning C4275: non dll-interface class 'YourClass' used as base for dll-interface class 'Base'
|
| +
|
| +If you don’t 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:
|
| +
|
| + ```c++
|
| + class YourClass : public NON_EXPORTED_BASE(Base) { ... };
|
| + ```
|
| +
|
| +## 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 = [ "MYCOMPONENT_IMPLEMENTATION" ]
|
| + sources = [ ... ]
|
| + }
|
| + ```
|
| +
|
| +## Common mistakes
|
| +
|
| +### Forgetting to mark a symbol with `*_EXPORT`
|
| +
|
| +If a function is not marked with your `*_EXPORT` annotation, 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 `*_IMPLEMENTATION` 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 `*_IMPLEMENTATION` define is set consistently
|
| +for all code in the component. If your component links in source sets or static
|
| +libraries, the `*_IMPLEMENTATION` macro must be set on those as well.
|
| +
|
| +### Defining `*_IMPLEMENTATION` for code outside your component
|
| +
|
| +If your `*_IMPLEMENTATION` 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 `_IMPLEMENTATION` 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.
|
|
|