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
((names (main))
(executable
((name main)
(public_name jbuilder)
(libraries (unix jbuilder jbuilder_cmdliner))
(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
=(c_library_flags (:include ...))=.
**** executables
**** executable
The =executables= stanza must be used to describe sets of
executables. The format of executables stanzas is as follows:
The =executable= stanza must be used to describe an executable. The
format of executables stanzas is as follows:
#+begin_src scheme
(executables
((names (<entry point names>))
(executable
((name <name>)
<optional-fields>
))
#+end_src
=<entry point names>= is a list of module names that contain the main
entry point of each executables. There can be additional modules in
the current directory, you only need to list the entry point in
=(names ...)=. For every =<name>=, Jbuilder will know how to build
=<name>.exe= and =<name>.bc=. =<name>.exe= is a native code executable
and =<name>.bc= is a bytecode executable which requires =ocamlrun= to
run.
=<entry point name>= is a module name that contain the main entry
point of the executable. There can be additional modules in the
current directory, you only need to specify the entry point. Given an
=executable= stanza with =(name <name>)=, Jbuilder will know how to
build =<name>.exe= and =<name>.bc=. =<name>.exe= is a native code
executable and =<name>.bc= is a bytecode executable which requires
=ocamlrun= to run.
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
@ -417,14 +417,26 @@ can always rely on =<name>.exe= being available.
=<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
dependencies. See the [[Library dependencies][section about library dependencies]] for more
details
- =(modules <modules>)= specifies which modules in the current
directory Jbuilder should consider when building
executables. Modules not listed here will be ignored and cannot be
used inside executables described by the current stanza. It is
directory Jbuilder should consider when building this
executable. Modules not listed here will be ignored and cannot be
used inside the executable described by the current stanza. It is
interpreted in the same way as the =(modules ...)= field of
[[library][libraries]]
@ -437,6 +449,24 @@ can always rely on =<name>.exe= being available.
- =flags=, =ocamlc_flags= and =ocamlopt_flags=. See the
[[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
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
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
type t =
{ names : string list
@ -525,18 +560,55 @@ module Executables = struct
; 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
(Buildable.v1 >>= fun buildable ->
field "names" (list string) >>= fun names ->
field "link_executables" bool ~default:true >>= fun link_executables ->
field "link_flags" (list string) ~default:[] >>= fun link_flags ->
return
{ names
; link_executables
; link_flags
; buildable
})
(field "names" (list string) >>= fun names ->
map_validate (field_o "public_names" (list public_name)) ~f:(function
| None -> Ok (List.map names ~f:(fun _ -> None))
| Some public_names ->
if List.length public_names = List.length names then
Ok public_names
else
Error "The list of public names must be of the same \
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 =
record
@ -631,41 +703,6 @@ module Provides = struct
let vjs = v1
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
type t =
{ name : string
@ -702,10 +739,16 @@ module Stanza = struct
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 =
sum
[ 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 "ocamllex" (list string @> nil) (fun x -> rules (Rule.ocamllex_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
})
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 =
match Name_map.find name state.unparsed with
| 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_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 record : 'a record_parser -> 'a t