blob: 540abf6cb4a027e7216fa99395ea843cd664d0f2 [file] [log] [blame] [view]
andybons6eaa0c0d2015-08-26 20:12:521# Closure Compilation
2
3## I just need to fix the compile!
andybons3322f762015-08-24 21:37:094
dbeam43f31dd2016-03-09 22:05:595### Pre-requisites
andybons3322f762015-08-24 21:37:096
dbeam43f31dd2016-03-09 22:05:597You'll need Java 7 (preferably the OpenJDK version). To install on Ubuntu:
dbeam707cf272016-03-10 04:12:138
andybons6eaa0c0d2015-08-26 20:12:529```shell
dbeam43f31dd2016-03-09 22:05:5910sudo apt-get install openjdk-7-jre
dbeame3d03b7c2016-02-06 03:08:2411```
12
dbeam43f31dd2016-03-09 22:05:5913On 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)
dbeame3d03b7c2016-02-06 03:08:2415
dbeam43f31dd2016-03-09 22:05:5916### Using ninja to compile the code
17
18We use GYP and ninja as our build system. To generate the ninja files from GYP:
dbeam707cf272016-03-10 04:12:1319
dbeame3d03b7c2016-02-06 03:08:2420```shell
dbeam1ff34582016-03-10 18:30:3621# notice the 2 in compiled_resources2.gyp
dbeame3d03b7c2016-02-06 03:08:2422GYP_GENERATORS=ninja tools/gyp/gyp --depth . third_party/closure_compiler/compiled_resources2.gyp
dbeam43f31dd2016-03-09 22:05:5923```
24
25To compile the JavaScript:
dbeam707cf272016-03-10 04:12:1326
dbeam43f31dd2016-03-09 22:05:5927```shell
28ninja -C out/Default -j4
29```
30
31The output should look something like this:
dbeam707cf272016-03-10 04:12:1332
dbeam43f31dd2016-03-09 22:05:5933```shell
34ninja: Entering directory `out/Default/'
35[30/106] ACTION Compiling chrome/browser/resources/md_history/constants.js
36```
37
38To generate and run the **deprecated** v1 gyp format, remove the "2" from "compiled_resources2.gyp":
dbeam707cf272016-03-10 04:12:1339
dbeam43f31dd2016-03-09 22:05:5940```shell
41$ GYP_GENERATORS=ninja tools/gyp/gyp --depth . third_party/closure_compiler/compiled_resources.gyp
42```
43
44Compiling works the same way for both v1 and v2 systems:
dbeam707cf272016-03-10 04:12:1345
dbeam43f31dd2016-03-09 22:05:5946```shell
47ninja -C out/Default -j4
andybons3322f762015-08-24 21:37:0948```
49
andybons6eaa0c0d2015-08-26 20:12:5250## Background
andybons3322f762015-08-24 21:37:0951
andybons6eaa0c0d2015-08-26 20:12:5252In C++ and Java, compiling the code gives you _some_ level of protection against
53misusing variables based on their type information. JavaScript is loosely typed
54and therefore doesn't offer this safety. This makes writing JavaScript more
55error prone as it's _one more thing_ to mess up.
andybons3322f762015-08-24 21:37:0956
andybons6eaa0c0d2015-08-26 20:12:5257Because having this safety is handy, Chrome now has a way to optionally
58typecheck your JavaScript and produce compiled output with
59[Closure Compiler](https://developers.google.com/closure/compiler/).
dschuyler630436142015-09-22 05:10:1660The type information is
61[annotated in comment tags](https://developers.google.com/closure/compiler/docs/js-for-compiler)
62that are briefly described below.
andybons3322f762015-08-24 21:37:0963
andybons6eaa0c0d2015-08-26 20:12:5264See also:
65[the design doc](https://docs.google.com/a/chromium.org/document/d/1Ee9ggmp6U-lM-w9WmxN5cSLkK9B5YAq14939Woo-JY0/edit).
andybons3322f762015-08-24 21:37:0966
andybons6eaa0c0d2015-08-26 20:12:5267## Assumptions
andybons3322f762015-08-24 21:37:0968
andybons6eaa0c0d2015-08-26 20:12:5269A working Chrome checkout. See here:
tfarina5f2d8e2f2016-03-04 09:57:3370https://www.chromium.org/developers/how-tos/get-the-code
andybons3322f762015-08-24 21:37:0971
andybons6eaa0c0d2015-08-26 20:12:5272## Typechecking Your Javascript
andybons3322f762015-08-24 21:37:0973
74So you'd like to compile your JavaScript!
75
76Maybe you're working on a page that looks like this:
77
andybons6eaa0c0d2015-08-26 20:12:5278```html
andybons3322f762015-08-24 21:37:0979<script src="other_file.js"></script>
80<script src="my_product/my_file.js"></script>
81```
82
83Where `other_file.js` contains:
84
andybons6eaa0c0d2015-08-26 20:12:5285```javascript
andybons3322f762015-08-24 21:37:0986var wit = 100;
87
88// ... later on, sneakily ...
89
90wit += ' IQ'; // '100 IQ'
91```
92
93and `src/my_product/my_file.js` contains:
94
andybons6eaa0c0d2015-08-26 20:12:5295```javascript
andybons3322f762015-08-24 21:37:0996/** @type {number} */ var mensa = wit + 50;
97alert(mensa); // '100 IQ50' instead of 150
98```
99
100In order to check that our code acts as we'd expect, we can create a
101
dbeam1ff34582016-03-10 18:30:36102 my_project/compiled_resources2.gyp
andybons3322f762015-08-24 21:37:09103
104with the contents:
105
106```
dbeam1ff34582016-03-10 18:30:36107# Copyright 2016 The Chromium Authors. All rights reserved.
andybons3322f762015-08-24 21:37:09108# 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"
dbeam1ff34582016-03-10 18:30:36114 'dependencies': [ # No need to specify empty lists.
115 '../compiled_resources2.gyp:other_file',
116 '<(EXTERNS_GYP):any_needed_externs' # e.g. chrome.send(), chrome.app.window, etc.
117 ],
118 'includes': ['../third_party/closure_compiler/compile_js2.gypi'],
andybons3322f762015-08-24 21:37:09119 },
120 ],
121}
122```
123
124You should get results like:
125
126```
127(ERROR) Error in: my_project/my_file.js
128## /my/home/chromium/src/my_project/my_file.js:1: ERROR - initializing variable
129## found : string
130## required: number
131## /** @type {number} */ var mensa = wit + 50;
132## ^
133```
134
andybons6eaa0c0d2015-08-26 20:12:52135Yay! We can easily find our unexpected type errors and write less error-prone
136code!
andybons3322f762015-08-24 21:37:09137
andybons6eaa0c0d2015-08-26 20:12:52138## Continuous Checking
andybons3322f762015-08-24 21:37:09139
andybons6eaa0c0d2015-08-26 20:12:52140To compile your code on every commit, add a line to
141/third_party/closure_compiler/compiled_resources.gyp
142like this:
andybons3322f762015-08-24 21:37:09143
144```
145{
146 'targets': [
147 {
148 'target_name': 'compile_all_resources',
149 'dependencies': [
150 # ... other projects ...
dbeam1ff34582016-03-10 18:30:36151++ '../my_project/compiled_resources2.gyp:*',
andybons3322f762015-08-24 21:37:09152 ],
153 }
154 ]
155}
156```
157
andybons6eaa0c0d2015-08-26 20:12:52158and the
159[Closure compiler bot](http://build.chromium.org/p/chromium.fyi/builders/Closure%20Compilation%20Linux)
160will [re-]compile your code whenever relevant .js files change.
andybons3322f762015-08-24 21:37:09161
andybons6eaa0c0d2015-08-26 20:12:52162## Using Compiled JavaScript
andybons3322f762015-08-24 21:37:09163
andybons6eaa0c0d2015-08-26 20:12:52164Compiled JavaScript is output in
165`src/out/<Debug|Release>/gen/closure/my_project/my_file.js` along with a source
166map for use in debugging. In order to use the compiled JavaScript, we can create
167a
andybons3322f762015-08-24 21:37:09168
dbeam1ff34582016-03-10 18:30:36169 my_project/my_project_resources.gyp
andybons3322f762015-08-24 21:37:09170
171with the contents:
172
173```
174# Copyright 2015 The Chromium Authors. All rights reserved.
175# Use of this source code is governed by a BSD-style license that can be
176# found in the LICENSE file.
177
178{
179 'targets': [
180 {
181 # GN version: //my_project/resources
182 'target_name': 'my_project_resources',
183 'type': 'none',
184 'variables': {
185 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/my_project',
186 'my_file_gen_js': '<(SHARED_INTERMEDIATE_DIR)/closure/my_project/my_file.js',
187 },
188 'actions': [
189 {
190 # GN version: //my_project/resources:my_project_resources
191 'action_name': 'generate_my_project_resources',
192 'variables': {
193 'grit_grd_file': 'resources/my_project_resources.grd',
194 'grit_additional_defines': [
195 '-E', 'my_file_gen_js=<(my_file_gen_js)',
196 ],
197 },
198 'includes': [ '../build/grit_action.gypi' ],
199 },
200 ],
201 'includes': [ '../build/grit_target.gypi' ],
202 },
203 ],
204}
205```
206
andybons6eaa0c0d2015-08-26 20:12:52207The variables can also be defined in an existing .gyp file if appropriate. The
208variables can then be used in to create a
andybons3322f762015-08-24 21:37:09209
andybons6eaa0c0d2015-08-26 20:12:52210 my_project/my_project_resources.grd
andybons3322f762015-08-24 21:37:09211
212with the contents:
213
214```
215<?xml version="1.0" encoding="utf-8"?>
216<grit-part>
217 <include name="IDR_MY_FILE_GEN_JS" file="${my_file_gen_js}" use_base_dir="false" type="BINDATA" />
218</grit-part>
219```
220
221In your C++, the resource can be retrieved like this:
dbeam1ff34582016-03-10 18:30:36222
andybons3322f762015-08-24 21:37:09223```
224base::string16 my_script =
225 base::UTF8ToUTF16(
226 ResourceBundle::GetSharedInstance()
227 .GetRawDataResource(IDR_MY_FILE_GEN_JS)
228 .as_string());
229```
230
andybons6eaa0c0d2015-08-26 20:12:52231## Debugging Compiled JavaScript
andybons3322f762015-08-24 21:37:09232
andybons6eaa0c0d2015-08-26 20:12:52233Along with the compiled JavaScript, a source map is created:
234`src/out/<Debug|Release>/gen/closure/my_project/my_file.js.map`
andybons3322f762015-08-24 21:37:09235
andybons6eaa0c0d2015-08-26 20:12:52236Chrome DevTools has built in support for working with source maps:
237https://developer.chrome.com/devtools/docs/javascript-debugging#source-maps
andybons3322f762015-08-24 21:37:09238
andybons6eaa0c0d2015-08-26 20:12:52239In order to use the source map, you must first manually edit the path to the
240'sources' in the .js.map file that was generated. For example, if the source map
241looks like this:
242
andybons3322f762015-08-24 21:37:09243```
244{
245"version":3,
246"file":"/tmp/gen/test_script.js",
247"lineCount":1,
248"mappings":"A,aAAA,IAAIA,OAASA,QAAQ,EAAG,CACtBC,KAAA,CAAM,OAAN,CADsB;",
249"sources":["/tmp/tmp70_QUi"],
250"names":["fooBar","alert"]
251}
252```
253
254sources should be changed to:
andybons6eaa0c0d2015-08-26 20:12:52255
andybons3322f762015-08-24 21:37:09256```
257...
258"sources":["/tmp/test_script.js"],
259...
260```
261
andybons6eaa0c0d2015-08-26 20:12:52262In your browser, the source map can be loaded through the Chrome DevTools
263context menu that appears when you right click in the compiled JavaScript source
264body. A dialog will pop up prompting you for the path to the source map file.
265Once the source map is loaded, the uncompiled version of the JavaScript will
266appear in the Sources panel on the left. You can set break points in the
267uncompiled version to help debug; behind the scenes Chrome will still be running
268the compiled version of the JavaScript.
andybons3322f762015-08-24 21:37:09269
andybons6eaa0c0d2015-08-26 20:12:52270## Additional Arguments
andybons3322f762015-08-24 21:37:09271
andybons6eaa0c0d2015-08-26 20:12:52272`compile_js.gypi` accepts an optional `script_args` variable, which passes
273additional arguments to `compile.py`, as well as an optional `closure_args`
274variable, which passes additional arguments to the closure compiler. You may
275also override the `disabled_closure_args` for more strict compilation.
andybons3322f762015-08-24 21:37:09276
andybons6eaa0c0d2015-08-26 20:12:52277For example, if you would like to specify multiple sources, strict compilation,
278and an output wrapper, you would create a
andybons3322f762015-08-24 21:37:09279
280```
281my_project/compiled_resources.gyp
282```
283
284with contents similar to this:
dbeam1ff34582016-03-10 18:30:36285
andybons3322f762015-08-24 21:37:09286```
287# Copyright 2015 The Chromium Authors. All rights reserved.
288# Use of this source code is governed by a BSD-style license that can be
289# found in the LICENSE file.
290{
291 'targets' :[
292 {
293 'target_name': 'my_file',
294 'variables': {
295 'source_files': [
296 'my_file.js',
297 'my_file2.js',
298 ],
299 'script_args': ['--no-single-file'], # required to process multiple files at once
300 'closure_args': [
301 'output_wrapper=\'(function(){%output%})();\'',
302 'jscomp_error=reportUnknownTypes', # the following three provide more strict compilation
303 'jscomp_error=duplicate',
304 'jscomp_error=misplacedTypeAnnotation',
305 ],
306 'disabled_closure_args': [], # remove the disabled closure args for more strict compilation
307 },
308 'includes': ['../third_party/closure_compiler/compile_js.gypi'],
309 },
310 ],
311}
andybons6eaa0c0d2015-08-26 20:12:52312```