blob: 7502d51cf747aef37c814d8601221f30716b674e [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
dbeam43f31dd2016-03-09 22:05:5921# notice the 2 in compiled_resources.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
andybons6eaa0c0d2015-08-26 20:12:52102 my_project/compiled_resources.gyp
andybons3322f762015-08-24 21:37:09103
104with 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
130You 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
andybons6eaa0c0d2015-08-26 20:12:52141Yay! We can easily find our unexpected type errors and write less error-prone
142code!
andybons3322f762015-08-24 21:37:09143
andybons6eaa0c0d2015-08-26 20:12:52144## Continuous Checking
andybons3322f762015-08-24 21:37:09145
andybons6eaa0c0d2015-08-26 20:12:52146To compile your code on every commit, add a line to
147/third_party/closure_compiler/compiled_resources.gyp
148like this:
andybons3322f762015-08-24 21:37:09149
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
andybons6eaa0c0d2015-08-26 20:12:52164and the
165[Closure compiler bot](http://build.chromium.org/p/chromium.fyi/builders/Closure%20Compilation%20Linux)
166will [re-]compile your code whenever relevant .js files change.
andybons3322f762015-08-24 21:37:09167
andybons6eaa0c0d2015-08-26 20:12:52168## Using Compiled JavaScript
andybons3322f762015-08-24 21:37:09169
andybons6eaa0c0d2015-08-26 20:12:52170Compiled JavaScript is output in
171`src/out/<Debug|Release>/gen/closure/my_project/my_file.js` along with a source
172map for use in debugging. In order to use the compiled JavaScript, we can create
173a
andybons3322f762015-08-24 21:37:09174
andybons6eaa0c0d2015-08-26 20:12:52175 my_project/my_project_resources.gpy
andybons3322f762015-08-24 21:37:09176
177with 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
andybons6eaa0c0d2015-08-26 20:12:52213The variables can also be defined in an existing .gyp file if appropriate. The
214variables can then be used in to create a
andybons3322f762015-08-24 21:37:09215
andybons6eaa0c0d2015-08-26 20:12:52216 my_project/my_project_resources.grd
andybons3322f762015-08-24 21:37:09217
218with 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
227In your C++, the resource can be retrieved like this:
228```
229base::string16 my_script =
230 base::UTF8ToUTF16(
231 ResourceBundle::GetSharedInstance()
232 .GetRawDataResource(IDR_MY_FILE_GEN_JS)
233 .as_string());
234```
235
andybons6eaa0c0d2015-08-26 20:12:52236## Debugging Compiled JavaScript
andybons3322f762015-08-24 21:37:09237
andybons6eaa0c0d2015-08-26 20:12:52238Along with the compiled JavaScript, a source map is created:
239`src/out/<Debug|Release>/gen/closure/my_project/my_file.js.map`
andybons3322f762015-08-24 21:37:09240
andybons6eaa0c0d2015-08-26 20:12:52241Chrome DevTools has built in support for working with source maps:
242https://developer.chrome.com/devtools/docs/javascript-debugging#source-maps
andybons3322f762015-08-24 21:37:09243
andybons6eaa0c0d2015-08-26 20:12:52244In 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
246looks like this:
247
andybons3322f762015-08-24 21:37:09248```
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
259sources should be changed to:
andybons6eaa0c0d2015-08-26 20:12:52260
andybons3322f762015-08-24 21:37:09261```
262...
263"sources":["/tmp/test_script.js"],
264...
265```
266
andybons6eaa0c0d2015-08-26 20:12:52267In your browser, the source map can be loaded through the Chrome DevTools
268context menu that appears when you right click in the compiled JavaScript source
269body. A dialog will pop up prompting you for the path to the source map file.
270Once the source map is loaded, the uncompiled version of the JavaScript will
271appear in the Sources panel on the left. You can set break points in the
272uncompiled version to help debug; behind the scenes Chrome will still be running
273the compiled version of the JavaScript.
andybons3322f762015-08-24 21:37:09274
andybons6eaa0c0d2015-08-26 20:12:52275## Additional Arguments
andybons3322f762015-08-24 21:37:09276
andybons6eaa0c0d2015-08-26 20:12:52277`compile_js.gypi` accepts an optional `script_args` variable, which passes
278additional arguments to `compile.py`, as well as an optional `closure_args`
279variable, which passes additional arguments to the closure compiler. You may
280also override the `disabled_closure_args` for more strict compilation.
andybons3322f762015-08-24 21:37:09281
andybons6eaa0c0d2015-08-26 20:12:52282For example, if you would like to specify multiple sources, strict compilation,
283and an output wrapper, you would create a
andybons3322f762015-08-24 21:37:09284
285```
286my_project/compiled_resources.gyp
287```
288
289with 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}
andybons6eaa0c0d2015-08-26 20:12:52316```