|
1 | 1 | = Compilation modes |
2 | 2 |
|
3 | | -The js_of_ocaml compiler supports two compilation modes. Whole program |
4 | | -compilation that compiles a bytecode executable to a single JavaScript |
5 | | -file and separate compilation that compiles individual compilation units |
6 | | -(.cmo) and libraries (.cma) to one or more javascript files. |
| 3 | +Js_of_ocaml supports two compilation modes: |
7 | 4 |
|
8 | | -==@@id="why-separate"@@ Why use separate compilation |
| 5 | +* **Whole program compilation** — compiles a bytecode executable to a single JavaScript file |
| 6 | +* **Separate compilation** — compiles individual units ({{{.cmo}}}) and libraries ({{{.cma}}}) to JavaScript files, then links them together |
9 | 7 |
|
10 | | -Separate compilation improves the overall compilation times and gives |
11 | | -(many) incremental build opportunities. |
| 8 | +==@@id="comparison"@@ Choosing a mode |
12 | 9 |
|
13 | | -These improvements come at the cost of bigger executable and slower |
14 | | -runtime. In particular, separate compilation will disable most cross |
15 | | -module and cross library optimizations and make the dead-code |
16 | | -elimination almost useless. |
| 10 | +|= |= Whole program |= Separate | |
| 11 | +| Build speed | Slower (recompiles everything) | Faster (incremental builds) | |
| 12 | +| Output size | Smaller (dead code elimination) | Larger | |
| 13 | +| Runtime speed | Faster (cross-module optimizations) | Slower | |
17 | 14 |
|
| 15 | +**Recommendation**: Use separate compilation during development for fast iteration, |
| 16 | +and whole program compilation for production releases. |
18 | 17 |
|
19 | | -==@@id="scheme"@@ Compilation scheme |
| 18 | +==@@id="whole-program"@@ Whole program compilation |
20 | 19 |
|
21 | | -Js_of_ocaml separate compilation is somewhat similar to the OCaml |
22 | | -separate compilation with some differences. |
| 20 | +This is the simplest mode. Compile your bytecode executable directly: |
23 | 21 |
|
24 | | -The general idea is to generate one JavaScript file containing the |
25 | | -JavaScript runtime and JavaScript files for every compilation unit (or |
26 | | -library) needed to run the program. One can then link all individual |
27 | | -JavaScript files together, respecting the order induced by |
28 | | -dependencies, into a single JavaScript file. |
29 | | - |
30 | | -=== 1. Build the runtime |
31 | | - |
32 | | -To build a standalone runtime: |
33 | 22 | {{{ |
34 | | -js_of_ocaml build-runtime -o my-runtime.js |
| 23 | +js_of_ocaml program.byte -o program.js |
35 | 24 | }}} |
36 | 25 |
|
37 | | -Additional runtime files are sometimes needed and can be passed on the |
38 | | -command line. In particular, some packages/libraries come with their |
39 | | -own runtime file. |
| 26 | +Some libraries require additional runtime files. See |
| 27 | +<<a_manual chapter="linker" fragment="finding-runtime"|JavaScript primitives>> for how to discover them. |
40 | 28 |
|
41 | | -See <<a_manual chapter="linker" fragment="finding-runtime"|Linking JavaScript code>> for how to discover |
42 | | -these files. |
| 29 | +The compiler performs global dead-code elimination and cross-module optimizations, |
| 30 | +producing smaller and faster output. |
43 | 31 |
|
44 | | -=== 2. Build compilation units |
| 32 | +==@@id="separate"@@ Separate compilation |
45 | 33 |
|
46 | | -In addition to bytecode executables, the js_of_ocaml compiler can |
47 | | -process compilation units (.cmo) and libraries (.cma). |
| 34 | +Separate compilation involves three steps: build the runtime, compile units, and link. |
48 | 35 |
|
49 | | -One can specify the name of the generated file with {{{-o}}}. By |
50 | | -default, the name will be inferred from the input file. |
51 | | - |
52 | | -By default, the js_of_ocaml compiler will generate a single JavaScript |
53 | | -file when compiling an ocaml library (.cma). One can choose to |
54 | | -generate one file per compilation unit by passing the |
55 | | -{{{--keep-unit-names}}} flag. In that case, the name will be inferred |
56 | | -from the compilation unit name and {{{-o}}} will be understood as a |
57 | | -destination directory. |
| 36 | +=== 1. Build the runtime |
58 | 37 |
|
59 | 38 | {{{ |
60 | | -js_of_ocaml modname.cmo |
61 | | -#generates modname.js |
| 39 | +js_of_ocaml build-runtime -o runtime.js |
| 40 | +}}} |
62 | 41 |
|
63 | | -js_of_ocaml libname.cma |
64 | | -#generates libname.js |
| 42 | +Some libraries require additional runtime files. See |
| 43 | +<<a_manual chapter="linker" fragment="finding-runtime"|JavaScript primitives>> for how to discover them. |
65 | 44 |
|
66 | | -js_of_ocaml libname.cma -o name.js |
67 | | -#generates name.js |
| 45 | +=== 2. Compile units and libraries |
68 | 46 |
|
69 | | -mkdir libname |
70 | | -js_of_ocaml libname.cma --keep-unit-names -o libname/ |
71 | | -#generates libname/Unit1.js libname/Unit2.js |
| 47 | +{{{ |
| 48 | +js_of_ocaml mymodule.cmo # generates mymodule.js |
| 49 | +js_of_ocaml mylib.cma # generates mylib.js |
| 50 | +js_of_ocaml mylib.cma -o lib.js # generates lib.js |
72 | 51 | }}} |
73 | 52 |
|
74 | 53 | === 3. Link |
75 | 54 |
|
76 | | -The final step is to link all individual JavaScript files into a |
77 | | -single one. The {{{link}}} command of the js_of_ocaml-compiler takes |
78 | | -a list of JavaScript filename as input and concatenates them all |
79 | | -(merging source-maps together if needed). |
| 55 | +Concatenate all JavaScript files in dependency order: |
80 | 56 |
|
81 | 57 | {{{ |
82 | | -js_of_ocaml link my-runtime.js stdlib.js libname.js modname.js std_exit.js -o myexe.js |
| 58 | +js_of_ocaml link runtime.js stdlib.js mylib.js mymodule.js std_exit.js -o program.js |
83 | 59 | }}} |
84 | 60 |
|
85 | | -Note that, unlike the ocaml compiler, the ocaml stdlib and std_exit need to be |
86 | | -passed explicitly (here, {stdlib.js} and {std_exit.js} are the result of compiling |
87 | | -{stdlib.cma} and {std_exit.cmo}). |
| 61 | +**Note**: Unlike OCaml linking, you must explicitly include {{{stdlib.js}}} and {{{std_exit.js}}}. |
88 | 62 |
|
89 | | -==@@id="dune"@@ Support in dune |
| 63 | +==@@id="dune"@@ Dune integration |
90 | 64 |
|
91 | | -Support for js_of_ocaml separate compilation has been added to dune |
92 | | -(previously jbuilder). The separate compilation mode is selected when |
93 | | -the build profile is dev, which is the default. There is currently no |
94 | | -other way to control this behavior. |
| 65 | +Dune handles compilation mode automatically based on the build profile: |
95 | 66 |
|
96 | | -See [[https://dune.readthedocs.io/en/latest/jsoo.html|dune documentation]] |
| 67 | +|= Profile |= Mode | |
| 68 | +| {{{dev}}} (default) | Separate compilation | |
| 69 | +| {{{release}}} | Whole program compilation | |
| 70 | + |
| 71 | +To build with a specific profile: |
| 72 | +{{{ |
| 73 | +dune build --profile release |
| 74 | +}}} |
| 75 | + |
| 76 | +Or configure the default in {{{dune-project}}} or {{{dune-workspace}}}. |
| 77 | + |
| 78 | +See [[https://dune.readthedocs.io/en/latest/jsoo.html|dune documentation]] for details. |
| 79 | + |
| 80 | +==@@id="source-maps"@@ Source maps with separate compilation |
| 81 | + |
| 82 | +For source maps in separate compilation mode, use inline source maps: |
| 83 | + |
| 84 | +{{{ |
| 85 | +js_of_ocaml --source-map-inline mymodule.cmo |
| 86 | +}}} |
97 | 87 |
|
98 | | -==@@id="source-maps"@@ Source-maps support |
| 88 | +The {{{link}}} command will merge source maps automatically. |
99 | 89 |
|
100 | | -Separate compilation can be used together with source-map. The |
101 | | -recommended way is to build all individual javascript files with |
102 | | -inlined source-map by passing {{{--source-map-inline}}} to |
103 | | -{{{js_of_ocaml}}}. |
| 90 | +== See also |
104 | 91 |
|
105 | | -Note that other configurations have not been well tested and might |
106 | | -require additional tuning. |
| 92 | +* <<a_manual chapter="options"|Command-line options>> — Full list of compiler flags |
| 93 | +* <<a_manual chapter="linker"|JavaScript primitives>> — Runtime files and custom primitives |
0 commit comments