• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Build System Best Practices
2
3## Read only source tree
4
5Never write to the source directory during the build, always write to
6`$OUT_DIR`. We expect to enforce this in the future.
7
8If you want to verify / provide an update to a checked in generated source
9file, generate that file into `$OUT_DIR` during the build, fail the build
10asking the user to run a command (either a straight command, checked in script,
11generated script, etc) to explicitly copy that file from the output into the
12source tree.
13
14## Network access
15
16Never access the network during the build. We expect to enforce this in the
17future, though there will be some level of exceptions for tools like `distcc`
18and `goma`.
19
20## Paths
21
22Don't use absolute paths in Ninja files (with make's `$(abspath)` or similar),
23as that could trigger extra rebuilds when a source directory is moved.
24
25Assume that the source directory is `$PWD`. If a script is going to change
26directories and needs to convert an input from a relative to absolute path,
27prefer to do that in the script.
28
29Don't encode absolute paths in build intermediates or outputs. This would make
30it difficult to reproduce builds on other machines.
31
32Don't assume that `$OUT_DIR` is `out`. The source and output trees are very
33large these days, so some people put these on different disks. There are many
34other uses as well.
35
36Don't assume that `$OUT_DIR` is under `$PWD`, users can set it to a relative path
37or an absolute path.
38
39## $(shell) use in Android.mk files
40
41Don't use `$(shell)` to write files, create symlinks, etc. We expect to
42enforce this in the future. Encode these as build rules in the build graph
43instead.  This can be problematic in a number of ways:
44
45* `$(shell)` calls run at the beginning of every build, at minimum this slows
46  down build startup, but it can also trigger more build steps to run than are
47  necessary, since these files will change more often than necessary.
48* It's no longer possible for a stripped-down product configuration to opt-out
49  of these created files. It's better to have actual rules and dependencies set
50  up so that space isn't wasted, but the files are there when necessary.
51
52## Headers
53
54`LOCAL_COPY_HEADERS` is deprecated. Soong modules cannot use these headers, and
55when the VNDK is enabled, System modules in Make cannot declare or use them
56either.
57
58The set of global include paths provided by the build system is also being
59removed. They've been switched from using `-isystem` to `-I` already, and are
60removed entirely in some environments (vendor code when the VNDK is enabled).
61
62Instead, use `LOCAL_EXPORT_C_INCLUDE_DIRS`/`export_include_dirs`. These allow
63access to the headers automatically if you link to the associated code.
64
65If your library uses `LOCAL_EXPORT_C_INCLUDE_DIRS`/`export_include_dirs`, and
66the exported headers reference a library that you link to, use
67`LOCAL_EXPORT_SHARED_LIBRARY_HEADERS`/`LOCAL_EXPORT_STATIC_LIBRARY_HEADERS`/`LOCAL_EXPORT_HEADER_LIBRARY_HEADERS`
68(`export_shared_lib_headers`/`export_static_lib_headers`/`export_header_lib_headers`)
69to re-export the necessary headers to your users.
70
71Don't use non-local paths in your `LOCAL_EXPORT_C_INCLUDE_DIRS`, use one of the
72`LOCAL_EXPORT_*_HEADERS` instead. Non-local exported include dirs are not
73supported in Soong. You may need to either move your module definition up a
74directory (for example, if you have ./src/ and ./include/, you probably want to
75define the module in ./Android.bp, not ./src/Android.bp), define a header
76library and re-export it, or move the headers into a more appropriate location.
77
78Prefer to use header libraries (`BUILD_HEADER_LIBRARY`/ `cc_library_headers`)
79only if the headers are actually standalone, and do not have associated code.
80Sometimes there are headers that have header-only sections, but also define
81interfaces to a library. Prefer to split those header-only sections out to a
82separate header-only library containing only the header-only sections, and
83re-export that header library from the existing library. This will prevent
84accidentally linking more code than you need (slower at build and/or runtime),
85or accidentally not linking to a library that's actually necessary.
86
87Prefer `LOCAL_EXPORT_C_INCLUDE_DIRS` over `LOCAL_C_INCLUDES` as well.
88Eventually we'd like to remove `LOCAL_C_INCLUDES`, though significant cleanup
89will be required first. This will be necessary to detect cases where modules
90are using headers that shouldn't be available to them -- usually due to the
91lack of ABI/API guarantees, but for various other reasons as well: layering
92violations, planned deprecations, potential optimizations like C++ modules,
93etc.
94
95## Use defaults over variables
96
97Soong supports variable definitions in Android.bp files, but in many cases,
98it's better to use defaults modules like `cc_defaults`, `java_defaults`, etc.
99
100* It moves more information next to the values -- that the array of strings
101  will be used as a list of sources is useful, both for humans and automated
102  tools.  This is even more useful if it's used inside an architecture or
103  target specific property.
104* It can collect multiple pieces of information together into logical
105  inheritable groups that can be selected with a single property.
106
107## Custom build tools
108
109If writing multiple files from a tool, declare them all in the build graph.
110* Make: Use `.KATI_IMPLICIT_OUTPUTS`
111* Android.bp: Just add them to the `out` list in genrule
112* Custom Soong Plugin: Add to `Outputs` or `ImplicitOutputs`
113
114Declare all files read by the tool, either with a dependency if you can, or by
115writing a dependency file. Ninja supports a fairly limited set of dependency
116file formats. You can verify that the dependencies are read correctly with:
117
118```
119NINJA_ARGS="-t deps <output_file>" m
120```
121
122Prefer to list input files on the command line, otherwise we may not know to
123re-run your command when a new input file is added. Ninja does not treat a
124change in dependencies as something that would invalidate an action -- the
125command line would need to change, or one of the inputs would need to be newer
126than the output file. If you don't include the inputs in your command line, you
127may need to add the the directories to your dependency list or dependency file,
128so that any additions or removals from those directories would trigger your
129tool to be re-run. That can be more expensive than necessary though, since many
130editors will write temporary files into the same directory, so changing a
131README could trigger the directory's timestamp to be updated.
132
133Only control output files based on the command line, not by an input file. We
134need to know which files will be created before any inputs are read, since we
135generate the entire build graph before reading source files, or running your
136tool. This comes up with Java based tools fairly often -- they'll generate
137different output files based on the classes declared in their input files.
138We've worked around these tools with the "srcjar" concept, which is just a jar
139file containing the generated sources. Our Java compilation tasks understand
140*.srcjar files, and will extract them before passing them on to the compiler.
141
142## Libraries in PRODUCT_PACKAGES
143
144Most libraries aren't necessary to include in `PRODUCT_PACKAGES`, unless
145they're used dynamically via `dlopen`. If they're only used via
146`LOCAL_SHARED_LIBRARIES` / `shared_libs`, then those dependencies will trigger
147them to be installed when necessary. Adding unnecessary libraries into
148`PRODUCT_PACKAGES` will force them to always be installed, wasting space.
149
150## Removing conditionals
151
152Over-use of conditionals in the build files results in an untestable number
153of build combinations, leading to more build breakages.  It also makes the
154code less testable, as it must be built with each combination of flags to
155be tested.
156
157### Conditionally compiled module
158
159Conditionally compiling a module can generally be replaced with conditional
160installation:
161
162```
163ifeq (some condition)
164# body of the Android.mk file
165LOCAL_MODULE:= bt_logger
166include $(BUILD_EXECUTABLE)
167endif
168```
169
170Becomes:
171
172```
173cc_binary {
174    name: "bt_logger",
175    // body of the module
176}
177```
178
179And in a product Makefile somewhere (something included with
180`$(call inherit-product, ...)`:
181
182```
183ifeq (some condition) # Or no condition
184PRODUCT_PACKAGES += bt_logger
185endif
186```
187
188If the condition was on a type of board or product, it can often be dropped
189completely by putting the `PRODUCT_PACKAGES` entry in a product makefile that
190is included only by the correct products or boards.
191
192### Conditionally compiled module with multiple implementations
193
194If there are multiple implementations of the same module with one selected
195for compilation via a conditional, the implementations can sometimes be renamed
196to unique values.
197
198For example, the name of the gralloc HAL module can be overridden by the
199`ro.hardware.gralloc` system property:
200
201```
202# In hardware/acme/soc_a/gralloc/Android.mk:
203ifeq ($(TARGET_BOARD_PLATFORM),soc_a)
204LOCAL_MODULE := gralloc.acme
205...
206include $(BUILD_SHARED_LIBRARY)
207endif
208
209# In hardware/acme/soc_b/gralloc/Android.mk:
210ifeq ($(TARGET_BOARD_PLATFORM),soc_b)
211LOCAL_MODULE := gralloc.acme
212...
213include $(BUILD_SHARED_LIBRARY)
214endif
215```
216
217Becomes:
218```
219# In hardware/acme/soc_a/gralloc/Android.bp:
220cc_library {
221    name: "gralloc.soc_a",
222    ...
223}
224
225# In hardware/acme/soc_b/gralloc/Android.bp:
226cc_library {
227    name: "gralloc.soc_b",
228    ...
229}
230```
231
232Then to select the correct gralloc implementation, a product makefile inherited
233by products that use soc_a should contain:
234
235```
236PRODUCT_PACKAGES += gralloc.soc_a
237PRODUCT_PROPERTY_OVERRIDES += ro.hardware.gralloc=soc_a
238```
239
240In cases where the names cannot be made unique a `soong_namespace` should be
241used to partition a set of modules so that they are built only when the
242namespace is listed in `PRODUCT_SOONG_NAMESPACES`.  See the
243[Referencing Modules](../README.md#referencing-modules) section of the Soong
244README.md for more on namespaces.
245
246### Module with name based on variable
247
248HAL modules sometimes use variables like `$(TARGET_BOARD_PLATFORM)` in their
249module name.  These can be renamed to a fixed name.
250
251For example, the name of the gralloc HAL module can be overridden by the
252`ro.hardware.gralloc` system property:
253
254```
255LOCAL_MODULE := gralloc.$(TARGET_BOARD_PLATFORM)
256...
257include $(BUILD_SHARED_LIBRARY)
258```
259
260Becomes:
261```
262cc_library {
263    name: "gralloc.acme",
264    ...
265}
266```
267
268Then to select the correct gralloc implementation, a product makefile should
269contain:
270
271```
272PRODUCT_PACKAGES += gralloc.acme
273PRODUCT_PROPERTY_OVERRIDES += ro.hardware.gralloc=acme
274```
275
276### Conditionally used source files, libraries or flags
277
278The preferred solution is to convert the conditional to runtime, either by
279autodetecting the correct value or loading the value from a system property
280or a configuration file.
281
282As a last resort, if the conditional cannot be removed, a Soong plugin can
283be written in Go that can implement additional features for specific module
284types.  Soong plugins are inherently tightly coupled to the build system
285and will require ongoing maintenance as the build system is changed; so
286plugins should be used only when absolutely required.
287
288See [art/build/art.go](https://android.googlesource.com/platform/art/+/master/build/art.go)
289or [external/llvm/soong/llvm.go](https://android.googlesource.com/platform/external/llvm/+/master/soong/llvm.go)
290for examples of more complex conditionals on product variables or environment variables.
291