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
|
(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)))))
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
24
src/sexp.ml
24
src/sexp.ml
|
@ -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; _ } ->
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue