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:
parent
57f4203102
commit
3ae53de431
|
@ -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)))))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
24
src/sexp.ml
24
src/sexp.ml
|
@ -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; _ } ->
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue