andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 1 | # Closure Compilation |
| 2 | |
| 3 | ## I just need to fix the compile! |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 4 | |
dbeam | 43f31dd | 2016-03-09 22:05:59 | [diff] [blame] | 5 | ### Pre-requisites |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 6 | |
dbeam | 43f31dd | 2016-03-09 22:05:59 | [diff] [blame] | 7 | You'll need Java 7 (preferably the OpenJDK version). To install on Ubuntu: |
dbeam | 707cf27 | 2016-03-10 04:12:13 | [diff] [blame^] | 8 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 9 | ```shell |
dbeam | 43f31dd | 2016-03-09 22:05:59 | [diff] [blame] | 10 | sudo apt-get install openjdk-7-jre |
dbeam | e3d03b7c | 2016-02-06 03:08:24 | [diff] [blame] | 11 | ``` |
| 12 | |
dbeam | 43f31dd | 2016-03-09 22:05:59 | [diff] [blame] | 13 | On Mac or Windows, visit: |
| 14 | [http://www.oracle.com/technetwork/java/javase/downloads/index.html](http://www.oracle.com/technetwork/java/javase/downloads/index.html) |
dbeam | e3d03b7c | 2016-02-06 03:08:24 | [diff] [blame] | 15 | |
dbeam | 43f31dd | 2016-03-09 22:05:59 | [diff] [blame] | 16 | ### Using ninja to compile the code |
| 17 | |
| 18 | We use GYP and ninja as our build system. To generate the ninja files from GYP: |
dbeam | 707cf27 | 2016-03-10 04:12:13 | [diff] [blame^] | 19 | |
dbeam | e3d03b7c | 2016-02-06 03:08:24 | [diff] [blame] | 20 | ```shell |
dbeam | 43f31dd | 2016-03-09 22:05:59 | [diff] [blame] | 21 | # notice the 2 in compiled_resources.gyp |
dbeam | e3d03b7c | 2016-02-06 03:08:24 | [diff] [blame] | 22 | GYP_GENERATORS=ninja tools/gyp/gyp --depth . third_party/closure_compiler/compiled_resources2.gyp |
dbeam | 43f31dd | 2016-03-09 22:05:59 | [diff] [blame] | 23 | ``` |
| 24 | |
| 25 | To compile the JavaScript: |
dbeam | 707cf27 | 2016-03-10 04:12:13 | [diff] [blame^] | 26 | |
dbeam | 43f31dd | 2016-03-09 22:05:59 | [diff] [blame] | 27 | ```shell |
| 28 | ninja -C out/Default -j4 |
| 29 | ``` |
| 30 | |
| 31 | The output should look something like this: |
dbeam | 707cf27 | 2016-03-10 04:12:13 | [diff] [blame^] | 32 | |
dbeam | 43f31dd | 2016-03-09 22:05:59 | [diff] [blame] | 33 | ```shell |
| 34 | ninja: Entering directory `out/Default/' |
| 35 | [30/106] ACTION Compiling chrome/browser/resources/md_history/constants.js |
| 36 | ``` |
| 37 | |
| 38 | To generate and run the **deprecated** v1 gyp format, remove the "2" from "compiled_resources2.gyp": |
dbeam | 707cf27 | 2016-03-10 04:12:13 | [diff] [blame^] | 39 | |
dbeam | 43f31dd | 2016-03-09 22:05:59 | [diff] [blame] | 40 | ```shell |
| 41 | $ GYP_GENERATORS=ninja tools/gyp/gyp --depth . third_party/closure_compiler/compiled_resources.gyp |
| 42 | ``` |
| 43 | |
| 44 | Compiling works the same way for both v1 and v2 systems: |
dbeam | 707cf27 | 2016-03-10 04:12:13 | [diff] [blame^] | 45 | |
dbeam | 43f31dd | 2016-03-09 22:05:59 | [diff] [blame] | 46 | ```shell |
| 47 | ninja -C out/Default -j4 |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 48 | ``` |
| 49 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 50 | ## Background |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 51 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 52 | In C++ and Java, compiling the code gives you _some_ level of protection against |
| 53 | misusing variables based on their type information. JavaScript is loosely typed |
| 54 | and therefore doesn't offer this safety. This makes writing JavaScript more |
| 55 | error prone as it's _one more thing_ to mess up. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 56 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 57 | Because having this safety is handy, Chrome now has a way to optionally |
| 58 | typecheck your JavaScript and produce compiled output with |
| 59 | [Closure Compiler](https://developers.google.com/closure/compiler/). |
dschuyler | 63043614 | 2015-09-22 05:10:16 | [diff] [blame] | 60 | The type information is |
| 61 | [annotated in comment tags](https://developers.google.com/closure/compiler/docs/js-for-compiler) |
| 62 | that are briefly described below. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 63 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 64 | See also: |
| 65 | [the design doc](https://docs.google.com/a/chromium.org/document/d/1Ee9ggmp6U-lM-w9WmxN5cSLkK9B5YAq14939Woo-JY0/edit). |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 66 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 67 | ## Assumptions |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 68 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 69 | A working Chrome checkout. See here: |
tfarina | 5f2d8e2f | 2016-03-04 09:57:33 | [diff] [blame] | 70 | https://www.chromium.org/developers/how-tos/get-the-code |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 71 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 72 | ## Typechecking Your Javascript |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 73 | |
| 74 | So you'd like to compile your JavaScript! |
| 75 | |
| 76 | Maybe you're working on a page that looks like this: |
| 77 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 78 | ```html |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 79 | <script src="other_file.js"></script> |
| 80 | <script src="my_product/my_file.js"></script> |
| 81 | ``` |
| 82 | |
| 83 | Where `other_file.js` contains: |
| 84 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 85 | ```javascript |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 86 | var wit = 100; |
| 87 | |
| 88 | // ... later on, sneakily ... |
| 89 | |
| 90 | wit += ' IQ'; // '100 IQ' |
| 91 | ``` |
| 92 | |
| 93 | and `src/my_product/my_file.js` contains: |
| 94 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 95 | ```javascript |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 96 | /** @type {number} */ var mensa = wit + 50; |
| 97 | alert(mensa); // '100 IQ50' instead of 150 |
| 98 | ``` |
| 99 | |
| 100 | In order to check that our code acts as we'd expect, we can create a |
| 101 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 102 | my_project/compiled_resources.gyp |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 103 | |
| 104 | with the contents: |
| 105 | |
| 106 | ``` |
| 107 | # Copyright 2015 The Chromium Authors. All rights reserved. |
| 108 | # Use of this source code is governed by a BSD-style license that can be |
| 109 | # found in the LICENSE file. |
| 110 | { |
| 111 | 'targets': [ |
| 112 | { |
| 113 | 'target_name': 'my_file', # file name without ".js" |
| 114 | |
| 115 | 'variables': { # Only use if necessary (no need to specify empty lists). |
| 116 | 'depends': [ |
| 117 | 'other_file.js', # or 'other_project/compiled_resources.gyp:target', |
| 118 | ], |
| 119 | 'externs': [ |
| 120 | '<(CLOSURE_DIR)/externs/any_needed_externs.js' # e.g. chrome.send(), chrome.app.window, etc. |
| 121 | ], |
| 122 | }, |
| 123 | |
| 124 | 'includes': ['../third_party/closure_compiler/compile_js.gypi'], |
| 125 | }, |
| 126 | ], |
| 127 | } |
| 128 | ``` |
| 129 | |
| 130 | You should get results like: |
| 131 | |
| 132 | ``` |
| 133 | (ERROR) Error in: my_project/my_file.js |
| 134 | ## /my/home/chromium/src/my_project/my_file.js:1: ERROR - initializing variable |
| 135 | ## found : string |
| 136 | ## required: number |
| 137 | ## /** @type {number} */ var mensa = wit + 50; |
| 138 | ## ^ |
| 139 | ``` |
| 140 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 141 | Yay! We can easily find our unexpected type errors and write less error-prone |
| 142 | code! |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 143 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 144 | ## Continuous Checking |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 145 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 146 | To compile your code on every commit, add a line to |
| 147 | /third_party/closure_compiler/compiled_resources.gyp |
| 148 | like this: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 149 | |
| 150 | ``` |
| 151 | { |
| 152 | 'targets': [ |
| 153 | { |
| 154 | 'target_name': 'compile_all_resources', |
| 155 | 'dependencies': [ |
| 156 | # ... other projects ... |
| 157 | ++ '../my_project/compiled_resources.gyp:*', |
| 158 | ], |
| 159 | } |
| 160 | ] |
| 161 | } |
| 162 | ``` |
| 163 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 164 | and the |
| 165 | [Closure compiler bot](http://build.chromium.org/p/chromium.fyi/builders/Closure%20Compilation%20Linux) |
| 166 | will [re-]compile your code whenever relevant .js files change. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 167 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 168 | ## Using Compiled JavaScript |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 169 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 170 | Compiled JavaScript is output in |
| 171 | `src/out/<Debug|Release>/gen/closure/my_project/my_file.js` along with a source |
| 172 | map for use in debugging. In order to use the compiled JavaScript, we can create |
| 173 | a |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 174 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 175 | my_project/my_project_resources.gpy |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 176 | |
| 177 | with the contents: |
| 178 | |
| 179 | ``` |
| 180 | # Copyright 2015 The Chromium Authors. All rights reserved. |
| 181 | # Use of this source code is governed by a BSD-style license that can be |
| 182 | # found in the LICENSE file. |
| 183 | |
| 184 | { |
| 185 | 'targets': [ |
| 186 | { |
| 187 | # GN version: //my_project/resources |
| 188 | 'target_name': 'my_project_resources', |
| 189 | 'type': 'none', |
| 190 | 'variables': { |
| 191 | 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/my_project', |
| 192 | 'my_file_gen_js': '<(SHARED_INTERMEDIATE_DIR)/closure/my_project/my_file.js', |
| 193 | }, |
| 194 | 'actions': [ |
| 195 | { |
| 196 | # GN version: //my_project/resources:my_project_resources |
| 197 | 'action_name': 'generate_my_project_resources', |
| 198 | 'variables': { |
| 199 | 'grit_grd_file': 'resources/my_project_resources.grd', |
| 200 | 'grit_additional_defines': [ |
| 201 | '-E', 'my_file_gen_js=<(my_file_gen_js)', |
| 202 | ], |
| 203 | }, |
| 204 | 'includes': [ '../build/grit_action.gypi' ], |
| 205 | }, |
| 206 | ], |
| 207 | 'includes': [ '../build/grit_target.gypi' ], |
| 208 | }, |
| 209 | ], |
| 210 | } |
| 211 | ``` |
| 212 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 213 | The variables can also be defined in an existing .gyp file if appropriate. The |
| 214 | variables can then be used in to create a |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 215 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 216 | my_project/my_project_resources.grd |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 217 | |
| 218 | with the contents: |
| 219 | |
| 220 | ``` |
| 221 | <?xml version="1.0" encoding="utf-8"?> |
| 222 | <grit-part> |
| 223 | <include name="IDR_MY_FILE_GEN_JS" file="${my_file_gen_js}" use_base_dir="false" type="BINDATA" /> |
| 224 | </grit-part> |
| 225 | ``` |
| 226 | |
| 227 | In your C++, the resource can be retrieved like this: |
| 228 | ``` |
| 229 | base::string16 my_script = |
| 230 | base::UTF8ToUTF16( |
| 231 | ResourceBundle::GetSharedInstance() |
| 232 | .GetRawDataResource(IDR_MY_FILE_GEN_JS) |
| 233 | .as_string()); |
| 234 | ``` |
| 235 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 236 | ## Debugging Compiled JavaScript |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 237 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 238 | Along with the compiled JavaScript, a source map is created: |
| 239 | `src/out/<Debug|Release>/gen/closure/my_project/my_file.js.map` |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 240 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 241 | Chrome DevTools has built in support for working with source maps: |
| 242 | https://developer.chrome.com/devtools/docs/javascript-debugging#source-maps |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 243 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 244 | In order to use the source map, you must first manually edit the path to the |
| 245 | 'sources' in the .js.map file that was generated. For example, if the source map |
| 246 | looks like this: |
| 247 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 248 | ``` |
| 249 | { |
| 250 | "version":3, |
| 251 | "file":"/tmp/gen/test_script.js", |
| 252 | "lineCount":1, |
| 253 | "mappings":"A,aAAA,IAAIA,OAASA,QAAQ,EAAG,CACtBC,KAAA,CAAM,OAAN,CADsB;", |
| 254 | "sources":["/tmp/tmp70_QUi"], |
| 255 | "names":["fooBar","alert"] |
| 256 | } |
| 257 | ``` |
| 258 | |
| 259 | sources should be changed to: |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 260 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 261 | ``` |
| 262 | ... |
| 263 | "sources":["/tmp/test_script.js"], |
| 264 | ... |
| 265 | ``` |
| 266 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 267 | In your browser, the source map can be loaded through the Chrome DevTools |
| 268 | context menu that appears when you right click in the compiled JavaScript source |
| 269 | body. A dialog will pop up prompting you for the path to the source map file. |
| 270 | Once the source map is loaded, the uncompiled version of the JavaScript will |
| 271 | appear in the Sources panel on the left. You can set break points in the |
| 272 | uncompiled version to help debug; behind the scenes Chrome will still be running |
| 273 | the compiled version of the JavaScript. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 274 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 275 | ## Additional Arguments |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 276 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 277 | `compile_js.gypi` accepts an optional `script_args` variable, which passes |
| 278 | additional arguments to `compile.py`, as well as an optional `closure_args` |
| 279 | variable, which passes additional arguments to the closure compiler. You may |
| 280 | also override the `disabled_closure_args` for more strict compilation. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 281 | |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 282 | For example, if you would like to specify multiple sources, strict compilation, |
| 283 | and an output wrapper, you would create a |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 284 | |
| 285 | ``` |
| 286 | my_project/compiled_resources.gyp |
| 287 | ``` |
| 288 | |
| 289 | with contents similar to this: |
| 290 | ``` |
| 291 | # Copyright 2015 The Chromium Authors. All rights reserved. |
| 292 | # Use of this source code is governed by a BSD-style license that can be |
| 293 | # found in the LICENSE file. |
| 294 | { |
| 295 | 'targets' :[ |
| 296 | { |
| 297 | 'target_name': 'my_file', |
| 298 | 'variables': { |
| 299 | 'source_files': [ |
| 300 | 'my_file.js', |
| 301 | 'my_file2.js', |
| 302 | ], |
| 303 | 'script_args': ['--no-single-file'], # required to process multiple files at once |
| 304 | 'closure_args': [ |
| 305 | 'output_wrapper=\'(function(){%output%})();\'', |
| 306 | 'jscomp_error=reportUnknownTypes', # the following three provide more strict compilation |
| 307 | 'jscomp_error=duplicate', |
| 308 | 'jscomp_error=misplacedTypeAnnotation', |
| 309 | ], |
| 310 | 'disabled_closure_args': [], # remove the disabled closure args for more strict compilation |
| 311 | }, |
| 312 | 'includes': ['../third_party/closure_compiler/compile_js.gypi'], |
| 313 | }, |
| 314 | ], |
| 315 | } |
andybons | 6eaa0c0d | 2015-08-26 20:12:52 | [diff] [blame] | 316 | ``` |