OLD | NEW |
(Empty) | |
| 1 # The MB (Meta-Build wrapper) design spec |
| 2 |
| 3 [TOC] |
| 4 |
| 5 ## Summary of Design Requirements |
| 6 |
| 7 MB is intended to address two major aspects of the GYP -> GN |
| 8 transition for Chromium and two minor aspects: |
| 9 |
| 10 1. "bot toggling" - make it so that we can easily flip a given bot |
| 11 back and forth between GN and GYP. |
| 12 |
| 13 2. "bot configuration" - provide a single source of truth for all of |
| 14 the different configurations (os/arch/gyp_define combinations) of |
| 15 Chromium that are supported. |
| 16 |
| 17 3. "multi-build support" - make it easier for developers to build |
| 18 multiple different build configs (gyp and gn, or linux and android, |
| 19 for example) in a single checkout. |
| 20 |
| 21 4. "gn configs" - explore ways to manage sets of GN flags more easily |
| 22 (i.e., all the flags needed for a ChromeCast build, or the Linux ASAN |
| 23 bot). Eventually we may migrate this functionality into GN itself. |
| 24 |
| 25 MB must handle at least the `gen` and `analyze` steps on the bots, i.e., |
| 26 we need to wrap both the `gyp_chromium` invocation to generate the |
| 27 Ninja files, and the `analyze` step that takes a list of modified files |
| 28 and a list of targets to build and returns which targets are affected by |
| 29 the files. |
| 30 |
| 31 MB may also provide a `build` wrapper in order to build multiple |
| 32 configurations in a single step for developer convenience. |
| 33 |
| 34 ## Design |
| 35 |
| 36 MB is intended to be as simple as possible, and to defer as much work as |
| 37 possible to GN or GYP. It should live as a very simple Python wrapper |
| 38 that offers little in the way of surprises. |
| 39 |
| 40 ### Command line |
| 41 |
| 42 It is structured as a single binary that supports a list of subcommands: |
| 43 |
| 44 * `mb gen //out/Release chromium_linux_rel` |
| 45 * `mb analyze -i input.json -o output.json chromium_linux_rel` |
| 46 |
| 47 ### Configurations |
| 48 |
| 49 `mb` looks in the `//build/mb_conf.pyl` config file to determine whether |
| 50 to use GYP or GN for a particular build directory, and what set of flags |
| 51 (`GYP_DEFINES` or `gn args`) to use. |
| 52 |
| 53 `mb_conf.pyl` is structured as a file containing a single PYthon Literal |
| 54 expression, a dictionary with two main keys, `build_configs` and |
| 55 `mixins`. |
| 56 |
| 57 The `build_configs` key points to a dictionary of named build |
| 58 configurations. Each name should be either a bot name (e.g., |
| 59 `chromium_linux_rel`) or an obvious developer configuration |
| 60 (`gyp_shared_debug`). |
| 61 |
| 62 There should be an entry in this dict for every supported configuration |
| 63 of Chromium, meaning every configuration we have a bot for, and every |
| 64 configuration commonly used by develpers but that we may not have a bot |
| 65 for. |
| 66 |
| 67 Each build_config entry then points to a list of "mixins" that will |
| 68 define what that build_config does. Each item in the list must be |
| 69 an entry in the dictionary value of the `mixins` key. |
| 70 |
| 71 Each entry is itself a diction that contains one or more of the |
| 72 following keys: |
| 73 |
| 74 * `gyp_configs`: a list of the configurations to build, e.g., |
| 75 ['Release', 'Release_x64']; specifying this in conjunction |
| 76 with type=gyp_one_config can reduce the time it takes to run gyp_chromium |
| 77 a bit. |
| 78 * `gyp_defines`: a string containing a list of GYP_DEFINES. |
| 79 * `gn_args`: a string containing a list of values passed to gn --args. |
| 80 * `mixins`: a list of other mixins that should be included. |
| 81 * `type`: a string with either the value `gyp`, `gyp_one_config`, or `gn`; |
| 82 setting this indicates which meta-build tool to use. |
| 83 |
| 84 When `mb gen` executes, it takes a build_config name, looks it up in the |
| 85 'build_configs' dict, and then does a left-to-right expansion of the |
| 86 mixins; gyp_defines and gn_args values are concatenated, and type and |
| 87 gyp_configs values override each other. |
| 88 |
| 89 For example, if you had: |
| 90 |
| 91 ``` |
| 92 { |
| 93 'build_configs`: { |
| 94 'linux_release_trybot': ['gyp_release', 'trybot'], |
| 95 'gn_shared_debug': None, |
| 96 } |
| 97 'mixins': { |
| 98 'bot': { |
| 99 'gyp_defines': 'use_goma=1 dcheck_always_on=0', |
| 100 'gn_args': 'use_goma=true dcheck_always_on=false', |
| 101 }, |
| 102 'debug': { |
| 103 'gn_args': 'is_debug=true', |
| 104 }, |
| 105 'gn': {'type': 'gn'}, |
| 106 'gyp_release': { |
| 107 'gyp_config': 'Release' |
| 108 'mixins': ['release'], |
| 109 'type': 'gyp', |
| 110 }, |
| 111 'release': { |
| 112 'gn_args': 'is_debug=false', |
| 113 } |
| 114 'shared': { |
| 115 'gn_args': 'is_component_build=true', |
| 116 'gyp_defines': 'component=shared_library', |
| 117 }, |
| 118 'trybot': { |
| 119 'gyp_defines': 'dcheck_always_on=1', |
| 120 'gn_args': 'dcheck_always_on=true', |
| 121 } |
| 122 } |
| 123 } |
| 124 |
| 125 and you ran `mb gen //out/Release linux_release_trybot`, it would |
| 126 translate into a call to `gyp_chromium -G Release` with `GYP_DEFINES` set to |
| 127 `"use_goma=true dcheck_always_on=false dcheck_always_on=true"`. |
| 128 |
| 129 (From that you can see that mb is intentionally dumb and does not |
| 130 attempt to de-dup the flags, it lets gyp do that). |
| 131 |
| 132 ### Handling the analyze step |
| 133 |
| 134 The way a GYP bot runs the "analyze" step is to pass gyp_chromium |
| 135 two filename paths. |
| 136 |
| 137 The first path is to a JSON file used as input; it contains a single |
| 138 object with a two fields: |
| 139 |
| 140 * `files`: an array of the modified filenames to check (as |
| 141 paths relative to the checkout root). |
| 142 * `targets`: an array of the unqualified target names to check. |
| 143 |
| 144 The second path is a path where the analyze generator should write the |
| 145 result, also as a JSON object. This object may contain the following |
| 146 fields: |
| 147 |
| 148 * `error`: this should only be present if something failed. |
| 149 * `targets`: the subset of the input `targets` that depend on the |
| 150 input `files`. |
| 151 * `build_targets`: the minimal subset of targets needed to build all |
| 152 of `targets` that were affected. |
| 153 * `status`: one of three strings: |
| 154 * `"Found dependency"` (build the `build_targets`) |
| 155 * `"No dependency"` (i.e., no build needed) |
| 156 * `"Found dependency (all)"` (build everything, in which case |
| 157 `targets` and `build_targets` are not returned). |
| 158 |
| 159 mb replicates this command line interface, through `'mb analyze -i |
| 160 input.json -o output.json build_config`. |
| 161 |
| 162 It implements the equivalent functionality in GN by calling `'gn refs |
| 163 [list of files] --type=executable --all --as=output` and filtering the |
| 164 output to match the list of targets. |
| 165 |
| 166 ### Multi-build support |
| 167 |
| 168 Today one can pass options to GYP builds in many different ways: |
| 169 |
| 170 * specifying args directly to `gyp_chromium` with the `-D` flag. |
| 171 * setting the `GYP_DEFINES` environment variable. |
| 172 * setting the `GYP_DEFINES` key in a file named `chromium.gyp_env` that |
| 173 lives in the directory above `src`. |
| 174 * setting variables in a file in `~/.gyp/include.gypi`. |
| 175 |
| 176 Each of these approaches has advantages and disadvantages; the first two |
| 177 are flexible, and allow you to customize each build directory (except |
| 178 that you cannot have different flags for debug and release builds unless |
| 179 you invoke gyp twice and only build one config each time), but they |
| 180 require you to set something every time you run the command line (i.e., |
| 181 extra typing). The third saves typing, but does not allow you to |
| 182 generate different build dirs in a single checkout; the fourth applies |
| 183 not only to every build dir, but every checkout. |
| 184 |
| 185 GN, on the other hand, only supports setting flags via the `--args` |
| 186 command line argument, or the `args.gn` file in the build directory; |
| 187 these are the equivalent of the first two GYP methods. |
| 188 |
| 189 Ideally we would have a mechanism that offered the best of both worlds: |
| 190 we could customize each build directory (including whether to use gyp or |
| 191 gn), and we could generate multiple directories at once. |
| 192 |
| 193 MB supports this by looking for a config file named `builds.pyl` in the |
| 194 directory above the checkout. The builds.pyl config file should contain |
| 195 a dictionary where each key indicates the path to a desired build |
| 196 directory, and each value specifies a particular build config (one of |
| 197 the valid `build_config` values in `//build/mb_conf.pyl`). |
| 198 |
| 199 For example, this: |
| 200 |
| 201 ``` |
| 202 src% cat ../build.pyl |
| 203 { |
| 204 "//out/Release": ["linux_release_trybot"], |
| 205 "//out/Debug.gn": ["gn_shared_debug"], |
| 206 } |
| 207 src% mbw gen |
| 208 src% |
| 209 ``` |
| 210 |
| 211 is the equivalent of: |
| 212 |
| 213 ``` |
| 214 src% GYP_DEFINES="use_goma=1 dcheck_always_on=0 dcheck_always_on=1" build/gyp_ch
romium -G config=Release -G output_dir=out |
| 215 src% gn gen //out/Debug.gn --args='use_goma=true dcheck_always_on=true dcheck_al
ways_on=false' |
| 216 ``` |
| 217 |
| 218 ## Detailed Design Requirements and Rationale |
| 219 |
| 220 This section is collection of semi-organized notes on why MB is the way |
| 221 it is |
| 222 |
| 223 ### in-tree or out-of-tree |
| 224 |
| 225 The first issue is whether or not this should exist as a script in |
| 226 Chromium at all; an alternative would be to simply change the bot |
| 227 configurations to know whether to use GYP or GN, and which flags to |
| 228 pass. |
| 229 |
| 230 That would certainly work, but experience over the past two years |
| 231 suggests a few things: |
| 232 |
| 233 * we should push as much logic as we can into the source repositories |
| 234 so that they can be versioned and changed atomically with changes to |
| 235 the product code; having to coordinate changes between src/ and |
| 236 build/ is at best annoying and can lead to weird errors. |
| 237 * the infra team would really like to move to providing |
| 238 product-independent services (i.e., not have to do one thing for |
| 239 Chromium, another for NaCl, a third for V8, etc.). |
| 240 * we found that during the SVN->GIT migration the ability to flip bot |
| 241 configurations between the two via changes to a file in chromium |
| 242 was very useful. |
| 243 |
| 244 All of this suggests that the interface between bots and Chromium should |
| 245 be a simple one, hiding as much of the chromium logic as possible. |
| 246 |
| 247 ### Do we need multi-build support? |
| 248 |
| 249 Strictly speaking, no. MB could be useful just as a bot->chromium |
| 250 interface. However, we do know that managing multiple build directories |
| 251 is often awkward for many Chromium devs, at least until they write their |
| 252 own wrappers. Many devs often also use multiple checkouts here, which |
| 253 has higher overhead and can lead to confusion. |
| 254 |
| 255 We also know that some people miss the equivalents of setting |
| 256 environment variables, chromium.gyp_env, etc., and would like an easier |
| 257 way to make sure that multiple build dirs can share settings (like |
| 258 use_goma and goma_dir, for example). |
| 259 |
| 260 ### Why not have MB be smarter about de-duping flags? |
| 261 |
| 262 This just adds complexity to the MB implementation, and duplicates logic |
| 263 that GYP and GN already have to support anyway; in particular, it might |
| 264 require MB to know how to parse GYP and GN values. The belief is that |
| 265 if MB does *not* do this, it will lead to fewer surprises. |
| 266 |
| 267 It will not be hard to change this if need be. |
| 268 |
| 269 ### Non-goals |
| 270 |
| 271 * MB is not intended to replace the |
| 272 [CR tool](https://code.google.com/p/chromium/wiki/CRUserManual). It |
| 273 is only intended to replace the gyp_chromium part of `'gclient |
| 274 runhooks'`. Hopefully the CR tool will learn about and integrate w/ MB |
| 275 once MB becomes stable. |
| 276 |
| 277 * MB is not (yet?) intended to replace direct invocation of GN or GYP for |
| 278 complicated build scenarios (aka ChromeOS), where multiple flags need |
| 279 to be set to user-defined paths for specific toolchains (e.g., where |
| 280 ChromeOS needs to specify specific board types and compilers). |
| 281 |
| 282 ### Open issues |
| 283 |
| 284 * Some common flags (goma_dir being the obvious one) may need to be |
| 285 specified via the user, and it's unclear how to integrate this with |
| 286 the concept of build_configs. |
| 287 |
| 288 Right now, MB has hard-coded support for a few flags (i.e., you can |
| 289 pass the --goma-dir flag, and it will know to expand "${goma_dir}" in |
| 290 the string before calling out to the tool. We may want to generalize |
| 291 this to a common key/value approach (perhaps then meeting the |
| 292 ChromeOS non-goal, above), or we may want to keep this very strictly |
| 293 limited for simplicity. |
| 294 |
| 295 * Do we need to add a `'mb build'` command? |
| 296 |
| 297 Being able to update multiple build dirs after syncing is useful, but |
| 298 for GN-based dirs, ninja will do it for us automatically. It's unclear |
| 299 if enough people will have a mixture of GYP and GN builds to make this |
| 300 worth it to add. |
| 301 |
| 302 * How do we integrate `mb` with `gclient runhooks`? |
| 303 |
| 304 On the bots, we will disable gyp_chromium as part of runhooks (using |
| 305 GYP_CHROMIUM_NO_ACTION=1), so that mb shows up as a separate step. |
| 306 |
| 307 However, gyp_chromium is run by default as part of `gclient runhooks` |
| 308 and `gclient sync` for normal developers, and we probably don't want |
| 309 to make everyone need to use GYP_CHROMIUM_NO_ACTION. Given that |
| 310 `mb gen` with no arguments and no builds.pyl file is equivalent to |
| 311 `gyp_chromium`, we might want to just update the hook to call mb. |
| 312 Are there downsides to doing so? |
OLD | NEW |