Skip to content

Commit

Permalink
Add an Exe module to build and link executables (#533)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremiedimino committed Feb 20, 2018
1 parent d8e4145 commit 19f1c6f
Show file tree
Hide file tree
Showing 8 changed files with 365 additions and 122 deletions.
189 changes: 189 additions & 0 deletions src/exe.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
open Import
open Build.O

module SC = Super_context

module Program = struct
type t =
{ name : string
; main_module_name : string
}
end

module Linkage = struct
type t =
{ mode : Mode.t
; ext : string
; flags : string list
}

let byte =
{ mode = Byte
; ext = ".bc"
; flags = []
}

let native =
{ mode = Native
; ext = ".exe"
; flags = []
}

let custom =
{ mode = Byte
; ext = ".exe"
; flags = ["-custom"]
}

let native_or_custom (context : Context.t) =
match context.ocamlopt with
| None -> custom
| Some _ -> native

let make ~mode ~ext ?(flags=[]) () =
{ mode
; ext
; flags
}
end

let link_exe
~dir
~obj_dir
~scope
~requires
~name
~(linkage:Linkage.t)
~top_sorted_modules
?(flags=Ocaml_flags.empty)
?(link_flags=Build.arr (fun _ -> []))
?(js_of_ocaml=Jbuild.Js_of_ocaml.default)
sctx
=
let ctx = SC.context sctx in
let mode = linkage.mode in
let exe = Path.relative dir (name ^ linkage.ext) in
let compiler = Option.value_exn (Context.compiler ctx mode) in
let artifacts ~ext modules =
List.map modules ~f:(Module.obj_file ~obj_dir ~ext)
in
let modules_and_cm_files =
Build.memoize "cm files"
(top_sorted_modules >>^ fun modules ->
(modules,
artifacts modules ~ext:(Cm_kind.ext (Mode.cm_kind mode))))
in
let register_native_objs_deps build =
match mode with
| Byte -> build
| Native ->
build >>>
Build.dyn_paths (Build.arr (fun (modules, _) ->
artifacts modules ~ext:ctx.ext_obj))
in
SC.add_rule sctx
(Build.fanout4
requires
(register_native_objs_deps modules_and_cm_files >>^ snd)
(Ocaml_flags.get flags mode)
link_flags
>>>
Build.dyn_paths (Build.arr (fun (libs, _, _, _) ->
Lib.L.archive_files libs ~mode ~ext_lib:ctx.ext_lib))
>>>
Build.run ~context:ctx
(Ok compiler)
[ Dyn (fun (_, _, flags,_) -> As flags)
; A "-o"; Target exe
; As linkage.flags
; Dyn (fun (_, _, _, link_flags) -> As link_flags)
; Dyn (fun (libs, _, _, _) ->
Lib.L.link_flags libs ~mode ~stdlib_dir:ctx.stdlib_dir)
; Dyn (fun (_, cm_files, _, _) -> Deps cm_files)
]);
if mode = Mode.Byte then
let rules = Js_of_ocaml_rules.build_exe sctx ~dir ~js_of_ocaml ~src:exe in
let libs_and_cm_and_flags =
(requires &&& (modules_and_cm_files >>^ snd))
&&&
SC.expand_and_eval_set sctx ~scope ~dir js_of_ocaml.flags
~standard:(Js_of_ocaml_rules.standard ())
in
SC.add_rules sctx (List.map rules ~f:(fun r -> libs_and_cm_and_flags >>> r))

let build_and_link_many
~loc ~dir ~programs ~modules
~scope
~linkages
?(libraries=[])
?(flags=Ocaml_flags.empty)
?link_flags
?(preprocess=Jbuild.Preprocess_map.default)
?(preprocessor_deps=Build.arr (fun () -> []))
?(lint=Jbuild.Preprocess_map.default)
?(js_of_ocaml=Jbuild.Js_of_ocaml.default)
?(has_dot_merlin=false)
sctx
=
let item = (List.hd programs).Program.name in
(* Use "eobjs" rather than "objs" to avoid a potential conflict with a library of the
same name *)
let obj_dir = Path.relative dir ("." ^ item ^ ".eobjs") in
let dep_kind = Build.Required in
let modules =
String_map.map modules ~f:(Module.set_obj_name ~wrapper:None)
in

let modules =
SC.PP.pp_and_lint_modules sctx ~dir ~dep_kind ~modules ~scope
~preprocess
~preprocessor_deps
~lint
~lib_name:None
in

let dep_graphs =
Ocamldep.rules sctx ~dir ~modules ~alias_module:None
~lib_interface_module:None
in

let requires, real_requires =
SC.Libs.requires sctx
~loc
~dir
~scope
~dep_kind
~libraries
~has_dot_merlin
~preprocess
in

(* CR-someday jdimino: this should probably say [~dynlink:false] *)
Module_compilation.build_modules sctx
~js_of_ocaml
~dynlink:true ~flags ~scope:scope ~dir ~obj_dir ~dep_graphs ~modules
~requires ~alias_module:None;

List.iter programs ~f:(fun { Program.name; main_module_name } ->
let top_sorted_modules =
let main = Option.value_exn (String_map.find main_module_name modules) in
Ocamldep.Dep_graph.top_closed_implementations dep_graphs.impl
[main]
in
List.iter linkages ~f:(fun linkage ->
link_exe sctx
~dir
~obj_dir
~scope
~requires
~name
~linkage
~top_sorted_modules
~js_of_ocaml
~flags
?link_flags));

(obj_dir, real_requires)

let build_and_link ~loc ~dir ~program =
build_and_link_many ~loc ~dir ~programs:[program]
91 changes: 91 additions & 0 deletions src/exe.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
(** Compilation and linking of executables *)

open Import

module Program : sig
type t =
{ name : string
; main_module_name : string
}
end

module Linkage : sig
type t

(** Byte compilation, exetension [.bc] *)
val byte : t

(** Native compilation, extension [.exe] *)
val native : t

(** Byte compilation, link with [-custom], extension [.exe] *)
val custom : t

(** [native] if supported, [custom] if not *)
val native_or_custom : Context.t -> t

val make
: mode:Mode.t
-> ext:string
-> ?flags:string list
-> unit
-> t
end

(** {1 High-level functions} *)

(** Build and link one or more executables. Return the object
directory and the resolved list of library dependencies. *)

val build_and_link
: loc:Loc.t
-> dir:Path.t
-> program:Program.t
-> modules:Module.t String_map.t
-> scope:Scope.t
-> linkages:Linkage.t list
-> ?libraries:Jbuild.Lib_deps.t
-> ?flags:Ocaml_flags.t
-> ?link_flags:(unit, string list) Build.t
-> ?preprocess:Jbuild.Preprocess_map.t
-> ?preprocessor_deps:(unit, Path.t list) Build.t
-> ?lint:Jbuild.Preprocess_map.t
-> ?js_of_ocaml:Jbuild.Js_of_ocaml.t
-> ?has_dot_merlin:bool
-> Super_context.t
-> Path.t * (unit, Lib.t list) Build.t

val build_and_link_many
: loc:Loc.t
-> dir:Path.t
-> programs:Program.t list
-> modules:Module.t String_map.t
-> scope:Scope.t
-> linkages:Linkage.t list
-> ?libraries:Jbuild.Lib_deps.t
-> ?flags:Ocaml_flags.t
-> ?link_flags:(unit, string list) Build.t
-> ?preprocess:Jbuild.Preprocess_map.t
-> ?preprocessor_deps:(unit, Path.t list) Build.t
-> ?lint:Jbuild.Preprocess_map.t
-> ?js_of_ocaml:Jbuild.Js_of_ocaml.t
-> ?has_dot_merlin:bool
-> Super_context.t
-> Path.t * (unit, Lib.t list) Build.t

(** {1 Low-level functions} *)

(** Link a single executable *)
val link_exe
: dir:Path.t
-> obj_dir:Path.t
-> scope:Scope.t
-> requires:(unit, Lib.t list) Build.t
-> name:string
-> linkage:Linkage.t
-> top_sorted_modules:(unit, Module.t list) Build.t
-> ?flags:Ocaml_flags.t
-> ?link_flags:(unit, string list) Build.t
-> ?js_of_ocaml:Jbuild.Js_of_ocaml.t
-> Super_context.t
-> unit

0 comments on commit 19f1c6f

Please sign in to comment.