From ca112b210e77187e6d64158c3a3e5a2365f4231f Mon Sep 17 00:00:00 2001 From: Ken Rockot Date: Thu, 25 Jan 2018 22:36:31 +0000 Subject: [PATCH] Update component build documentation Recommends base/component_export.h and deprecates the old pattern. Bug: None Change-Id: I8bb5d5fbdbafbb9310638d7ce50c8acb5194dce7 Reviewed-on: https://chromium-review.googlesource.com/887624 Reviewed-by: Ken Rockot Cr-Commit-Position: refs/heads/master@{#531986} --- docs/component_build.md | 55 +++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/docs/component_build.md b/docs/component_build.md index 32355ace3d762d..88003ed4017f24 100644 --- a/docs/component_build.md +++ b/docs/component_build.md @@ -69,7 +69,36 @@ 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 +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 `_export.h`. Copy an [existing one](https://cs.chromium.org/chromium/src/ipc/ipc_export.h) @@ -185,18 +214,18 @@ group("browser") { source_set("browser_impl") { visibility = [ ":*" ] # Prevent accidental dependencies. - defines = [ "MYCOMPONENT_IMPLEMENTATION" ] + defines = [ "IS_MYCOMPONENT_IMPL" ] sources = [ ... ] } ``` ## Common mistakes -### Forgetting to mark a symbol with `*_EXPORT` +### Forgetting or misspelling `COMPONENT_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: +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 @@ -204,7 +233,7 @@ 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 +### 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 @@ -213,13 +242,13 @@ ends up in the same shared library, you’ll see the error: some_file.obj : warning LNK4217: locally defined symbol imported in function -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. +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 `*_IMPLEMENTATION` for code outside your component +### Defining `IS_*_IMPL` for code outside your component -If your `*_IMPLEMENTATION` macro is set for code compiled outside of the +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: @@ -230,7 +259,7 @@ the result will be an undefined symbol: 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 +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