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