Add an "executable" stanza and make it easier to install executables

(executable ...) is easier to use where there is a single executable.

Also add a (package ...) and (public_name <name>)/(public_names
(<names)) to executable/executables stanzas, to make it easier to
install executables.

Closes #33
This commit is contained in:
Jeremie Dimino 2017-03-22 15:09:26 +00:00
parent 57f4203102
commit 3ae53de431
5 changed files with 164 additions and 68 deletions

View File

@ -1,8 +1,5 @@
(executables (executable
((names (main)) ((name main)
(public_name jbuilder)
(libraries (unix jbuilder jbuilder_cmdliner)) (libraries (unix jbuilder jbuilder_cmdliner))
(preprocess no_preprocessing))) (preprocess no_preprocessing)))
(install
((section bin)
(files ((main.exe as jbuilder)))))

View File

@ -389,25 +389,25 @@ support for tools such as =pkg-config=, however it integrates easily
with [[https://github.com/janestreet/configurator][configurator]] by using =(c_flags (:include ...))= and with [[https://github.com/janestreet/configurator][configurator]] by using =(c_flags (:include ...))= and
=(c_library_flags (:include ...))=. =(c_library_flags (:include ...))=.
**** executables **** executable
The =executables= stanza must be used to describe sets of The =executable= stanza must be used to describe an executable. The
executables. The format of executables stanzas is as follows: format of executables stanzas is as follows:
#+begin_src scheme #+begin_src scheme
(executables (executable
((names (<entry point names>)) ((name <name>)
<optional-fields> <optional-fields>
)) ))
#+end_src #+end_src
=<entry point names>= is a list of module names that contain the main =<entry point name>= is a module name that contain the main entry
entry point of each executables. There can be additional modules in point of the executable. There can be additional modules in the
the current directory, you only need to list the entry point in current directory, you only need to specify the entry point. Given an
=(names ...)=. For every =<name>=, Jbuilder will know how to build =executable= stanza with =(name <name>)=, Jbuilder will know how to
=<name>.exe= and =<name>.bc=. =<name>.exe= is a native code executable build =<name>.exe= and =<name>.bc=. =<name>.exe= is a native code
and =<name>.bc= is a bytecode executable which requires =ocamlrun= to executable and =<name>.bc= is a bytecode executable which requires
run. =ocamlrun= to run.
Note that in case native compilation is not available, =<name>.exe= Note that in case native compilation is not available, =<name>.exe=
will in fact be a custom byte-code executable. Custom in the sense of will in fact be a custom byte-code executable. Custom in the sense of
@ -417,14 +417,26 @@ can always rely on =<name>.exe= being available.
=<optional-fields>= are: =<optional-fields>= are:
- =(public_name <public-name>)= specify that the executable should be
installed under that name. It is the same as adding the following
stanza to your =jbuild= file:
#+begin_src scheme
(install
((section bin)
(files ((<name>.exe as <public-name>)))))
#+end_src
- =(package <package>)= if there is a =(public_name ...)= field, this
specifies the package the executables are part of
- =(libraries (<library-dependencies>))= specifies the library - =(libraries (<library-dependencies>))= specifies the library
dependencies. See the [[Library dependencies][section about library dependencies]] for more dependencies. See the [[Library dependencies][section about library dependencies]] for more
details details
- =(modules <modules>)= specifies which modules in the current - =(modules <modules>)= specifies which modules in the current
directory Jbuilder should consider when building directory Jbuilder should consider when building this
executables. Modules not listed here will be ignored and cannot be executable. Modules not listed here will be ignored and cannot be
used inside executables described by the current stanza. It is used inside the executable described by the current stanza. It is
interpreted in the same way as the =(modules ...)= field of interpreted in the same way as the =(modules ...)= field of
[[library][libraries]] [[library][libraries]]
@ -437,6 +449,24 @@ can always rely on =<name>.exe= being available.
- =flags=, =ocamlc_flags= and =ocamlopt_flags=. See the - =flags=, =ocamlc_flags= and =ocamlopt_flags=. See the
[[OCaml flags][section about specifying OCaml flags]] [[OCaml flags][section about specifying OCaml flags]]
**** executables
The =executables= stanza is the same as the =executable= stanza,
except that it is used to describe several executables sharing the
same configuration.
It shares the same fields as the =executable= stanza, except that
instead of =(name ...)= and =(public_name ...)= you must use:
- =(names (<names>))= where =<names>= is a list of entry point
names. As for =executable= you only need to specify the modules
containing the entry point of each executable
- =(public_names (<names>))= describes under what name each executable
should be installed. The List of names must be of the same length as
the list in the =(names ...)= field. Moreover you can use =-= for
executables that shouldn't be installed
**** rule **** rule
The =rule= stanza is used to create custom user rules. It tells The =rule= stanza is used to create custom user rules. It tells

View File

@ -517,6 +517,41 @@ module Library = struct
List.map t.virtual_deps ~f:(fun s -> Lib_dep.Direct s) @ t.buildable.libraries List.map t.virtual_deps ~f:(fun s -> Lib_dep.Direct s) @ t.buildable.libraries
end end
module Install_conf = struct
type file =
{ src : string
; dst : string option
}
let file sexp =
match sexp with
| Atom (_, src) -> { src; dst = None }
| List (_, [Atom (_, src); Atom (_, "as"); Atom (_, dst)]) ->
{ src; dst = Some dst }
| _ ->
of_sexp_error sexp
"invalid format, <name> or (<name> as <install-as>) expected"
type t =
{ section : Install.Section.t
; files : file list
; package : string option
}
let v1 =
record
(field "section" Install.Section.t >>= fun section ->
field "files" (list file) >>= fun files ->
field_o "package" string >>= fun package ->
return
{ section
; files
; package
})
let vjs = v1
end
module Executables = struct module Executables = struct
type t = type t =
{ names : string list { names : string list
@ -525,18 +560,55 @@ module Executables = struct
; buildable : Buildable.t ; buildable : Buildable.t
} }
let v1 = let common_v1 names public_names =
Buildable.v1 >>= fun buildable ->
field "link_executables" bool ~default:true >>= fun link_executables ->
field "link_flags" (list string) ~default:[] >>= fun link_flags ->
field_o "package" string >>= fun package ->
let t =
{ names
; link_executables
; link_flags
; buildable
}
in
let to_install =
List.map2 names public_names
~f:(fun name pub ->
match pub with
| None -> None
| Some pub -> Some ({ Install_conf. src = name ^ ".exe"; dst = Some pub }))
|> List.filter_map ~f:(fun x -> x)
in
match to_install with
| [] -> return (t, None)
| files ->
return (t, Some { Install_conf. section = Bin; files; package })
let public_name sexp =
match string sexp with
| "-" -> None
| s -> Some s
let v1_multi =
record record
(Buildable.v1 >>= fun buildable -> (field "names" (list string) >>= fun names ->
field "names" (list string) >>= fun names -> map_validate (field_o "public_names" (list public_name)) ~f:(function
field "link_executables" bool ~default:true >>= fun link_executables -> | None -> Ok (List.map names ~f:(fun _ -> None))
field "link_flags" (list string) ~default:[] >>= fun link_flags -> | Some public_names ->
return if List.length public_names = List.length names then
{ names Ok public_names
; link_executables else
; link_flags Error "The list of public names must be of the same \
; buildable length as the list of names")
}) >>= fun public_names ->
common_v1 names public_names)
let v1_single =
record
(field "name" string >>= fun name ->
field_o "public_name" string >>= fun public_name ->
common_v1 [name] [public_name])
let vjs = let vjs =
record record
@ -631,41 +703,6 @@ module Provides = struct
let vjs = v1 let vjs = v1
end end
module Install_conf = struct
type file =
{ src : string
; dst : string option
}
let file sexp =
match sexp with
| Atom (_, src) -> { src; dst = None }
| List (_, [Atom (_, src); Atom (_, "as"); Atom (_, dst)]) ->
{ src; dst = Some dst }
| _ ->
of_sexp_error sexp
"invalid format, <name> or (<name> as <install-as>) expected"
type t =
{ section : Install.Section.t
; files : file list
; package : string option
}
let v1 =
record
(field "section" Install.Section.t >>= fun section ->
field "files" (list file) >>= fun files ->
field_o "package" string >>= fun package ->
return
{ section
; files
; package
})
let vjs = v1
end
module Alias_conf = struct module Alias_conf = struct
type t = type t =
{ name : string { name : string
@ -702,10 +739,16 @@ module Stanza = struct
let rules l = List.map l ~f:(fun x -> Rule x) let rules l = List.map l ~f:(fun x -> Rule x)
let execs (exe, install) =
match install with
| None -> [Executables exe]
| Some i -> [Executables exe; Install i]
let v1 = let v1 =
sum sum
[ cstr "library" (Library.v1 @> nil) (fun x -> [Library x]) [ cstr "library" (Library.v1 @> nil) (fun x -> [Library x])
; cstr "executables" (Executables.v1 @> nil) (fun x -> [Executables x]) ; cstr "executable" (Executables.v1_single @> nil) execs
; cstr "executables" (Executables.v1_multi @> nil) execs
; cstr "rule" (Rule.v1 @> nil) (fun x -> [Rule x]) ; cstr "rule" (Rule.v1 @> nil) (fun x -> [Rule x])
; cstr "ocamllex" (list string @> nil) (fun x -> rules (Rule.ocamllex_v1 x)) ; cstr "ocamllex" (list string @> nil) (fun x -> rules (Rule.ocamllex_v1 x))
; cstr "ocamlyacc" (list string @> nil) (fun x -> rules (Rule.ocamlyacc_v1 x)) ; cstr "ocamlyacc" (list string @> nil) (fun x -> rules (Rule.ocamlyacc_v1 x))

View File

@ -230,6 +230,30 @@ module Of_sexp = struct
; known = List.rev_append names state.known ; known = List.rev_append names state.known
}) })
let map_validate parse ~f state =
let x, state' = parse state in
match f x with
| Ok x -> x, state'
| Error msg ->
let parsed =
Name_map.merge state.unparsed state'.unparsed ~f:(fun _key before after ->
match before, after with
| Some _, None -> before
| _ -> None)
in
let loc =
match
Name_map.values parsed
|> List.map ~f:(fun f -> Ast.loc f.entry)
|> List.sort ~cmp:(fun a b -> compare a.Loc.start.pos_cnum b.start.pos_cnum)
with
| [] -> state.loc
| first :: l ->
let last = List.fold_left l ~init:first ~f:(fun _ x -> x) in
{ first with stop = last.stop }
in
Loc.fail loc "%s" msg
let field name ?default value_of_sexp state = let field name ?default value_of_sexp state =
match Name_map.find name state.unparsed with match Name_map.find name state.unparsed with
| Some { value = Some value; _ } -> | Some { value = Some value; _ } ->

View File

@ -66,6 +66,8 @@ module Of_sexp : sig
val field_o : string -> 'a t -> 'a option record_parser val field_o : string -> 'a t -> 'a option record_parser
val field_b : string -> bool record_parser val field_b : string -> bool record_parser
val map_validate : 'a record_parser -> f:('a -> ('b, string) result) -> 'b record_parser
val ignore_fields : string list -> unit record_parser val ignore_fields : string list -> unit record_parser
val record : 'a record_parser -> 'a t val record : 'a record_parser -> 'a t