Merge branch 'master' into jsoo-cctx

This commit is contained in:
Rudi Grinberg 2018-07-31 08:56:35 +02:00 committed by GitHub
commit 5c93619b47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 188 additions and 83 deletions

View File

@ -5,6 +5,10 @@ next
passing in `--root` in conjunction with `--workspace` or `--config` would not passing in `--root` in conjunction with `--workspace` or `--config` would not
work correctly (#997, @rgrinberg) work correctly (#997, @rgrinberg)
- Add support for customizing env nodes in workspace files. The `env` stanza is
now allowed in toplevel position in the workspace file, or for individual
contexts. This feature requires `(dune lang 1.1)` (#1038, @rgrinberg)
1.0.1 (19/07/2018) 1.0.1 (19/07/2018)
------------------ ------------------

View File

@ -488,7 +488,7 @@ stanza is rejected by dune:
ocamllex ocamllex
-------- --------
``(ocamllex (<names>))`` is essentially a shorthand for: ``(ocamllex <names>)`` is essentially a shorthand for:
.. code:: scheme .. code:: scheme
@ -503,13 +503,13 @@ To use a different rule mode, use the long form:
.. code:: scheme .. code:: scheme
(ocamllex (ocamllex
(modules (<names>)) (modules <names>)
(mode <mode>)) (mode <mode>))
ocamlyacc ocamlyacc
--------- ---------
``(ocamlyacc (<names>))`` is essentially a shorthand for: ``(ocamlyacc <names>)`` is essentially a shorthand for:
.. code:: scheme .. code:: scheme
@ -524,7 +524,7 @@ To use a different rule mode, use the long form:
.. code:: scheme .. code:: scheme
(ocamlyacc (ocamlyacc
(modules (<names>)) (modules <names>)
(mode <mode>)) (mode <mode>))
menhir menhir
@ -1231,22 +1231,8 @@ follows:
js_of_ocaml js_of_ocaml
----------- -----------
In ``library`` and ``executables`` stanzas, you can specify js_of_ocaml options A :ref:`dune-jsoo-field` exists in executable and libraries stanzas that allows
using ``(js_of_ocaml (<js_of_ocaml-options>))``. one to customize options relevant to jsoo.
``<js_of_ocaml-options>`` are all optional:
- ``(flags <flags>)`` to specify flags passed to ``js_of_ocaml``. This field
supports ``(:include ...)`` forms
- ``(javascript_files (<files-list>))`` to specify ``js_of_ocaml`` JavaScript
runtime files.
``<flags>`` is specified in the `Ordered set language`_.
The default value for ``(flags ...)`` depends on the selected build
profile. The build profile ``dev`` (the default) will enable sourcemap
and the pretty JavaScript output.
.. _user-actions: .. _user-actions:

View File

@ -20,6 +20,7 @@ Welcome to dune's documentation!
advanced-topics advanced-topics
configurator configurator
menhir menhir
jsoo
faq faq
known-issues known-issues
migration migration

83
doc/jsoo.rst Normal file
View File

@ -0,0 +1,83 @@
***********
js_of_ocaml
***********
js_of_ocaml_ is a compiler from OCaml to JavaScript. The compiler works by
translating OCaml bytecode to JS files. From here on, we'll abbreviate
js_of_ocaml to jsoo. The compiler can be installed with opam:
.. code:: bash
$ opam install js_of_ocaml-compiler
Compiling to JS
===============
Dune has full support building jsoo libraries and executables transparently.
There's no need to customize or enable anything to compile ocaml
libraries/executables to JS.
To build a JS executable, just define an executable as you would normally.
Consider this example:
.. code:: bash
echo 'print_endline "hello from js"' > foo.ml
With the following dune file:
.. code:: scheme
(executable (name foo))
And then request the ``.js`` target:
.. code:: bash
$ dune build ./foo.bc.js
$ node _build/default/foo.bc.js
hello from js
Similar targets are created for libraries, but we recommend sticking to the
executable targets.
.. _dune-jsoo-field:
``js_of_ocaml`` field
=====================
In ``library`` and ``executables`` stanzas, you can specify js_of_ocaml options
using ``(js_of_ocaml (<js_of_ocaml-options>))``.
``<js_of_ocaml-options>`` are all optional:
- ``(flags <flags>)`` to specify flags passed to ``js_of_ocaml``. This field
supports ``(:include ...)`` forms
- ``(javascript_files (<files-list>))`` to specify ``js_of_ocaml`` JavaScript
runtime files.
``<flags>`` is specified in the `Ordered set language`_.
The default value for ``(flags ...)`` depends on the selected build profile. The
build profile ``dev`` (the default) will enable sourcemap and the pretty
JavaScript output.
Separate Compilation
====================
Dune supports two modes of compilation
- Direct compilation of a bytecode program to JavaScript. This mode allows
js_of_ocaml to perform whole program deadcode elimination and whole program
inlining.
- Separate compilation, where compilation units are compiled to JavaScript
separately and then linked together. This mode is useful during development as
it builds more quickly.
The separate compilation mode will be selected when the build profile is
``dev``, which is the default. There is currently no other way to control this
behaviour.
.. _js_of_ocaml: http://ocsigen.org/js_of_ocaml/

View File

@ -136,7 +136,7 @@ Add this field to your ``library`` or ``executable`` stanzas:
.. code:: scheme .. code:: scheme
(preprocess (action (run %{bin:cppo} -V OCAML:%{ocaml_version} %{first-dep}))) (preprocess (action (run %{bin:cppo} -V OCAML:%{ocaml_version} %{input-file})))
Additionally, if you are include a ``config.h`` file, you need to Additionally, if you are include a ``config.h`` file, you need to
declare the dependency to this file via: declare the dependency to this file via:
@ -154,7 +154,7 @@ Write this in your ``dune`` file:
(rule (rule
(targets foo.ml) (targets foo.ml)
(deps foo.cppo.ml <other files that foo.ml includes>) (deps (:first-dep foo.cppo.ml) <other files that foo.ml includes>)
(action (run %{bin:cppo} %{first-dep} -o %{targets}))) (action (run %{bin:cppo} %{first-dep} -o %{targets})))
Defining a library with C stubs Defining a library with C stubs
@ -192,8 +192,8 @@ compilation and link flags. Write this ``dune`` file:
(rule (rule
(targets c_flags.sexp c_library_flags.sexp) (targets c_flags.sexp c_library_flags.sexp)
(deps config/discover.exe) (deps (:discover config/discover.exe))
(action (run %{first-dep} -ocamlc %{OCAMLC}))) (action (run %{discover} -ocamlc %{OCAMLC})))
Then create a ``config`` subdirectory and write this ``dune`` file: Then create a ``config`` subdirectory and write this ``dune`` file:
@ -240,8 +240,8 @@ To generate a file ``foo.ml`` using a program from another directory:
(rule (rule
(targets foo.ml) (targets foo.ml)
(deps ../generator/gen.exe) (deps (:gen ../generator/gen.exe))
(action (run %{first-dep} -o %{targets}))) (action (run %{gen} -o %{targets})))
Defining tests Defining tests
============== ==============
@ -252,8 +252,8 @@ Write this in your ``dune`` file:
(alias (alias
(name runtest) (name runtest)
(deps my-test-program.exe) (deps (:my-prog my-test-program.exe))
(action (run %{first-dep}))) (action (run %{my-prog})))
And run the tests with: And run the tests with:

View File

@ -379,6 +379,13 @@ The build profile can be selected in the ``dune-workspace`` file by write a
Note that the command line option ``--profile`` has precedence over this stanza. Note that the command line option ``--profile`` has precedence over this stanza.
env
~~~
The ``env`` stanza can be used to set the base environment for all contexts in
this workspace. This environment has the lowest precedence of all other ``env``
stanzas. The syntax for this stanza is the same dune's :ref:`dune-env` stanza.
context context
~~~~~~~ ~~~~~~~
@ -407,6 +414,10 @@ context or can be the description of an opam switch, as follows:
context. This has precedence over the command line option context. This has precedence over the command line option
``--profile`` ``--profile``
- ``(env <env>)`` to set the environment for a particular context. This is of
higher precedence than the toplevel ``env`` stanza in the workspace file. This
field the same options as the :ref:`dune-env` stanza.
Both ``(default ...)`` and ``(opam ...)`` accept a ``targets`` field in order to Both ``(default ...)`` and ``(opam ...)`` accept a ``targets`` field in order to
setup cross compilation. See :ref:`advanced-cross-compilation` for more setup cross compilation. See :ref:`advanced-cross-compilation` for more
information. information.
@ -420,29 +431,6 @@ For rare cases where this is not what you want, you can force dune to use a
different build contexts for merlin by adding the field ``(merlin)`` to this different build contexts for merlin by adding the field ``(merlin)`` to this
context. context.
Building JavaScript with js_of_ocaml
====================================
Dune knows how to generate a JavaScript version of an executable
(``<name>.bc.js``) using the js_of_ocaml compiler (the ``js_of_ocaml-compiler``
opam package must be installed).
It supports two modes of compilation:
- Direct compilation of a bytecode program to JavaScript. This mode allows
js_of_ocaml to perform whole program deadcode elimination and whole program
inlining.
- Separate compilation, where compilation units are compiled to JavaScript
separately and then linked together. This mode is useful during development as
it builds more quickly.
The separate compilation mode will be selected when the build profile is
``dev``, which is the default. There is currently no other way to control this
behaviour.
See the section about :ref:`dune-jsoo` for passing custom flags to the
js_of_ocaml compiler
Distributing Projects Distributing Projects
===================== =====================

View File

@ -18,6 +18,13 @@ module Kind = struct
]) ])
end end
module Env_nodes = struct
type t =
{ context: Dune_env.Stanza.t option
; workspace: Dune_env.Stanza.t option
}
end
type t = type t =
{ name : string { name : string
; kind : Kind.t ; kind : Kind.t
@ -26,7 +33,7 @@ type t =
; for_host : t option ; for_host : t option
; implicit : bool ; implicit : bool
; build_dir : Path.t ; build_dir : Path.t
; env_node : Dune_env.Stanza.t option ; env_nodes : Env_nodes.t
; path : Path.t list ; path : Path.t list
; toplevel_path : Path.t option ; toplevel_path : Path.t option
; ocaml_bin : Path.t ; ocaml_bin : Path.t
@ -131,7 +138,7 @@ let ocamlpath_sep =
else else
Bin.path_sep Bin.path_sep
let create ~(kind : Kind.t) ~path ~env ~env_node ~name ~merlin ~targets let create ~(kind : Kind.t) ~path ~env ~env_nodes ~name ~merlin ~targets
~profile () = ~profile () =
let opam_var_cache = Hashtbl.create 128 in let opam_var_cache = Hashtbl.create 128 in
(match kind with (match kind with
@ -334,7 +341,7 @@ let create ~(kind : Kind.t) ~path ~env ~env_node ~name ~merlin ~targets
; kind ; kind
; profile ; profile
; merlin ; merlin
; env_node ; env_nodes
; for_host = host ; for_host = host
; build_dir ; build_dir
; path ; path
@ -410,11 +417,11 @@ let create ~(kind : Kind.t) ~path ~env ~env_node ~name ~merlin ~targets
let opam_config_var t var = opam_config_var ~env:t.env ~cache:t.opam_var_cache var let opam_config_var t var = opam_config_var ~env:t.env ~cache:t.opam_var_cache var
let default ?(merlin=true) ~env_node ~env ~targets () = let default ?(merlin=true) ~env_nodes ~env ~targets () =
create ~kind:Default ~path:Bin.path ~env ~env_node ~name:"default" create ~kind:Default ~path:Bin.path ~env ~env_nodes ~name:"default"
~merlin ~targets () ~merlin ~targets ()
let create_for_opam ?root ~env ~env_node ~targets ~profile ~switch ~name let create_for_opam ?root ~env ~env_nodes ~targets ~profile ~switch ~name
?(merlin=false) () = ?(merlin=false) () =
match Bin.opam with match Bin.opam with
| None -> Utils.program_not_found "opam" | None -> Utils.program_not_found "opam"
@ -452,16 +459,23 @@ let create_for_opam ?root ~env ~env_node ~targets ~profile ~switch ~name
| Some s -> Bin.parse_path s | Some s -> Bin.parse_path s
in in
let env = Env.extend env ~vars in let env = Env.extend env ~vars in
create ~kind:(Opam { root; switch }) ~profile ~targets ~path ~env ~env_node create ~kind:(Opam { root; switch }) ~profile ~targets ~path ~env ~env_nodes
~name ~merlin () ~name ~merlin ()
let create ?merlin ~env def = let create ?merlin ?workspace_env ~env def =
let env_nodes context =
{ Env_nodes.
context
; workspace = workspace_env
}
in
match (def : Workspace.Context.t) with match (def : Workspace.Context.t) with
| Default { targets; profile; env = env_node ; loc = _ } -> | Default { targets; profile; env = env_node ; loc = _ } ->
default ~env ~env_node ~profile ~targets ?merlin () default ~env ~env_nodes:(env_nodes env_node) ~profile ~targets ?merlin ()
| Opam { base = { targets ; profile ; env = env_node ; loc = _ } | Opam { base = { targets ; profile ; env = env_node ; loc = _ }
; name; switch; root; merlin = _ } -> ; name; switch; root; merlin = _ } ->
create_for_opam ?root ~env_node ~env ~profile ~switch ~name ?merlin ~targets () create_for_opam ?root ~env_nodes:(env_nodes env_node) ~env ~profile
~switch ~name ?merlin ~targets ()
let which t s = which ~cache:t.which_cache ~path:t.path s let which t s = which ~cache:t.which_cache ~path:t.path s

View File

@ -30,6 +30,13 @@ module Kind : sig
type t = Default | Opam of Opam.t type t = Default | Opam of Opam.t
end end
module Env_nodes : sig
type t =
{ context: Dune_env.Stanza.t option
; workspace: Dune_env.Stanza.t option
}
end
type t = type t =
{ name : string { name : string
; kind : Kind.t ; kind : Kind.t
@ -51,7 +58,7 @@ type t =
build_dir : Path.t build_dir : Path.t
; (** env node that this context was initialized with *) ; (** env node that this context was initialized with *)
env_node : Dune_env.Stanza.t option env_nodes : Env_nodes.t
; (** [PATH] *) ; (** [PATH] *)
path : Path.t list path : Path.t list
@ -125,6 +132,7 @@ val compare : t -> t -> Ordering.t
val create val create
: ?merlin:bool : ?merlin:bool
-> ?workspace_env:Dune_env.Stanza.t
-> env:Env.t -> env:Env.t
-> Workspace.Context.t -> Workspace.Context.t
-> t list Fiber.t -> t list Fiber.t

View File

@ -73,7 +73,8 @@ let setup ?(log=Log.no_log)
Fiber.parallel_map workspace.contexts ~f:(fun ctx_def -> Fiber.parallel_map workspace.contexts ~f:(fun ctx_def ->
let name = Workspace.Context.name ctx_def in let name = Workspace.Context.name ctx_def in
Context.create ctx_def ~env ~merlin:(workspace.merlin_context = Some name)) Context.create ?workspace_env:workspace.env
ctx_def ~env ~merlin:(workspace.merlin_context = Some name))
>>= fun contexts -> >>= fun contexts ->
let contexts = List.concat contexts in let contexts = List.concat contexts in
List.iter contexts ~f:(fun (ctx : Context.t) -> List.iter contexts ~f:(fun (ctx : Context.t) ->

View File

@ -612,18 +612,24 @@ let create
} }
in in
let context_env_node = lazy ( let context_env_node = lazy (
let config = let make ~inherit_from ~config =
match context.env_node with { Env_node.
| Some s -> s dir = context.build_dir
| None -> { loc = Loc.none; rules = [] } ; scope = Scope.DB.find_by_dir scopes context.build_dir
; ocaml_flags = None
; inherit_from
; config
}
in in
{ Env_node. match context.env_nodes with
dir = context.build_dir | { context = None; workspace = None } ->
; inherit_from = None make ~config:{ loc = Loc.none; rules = [] } ~inherit_from:None
; scope = Scope.DB.find_by_dir scopes context.build_dir | { context = Some config; workspace = None }
; config | { context = None; workspace = Some config } ->
; ocaml_flags = None make ~config ~inherit_from:None
} | { context = Some context ; workspace = Some workspace } ->
make ~config:context
~inherit_from:(Some (lazy (make ~inherit_from:None ~config:workspace)))
) in ) in
List.iter stanzas List.iter stanzas
~f:(fun { Dir_with_jbuild. ctx_dir; scope; stanzas; _ } -> ~f:(fun { Dir_with_jbuild. ctx_dir; scope; stanzas; _ } ->

View File

@ -5,6 +5,11 @@ open Stanza.Of_sexp
for simplicity *) for simplicity *)
let syntax = Stanza.syntax let syntax = Stanza.syntax
let env_field =
field_o "env"
(Syntax.since syntax (1, 1) >>= fun () ->
Dune_env.Stanza.t)
module Context = struct module Context = struct
module Target = struct module Target = struct
type t = type t =
@ -49,7 +54,7 @@ module Context = struct
} }
let t ~profile = let t ~profile =
field_o "env" Dune_env.Stanza.t >>= fun env -> env_field >>= fun env ->
field "targets" (list Target.t) ~default:[Target.Native] field "targets" (list Target.t) ~default:[Target.Native]
>>= fun targets -> >>= fun targets ->
field "profile" string ~default:profile field "profile" string ~default:profile
@ -148,22 +153,25 @@ end
type t = type t =
{ merlin_context : string option { merlin_context : string option
; contexts : Context.t list ; contexts : Context.t list
; env : Dune_env.Stanza.t option
} }
include Versioned_file.Make(struct type t = unit end) include Versioned_file.Make(struct type t = unit end)
let () = Lang.register syntax () let () = Lang.register syntax ()
let t ?x ?profile:cmdline_profile () = let t ?x ?profile:cmdline_profile () =
env_field >>= fun env ->
field "profile" string ~default:Config.default_build_profile field "profile" string ~default:Config.default_build_profile
>>= fun profile -> >>= fun profile ->
let profile = Option.value cmdline_profile ~default:profile in let profile = Option.value cmdline_profile ~default:profile in
multi_field "context" (Context.t ~profile ~x) multi_field "context" (Context.t ~profile ~x)
>>= fun contexts -> >>= fun contexts ->
let defined_names = ref String.Set.empty in let defined_names = ref String.Set.empty in
let { merlin_context; contexts } = let { merlin_context; contexts; env } =
let init = let init =
{ merlin_context = None { merlin_context = None
; contexts = [] ; contexts = []
; env
} }
in in
List.fold_left contexts ~init ~f:(fun t ctx -> List.fold_left contexts ~init ~f:(fun t ctx ->
@ -178,7 +186,7 @@ let t ?x ?profile:cmdline_profile () =
Loc.fail (Context.loc ctx) Loc.fail (Context.loc ctx)
"you can only have one context for merlin" "you can only have one context for merlin"
| Opam { merlin = true; _ }, None -> | Opam { merlin = true; _ }, None ->
{ merlin_context = Some name; contexts = ctx :: t.contexts } { merlin_context = Some name; contexts = ctx :: t.contexts; env = None }
| _ -> | _ ->
{ t with contexts = ctx :: t.contexts }) { t with contexts = ctx :: t.contexts })
in in
@ -200,6 +208,7 @@ let t ?x ?profile:cmdline_profile () =
return return
{ merlin_context { merlin_context
; contexts = List.rev contexts ; contexts = List.rev contexts
; env
} }
let t ?x ?profile () = fields (t ?x ?profile ()) let t ?x ?profile () = fields (t ?x ?profile ())
@ -207,6 +216,7 @@ let t ?x ?profile () = fields (t ?x ?profile ())
let default ?x ?profile () = let default ?x ?profile () =
{ merlin_context = Some "default" { merlin_context = Some "default"
; contexts = [Context.default ?x ?profile ()] ; contexts = [Context.default ?x ?profile ()]
; env = None
} }
let load ?x ?profile p = let load ?x ?profile p =

View File

@ -38,6 +38,7 @@ end
type t = type t =
{ merlin_context : string option { merlin_context : string option
; contexts : Context.t list ; contexts : Context.t list
; env : Dune_env.Stanza.t option
} }
val load : ?x:string -> ?profile:string -> Path.t -> t val load : ?x:string -> ?profile:string -> Path.t -> t

View File

@ -1,6 +1,5 @@
$ cat dune-project $ [ -e dune-project ] || echo File does not exist
cat: dune-project: No such file or directory File does not exist
[1]
$ mkdir src $ mkdir src
$ echo '(alias (name runtest) (action (progn)))' > src/dune $ echo '(alias (name runtest) (action (progn)))' > src/dune
$ dune build $ dune build

View File

@ -53,6 +53,6 @@ Workspaces also allow you to set the env for a context:
Entering directory 'workspace-env' Entering directory 'workspace-env'
( (
(flags (-w -40 -machin)) (flags (-w -40 -machin))
(ocamlc_flags (-g)) (ocamlc_flags (-g -verbose))
(ocamlopt_flags (-g)) (ocamlopt_flags (-g))
) )

View File

@ -1 +1 @@
(lang dune 1.0) (lang dune 1.1)

View File

@ -1,4 +1,8 @@
(lang dune 1.0) (lang dune 1.1)
(env
(default
(ocamlc_flags (:standard -verbose))))
(context (context
(default (default