Merge pull request #1129 from rgrinberg/split-gen-rules
Move library rules to lib_rules module
This commit is contained in:
commit
f38edc22f0
424
src/gen_rules.ml
424
src/gen_rules.ml
|
@ -12,425 +12,12 @@ module Gen(P : Install_rules.Params) = struct
|
|||
module Alias = Build_system.Alias
|
||||
module CC = Compilation_context
|
||||
module SC = Super_context
|
||||
module Odoc = Odoc.Gen(P)
|
||||
module Lib_rules = Lib_rules.Gen(P)
|
||||
|
||||
let sctx = P.sctx
|
||||
let ctx = SC.context sctx
|
||||
|
||||
let opaque =
|
||||
ctx.profile = "dev" && Ocaml_version.supports_opaque_for_mli ctx.version
|
||||
|
||||
(* +-----------------------------------------------------------------+
|
||||
| Library stuff |
|
||||
+-----------------------------------------------------------------+ *)
|
||||
|
||||
let msvc_hack_cclibs cclibs =
|
||||
let f lib =
|
||||
if String.is_prefix lib ~prefix:"-l" then
|
||||
String.sub lib ~pos:2 ~len:(String.length lib - 2) ^ ".lib"
|
||||
else
|
||||
lib
|
||||
in
|
||||
let cclibs = List.map cclibs ~f in
|
||||
let f lib =
|
||||
if String.is_prefix lib ~prefix:"-l" then
|
||||
String.sub lib ~pos:2 ~len:(String.length lib - 2)
|
||||
else
|
||||
lib
|
||||
in
|
||||
List.map cclibs ~f
|
||||
|
||||
let build_lib (lib : Library.t) ~scope ~flags ~dir ~obj_dir ~mode
|
||||
~top_sorted_modules ~modules =
|
||||
Option.iter (Context.compiler ctx mode) ~f:(fun compiler ->
|
||||
let target = Library.archive lib ~dir ~ext:(Mode.compiled_lib_ext mode) in
|
||||
let stubs_flags =
|
||||
if not (Library.has_stubs lib) then
|
||||
[]
|
||||
else
|
||||
let stubs_name = lib.name ^ "_stubs" in
|
||||
match mode with
|
||||
| Byte -> ["-dllib"; "-l" ^ stubs_name; "-cclib"; "-l" ^ stubs_name]
|
||||
| Native -> ["-cclib"; "-l" ^ stubs_name]
|
||||
in
|
||||
let map_cclibs =
|
||||
(* https://github.com/ocaml/dune/issues/119 *)
|
||||
if ctx.ccomp_type = "msvc" then
|
||||
msvc_hack_cclibs
|
||||
else
|
||||
fun x -> x
|
||||
in
|
||||
let artifacts ~ext modules =
|
||||
List.map modules ~f:(Module.obj_file ~obj_dir ~ext)
|
||||
in
|
||||
let obj_deps =
|
||||
Build.paths (artifacts modules ~ext:(Cm_kind.ext (Mode.cm_kind mode)))
|
||||
in
|
||||
let obj_deps =
|
||||
match mode with
|
||||
| Byte -> obj_deps
|
||||
| Native ->
|
||||
obj_deps >>>
|
||||
Build.paths (artifacts modules ~ext:ctx.ext_obj)
|
||||
in
|
||||
SC.add_rule sctx
|
||||
(obj_deps
|
||||
>>>
|
||||
Build.fanout4
|
||||
(top_sorted_modules >>^artifacts ~ext:(Cm_kind.ext (Mode.cm_kind mode)))
|
||||
(SC.expand_and_eval_set sctx ~scope ~dir lib.c_library_flags
|
||||
~standard:(Build.return []))
|
||||
(Ocaml_flags.get flags mode)
|
||||
(SC.expand_and_eval_set sctx ~scope ~dir lib.library_flags
|
||||
~standard:(Build.return []))
|
||||
>>>
|
||||
Build.run ~context:ctx (Ok compiler)
|
||||
[ Dyn (fun (_, _, flags, _) -> As flags)
|
||||
; A "-a"; A "-o"; Target target
|
||||
; As stubs_flags
|
||||
; Dyn (fun (_, cclibs, _, _) -> Arg_spec.quote_args "-cclib" (map_cclibs cclibs))
|
||||
; Dyn (fun (_, _, _, library_flags) -> As library_flags)
|
||||
; As (match lib.kind with
|
||||
| Normal -> []
|
||||
| Ppx_deriver | Ppx_rewriter -> ["-linkall"])
|
||||
; Dyn (fun (cm_files, _, _, _) -> Deps cm_files)
|
||||
; Hidden_targets
|
||||
(match mode with
|
||||
| Byte -> []
|
||||
| Native -> [Library.archive lib ~dir ~ext:ctx.ext_lib])
|
||||
]))
|
||||
|
||||
let build_c_file (lib : Library.t) ~scope ~dir ~includes (src, dst) =
|
||||
SC.add_rule sctx
|
||||
(SC.expand_and_eval_set sctx ~scope ~dir lib.c_flags
|
||||
~standard:(Build.return (Context.cc_g ctx))
|
||||
>>>
|
||||
Build.run ~context:ctx
|
||||
(* We have to execute the rule in the library directory as
|
||||
the .o is produced in the current directory *)
|
||||
~dir:(Path.parent_exn src)
|
||||
(Ok ctx.ocamlc)
|
||||
[ As (Utils.g ())
|
||||
; includes
|
||||
; Dyn (fun c_flags -> Arg_spec.quote_args "-ccopt" c_flags)
|
||||
; A "-o"; Target dst
|
||||
; Dep src
|
||||
]);
|
||||
dst
|
||||
|
||||
let build_cxx_file (lib : Library.t) ~scope ~dir ~includes (src, dst) =
|
||||
let open Arg_spec in
|
||||
let output_param =
|
||||
if ctx.ccomp_type = "msvc" then
|
||||
[Concat ("", [A "/Fo"; Target dst])]
|
||||
else
|
||||
[A "-o"; Target dst]
|
||||
in
|
||||
SC.add_rule sctx
|
||||
(SC.expand_and_eval_set sctx ~scope ~dir lib.cxx_flags
|
||||
~standard:(Build.return (Context.cc_g ctx))
|
||||
>>>
|
||||
Build.run ~context:ctx
|
||||
(* We have to execute the rule in the library directory as
|
||||
the .o is produced in the current directory *)
|
||||
~dir:(Path.parent_exn src)
|
||||
(SC.resolve_program ~loc:None sctx ctx.c_compiler)
|
||||
([ S [A "-I"; Path ctx.stdlib_dir]
|
||||
; As (SC.cxx_flags sctx)
|
||||
; includes
|
||||
; Dyn (fun cxx_flags -> As cxx_flags)
|
||||
] @ output_param @
|
||||
[ A "-c"; Dep src
|
||||
]));
|
||||
dst
|
||||
|
||||
(* If the compiler reads the cmi for module alias even with
|
||||
[-w -49 -no-alias-deps], we must sandbox the build of the
|
||||
alias module since the modules it references are built after. *)
|
||||
let alias_module_build_sandbox =
|
||||
Ocaml_version.always_reads_alias_cmi ctx.version
|
||||
|
||||
let library_rules (lib : Library.t) ~dir_contents ~dir ~scope
|
||||
~compile_info ~dir_kind =
|
||||
let obj_dir = Utils.library_object_directory ~dir lib.name in
|
||||
let requires = Lib.Compile.requires compile_info in
|
||||
let dep_kind =
|
||||
if lib.optional then Lib_deps_info.Kind.Optional else Required
|
||||
in
|
||||
let flags = SC.ocaml_flags sctx ~scope ~dir lib.buildable in
|
||||
let { Dir_contents.Library_modules.
|
||||
modules; main_module_name; alias_module } =
|
||||
Dir_contents.modules_of_library dir_contents ~name:(Library.best_name lib)
|
||||
in
|
||||
let source_modules = modules in
|
||||
(* Preprocess before adding the alias module as it doesn't need
|
||||
preprocessing *)
|
||||
let pp =
|
||||
Preprocessing.make sctx ~dir ~dep_kind ~scope
|
||||
~preprocess:lib.buildable.preprocess
|
||||
~preprocessor_deps:
|
||||
(SC.Deps.interpret sctx ~scope ~dir
|
||||
lib.buildable.preprocessor_deps)
|
||||
~lint:lib.buildable.lint
|
||||
~lib_name:(Some lib.name)
|
||||
~dir_kind
|
||||
in
|
||||
let modules = Preprocessing.pp_modules pp modules in
|
||||
|
||||
let modules =
|
||||
match alias_module with
|
||||
| None -> modules
|
||||
| Some m -> Module.Name.Map.add modules m.name m
|
||||
in
|
||||
|
||||
let lib_interface_module =
|
||||
if lib.wrapped then
|
||||
Module.Name.Map.find modules main_module_name
|
||||
else
|
||||
None
|
||||
in
|
||||
let cctx =
|
||||
Compilation_context.create ()
|
||||
~super_context:sctx
|
||||
~scope
|
||||
~dir
|
||||
~dir_kind
|
||||
~obj_dir
|
||||
~modules
|
||||
?alias_module
|
||||
?lib_interface_module
|
||||
~flags
|
||||
~requires
|
||||
~preprocessing:pp
|
||||
~no_keep_locs:lib.no_keep_locs
|
||||
~opaque
|
||||
in
|
||||
|
||||
let dep_graphs = Ocamldep.rules cctx in
|
||||
|
||||
Option.iter alias_module ~f:(fun m ->
|
||||
let file =
|
||||
match m.impl with
|
||||
| Some f -> f
|
||||
| None -> Option.value_exn m.intf
|
||||
in
|
||||
SC.add_rule sctx
|
||||
(Build.return
|
||||
(Module.Name.Map.values (Module.Name.Map.remove modules m.name)
|
||||
|> List.map ~f:(fun (m : Module.t) ->
|
||||
sprintf "(** @canonical %s.%s *)\n\
|
||||
module %s = %s\n"
|
||||
(Module.Name.to_string main_module_name)
|
||||
(Module.Name.to_string m.name)
|
||||
(Module.Name.to_string m.name)
|
||||
(Module.Name.to_string (Module.real_unit_name m))
|
||||
)
|
||||
|> String.concat ~sep:"\n")
|
||||
>>> Build.write_file_dyn file.path));
|
||||
|
||||
|
||||
let dynlink = lib.dynlink in
|
||||
let js_of_ocaml = lib.buildable.js_of_ocaml in
|
||||
Module_compilation.build_modules cctx ~js_of_ocaml ~dynlink ~dep_graphs;
|
||||
Option.iter alias_module ~f:(fun m ->
|
||||
let cctx = Compilation_context.for_alias_module cctx in
|
||||
Module_compilation.build_module cctx m
|
||||
~js_of_ocaml
|
||||
~dynlink
|
||||
~sandbox:alias_module_build_sandbox
|
||||
~dep_graphs:(Ocamldep.Dep_graphs.dummy m));
|
||||
|
||||
if Library.has_stubs lib then begin
|
||||
let all_dirs = Dir_contents.dirs dir_contents in
|
||||
let h_files =
|
||||
List.fold_left all_dirs ~init:[] ~f:(fun acc dc ->
|
||||
String.Set.fold (Dir_contents.text_files dc) ~init:acc
|
||||
~f:(fun fn acc ->
|
||||
if String.is_suffix fn ~suffix:".h" then
|
||||
Path.relative (Dir_contents.dir dc) fn :: acc
|
||||
else
|
||||
acc))
|
||||
in
|
||||
let all_dirs = Path.Set.of_list (List.map all_dirs ~f:Dir_contents.dir) in
|
||||
let resolve_name ~ext (loc, fn) =
|
||||
let p = Path.relative dir (fn ^ ext) in
|
||||
if not (match Path.parent p with
|
||||
| None -> false
|
||||
| Some p -> Path.Set.mem all_dirs p) then
|
||||
Loc.fail loc
|
||||
"File %a is not part of the current directory group. \
|
||||
This is not allowed."
|
||||
Path.pp (Path.drop_optional_build_context p)
|
||||
;
|
||||
(p, Path.relative dir (fn ^ ctx.ext_obj))
|
||||
in
|
||||
let o_files =
|
||||
let includes =
|
||||
Arg_spec.S
|
||||
[ Hidden_deps h_files
|
||||
; Arg_spec.of_result_map requires ~f:(fun libs ->
|
||||
S [ Lib.L.c_include_flags libs ~stdlib_dir:ctx.stdlib_dir
|
||||
; Hidden_deps (SC.Libs.file_deps sctx libs ~ext:".h")
|
||||
])
|
||||
]
|
||||
in
|
||||
List.map lib.c_names ~f:(fun name ->
|
||||
build_c_file lib ~scope ~dir ~includes (resolve_name name ~ext:".c")
|
||||
) @ List.map lib.cxx_names ~f:(fun name ->
|
||||
build_cxx_file lib ~scope ~dir ~includes (resolve_name name ~ext:".cpp")
|
||||
)
|
||||
in
|
||||
match lib.self_build_stubs_archive with
|
||||
| Some _ -> ()
|
||||
| None ->
|
||||
let ocamlmklib ~sandbox ~custom ~targets =
|
||||
SC.add_rule sctx ~sandbox
|
||||
(SC.expand_and_eval_set sctx ~scope ~dir
|
||||
lib.c_library_flags ~standard:(Build.return [])
|
||||
>>>
|
||||
Build.run ~context:ctx
|
||||
(Ok ctx.ocamlmklib)
|
||||
[ As (Utils.g ())
|
||||
; if custom then A "-custom" else As []
|
||||
; A "-o"
|
||||
; Path (Path.relative dir (sprintf "%s_stubs" lib.name))
|
||||
; Deps o_files
|
||||
; Dyn (fun cclibs ->
|
||||
(* https://github.com/ocaml/dune/issues/119 *)
|
||||
if ctx.ccomp_type = "msvc" then
|
||||
let cclibs = msvc_hack_cclibs cclibs in
|
||||
Arg_spec.quote_args "-ldopt" cclibs
|
||||
else
|
||||
As cclibs
|
||||
)
|
||||
; Hidden_targets targets
|
||||
])
|
||||
in
|
||||
let static = Library.stubs_archive lib ~dir ~ext_lib:ctx.ext_lib in
|
||||
let dynamic = Library.dll lib ~dir ~ext_dll:ctx.ext_dll in
|
||||
let modes =
|
||||
Mode_conf.Set.eval lib.modes
|
||||
~has_native:(Option.is_some ctx.ocamlopt)
|
||||
in
|
||||
if modes.native &&
|
||||
modes.byte &&
|
||||
lib.dynlink
|
||||
then begin
|
||||
(* If we build for both modes and support dynlink, use a
|
||||
single invocation to build both the static and dynamic
|
||||
libraries *)
|
||||
ocamlmklib ~sandbox:false ~custom:false ~targets:[static; dynamic]
|
||||
end else begin
|
||||
ocamlmklib ~sandbox:false ~custom:true ~targets:[static];
|
||||
(* We can't tell ocamlmklib to build only the dll, so we
|
||||
sandbox the action to avoid overriding the static archive *)
|
||||
ocamlmklib ~sandbox:true ~custom:false ~targets:[dynamic]
|
||||
end
|
||||
end;
|
||||
|
||||
List.iter Cm_kind.all ~f:(fun cm_kind ->
|
||||
let files =
|
||||
Module.Name.Map.fold modules ~init:Path.Set.empty ~f:(fun m acc ->
|
||||
match Module.cm_file m ~obj_dir cm_kind with
|
||||
| None -> acc
|
||||
| Some fn -> Path.Set.add acc fn)
|
||||
in
|
||||
SC.Libs.setup_file_deps_alias sctx ~dir lib ~ext:(Cm_kind.ext cm_kind)
|
||||
files);
|
||||
SC.Libs.setup_file_deps_group_alias sctx ~dir lib ~exts:[".cmi"; ".cmx"];
|
||||
SC.Libs.setup_file_deps_alias sctx ~dir lib ~ext:".h"
|
||||
(List.map lib.install_c_headers ~f:(fun header ->
|
||||
Path.relative dir (header ^ ".h"))
|
||||
|> Path.Set.of_list);
|
||||
|
||||
(let modules =
|
||||
Module.Name.Map.fold modules ~init:[] ~f:(fun m acc ->
|
||||
if Module.has_impl m then
|
||||
m :: acc
|
||||
else
|
||||
acc)
|
||||
in
|
||||
let top_sorted_modules =
|
||||
Ocamldep.Dep_graph.top_closed_implementations dep_graphs.impl modules
|
||||
in
|
||||
List.iter Mode.all ~f:(fun mode ->
|
||||
build_lib lib ~scope ~flags ~dir ~obj_dir ~mode ~top_sorted_modules
|
||||
~modules));
|
||||
(* Build *.cma.js *)
|
||||
SC.add_rules sctx (
|
||||
let src = Library.archive lib ~dir ~ext:(Mode.compiled_lib_ext Mode.Byte) in
|
||||
let target = Path.extend_basename src ~suffix:".js" in
|
||||
Js_of_ocaml_rules.build_cm cctx
|
||||
~js_of_ocaml:lib.buildable.js_of_ocaml ~src ~target);
|
||||
|
||||
if ctx.natdynlink_supported then
|
||||
Option.iter ctx.ocamlopt ~f:(fun ocamlopt ->
|
||||
let src = Library.archive lib ~dir ~ext:(Mode.compiled_lib_ext Native) in
|
||||
let dst = Library.archive lib ~dir ~ext:".cmxs" in
|
||||
let build =
|
||||
Build.dyn_paths (Build.arr (fun () ->
|
||||
[Library.archive lib ~dir ~ext:ctx.ext_lib]))
|
||||
>>>
|
||||
Ocaml_flags.get flags Native
|
||||
>>>
|
||||
Build.run ~context:ctx
|
||||
(Ok ocamlopt)
|
||||
[ Dyn (fun flags -> As flags)
|
||||
; A "-shared"; A "-linkall"
|
||||
; A "-I"; Path dir
|
||||
; A "-o"; Target dst
|
||||
; Dep src
|
||||
]
|
||||
in
|
||||
let build =
|
||||
if Library.has_stubs lib then
|
||||
Build.path (Library.stubs_archive ~dir lib ~ext_lib:ctx.ext_lib)
|
||||
>>>
|
||||
build
|
||||
else
|
||||
build
|
||||
in
|
||||
SC.add_rule sctx build
|
||||
);
|
||||
|
||||
Odoc.setup_library_odoc_rules lib ~requires ~modules ~dep_graphs ~scope;
|
||||
|
||||
let flags =
|
||||
match alias_module with
|
||||
| None -> Ocaml_flags.common flags
|
||||
| Some m ->
|
||||
Ocaml_flags.prepend_common ["-open"; Module.Name.to_string m.name] flags
|
||||
|> Ocaml_flags.common
|
||||
in
|
||||
|
||||
Sub_system.gen_rules
|
||||
{ super_context = sctx
|
||||
; dir
|
||||
; stanza = lib
|
||||
; scope
|
||||
; source_modules
|
||||
; compile_info
|
||||
};
|
||||
|
||||
(cctx,
|
||||
Merlin.make ()
|
||||
~requires:(Lib.Compile.requires compile_info)
|
||||
~flags
|
||||
~preprocess:(Buildable.single_preprocess lib.buildable)
|
||||
~libname:lib.name
|
||||
~objs_dirs:(Path.Set.singleton obj_dir))
|
||||
|
||||
let library_rules (lib : Library.t) ~dir_contents ~dir ~scope
|
||||
~dir_kind : Compilation_context.t * Merlin.t =
|
||||
let compile_info =
|
||||
Lib.DB.get_compile_info (Scope.libs scope) lib.name
|
||||
~allow_overlaps:lib.buildable.allow_overlapping_dependencies
|
||||
in
|
||||
SC.Libs.gen_select_rules sctx compile_info ~dir;
|
||||
SC.Libs.with_lib_deps sctx compile_info ~dir
|
||||
~f:(fun () ->
|
||||
library_rules lib ~dir_contents ~dir ~scope ~compile_info
|
||||
~dir_kind)
|
||||
let opaque = SC.opaque sctx
|
||||
|
||||
(* +-----------------------------------------------------------------+
|
||||
| Executables stuff |
|
||||
|
@ -640,8 +227,7 @@ module Gen(P : Install_rules.Params) = struct
|
|||
match (stanza : Stanza.t) with
|
||||
| Library lib ->
|
||||
let cctx, merlin =
|
||||
library_rules lib ~dir ~scope ~dir_contents
|
||||
~dir_kind:kind
|
||||
Lib_rules.rules lib ~dir ~scope ~dir_contents ~dir_kind:kind
|
||||
in
|
||||
loop stanzas (merlin :: merlins)
|
||||
((lib.buildable.loc, cctx) :: cctxs)
|
||||
|
@ -727,7 +313,7 @@ module Gen(P : Install_rules.Params) = struct
|
|||
(match components with
|
||||
| ".js" :: rest -> Js_of_ocaml_rules.setup_separate_compilation_rules
|
||||
sctx rest
|
||||
| "_doc" :: rest -> Odoc.gen_rules rest ~dir
|
||||
| "_doc" :: rest -> Lib_rules.Odoc.gen_rules rest ~dir
|
||||
| ".ppx" :: rest -> Preprocessing.gen_rules sctx rest
|
||||
| _ ->
|
||||
match
|
||||
|
@ -760,7 +346,7 @@ module Gen(P : Install_rules.Params) = struct
|
|||
Install_rules.Gen(P)
|
||||
in
|
||||
Install_rules.init ();
|
||||
Odoc.init ()
|
||||
Lib_rules.Odoc.init ()
|
||||
end
|
||||
|
||||
module type Gen = sig
|
||||
|
|
|
@ -0,0 +1,428 @@
|
|||
open Import
|
||||
open Build.O
|
||||
open Dune_file
|
||||
open! No_io
|
||||
|
||||
module SC = Super_context
|
||||
|
||||
module Gen (P : Install_rules.Params) = struct
|
||||
module Odoc = Odoc.Gen(P)
|
||||
|
||||
let sctx = P.sctx
|
||||
let ctx = SC.context sctx
|
||||
|
||||
let opaque = SC.opaque sctx
|
||||
|
||||
(* +-----------------------------------------------------------------+
|
||||
| Library stuff |
|
||||
+-----------------------------------------------------------------+ *)
|
||||
|
||||
let msvc_hack_cclibs cclibs =
|
||||
let f lib =
|
||||
if String.is_prefix lib ~prefix:"-l" then
|
||||
String.sub lib ~pos:2 ~len:(String.length lib - 2) ^ ".lib"
|
||||
else
|
||||
lib
|
||||
in
|
||||
let cclibs = List.map cclibs ~f in
|
||||
let f lib =
|
||||
if String.is_prefix lib ~prefix:"-l" then
|
||||
String.sub lib ~pos:2 ~len:(String.length lib - 2)
|
||||
else
|
||||
lib
|
||||
in
|
||||
List.map cclibs ~f
|
||||
|
||||
let build_lib (lib : Library.t) ~scope ~flags ~dir ~obj_dir ~mode
|
||||
~top_sorted_modules ~modules =
|
||||
Option.iter (Context.compiler ctx mode) ~f:(fun compiler ->
|
||||
let target = Library.archive lib ~dir ~ext:(Mode.compiled_lib_ext mode) in
|
||||
let stubs_flags =
|
||||
if not (Library.has_stubs lib) then
|
||||
[]
|
||||
else
|
||||
let stubs_name = lib.name ^ "_stubs" in
|
||||
match mode with
|
||||
| Byte -> ["-dllib"; "-l" ^ stubs_name; "-cclib"; "-l" ^ stubs_name]
|
||||
| Native -> ["-cclib"; "-l" ^ stubs_name]
|
||||
in
|
||||
let map_cclibs =
|
||||
(* https://github.com/ocaml/dune/issues/119 *)
|
||||
if ctx.ccomp_type = "msvc" then
|
||||
msvc_hack_cclibs
|
||||
else
|
||||
fun x -> x
|
||||
in
|
||||
let artifacts ~ext modules =
|
||||
List.map modules ~f:(Module.obj_file ~obj_dir ~ext)
|
||||
in
|
||||
let obj_deps =
|
||||
Build.paths (artifacts modules ~ext:(Cm_kind.ext (Mode.cm_kind mode)))
|
||||
in
|
||||
let obj_deps =
|
||||
match mode with
|
||||
| Byte -> obj_deps
|
||||
| Native ->
|
||||
obj_deps >>>
|
||||
Build.paths (artifacts modules ~ext:ctx.ext_obj)
|
||||
in
|
||||
SC.add_rule sctx
|
||||
(obj_deps
|
||||
>>>
|
||||
Build.fanout4
|
||||
(top_sorted_modules >>^artifacts ~ext:(Cm_kind.ext (Mode.cm_kind mode)))
|
||||
(SC.expand_and_eval_set sctx ~scope ~dir lib.c_library_flags
|
||||
~standard:(Build.return []))
|
||||
(Ocaml_flags.get flags mode)
|
||||
(SC.expand_and_eval_set sctx ~scope ~dir lib.library_flags
|
||||
~standard:(Build.return []))
|
||||
>>>
|
||||
Build.run ~context:ctx (Ok compiler)
|
||||
[ Dyn (fun (_, _, flags, _) -> As flags)
|
||||
; A "-a"; A "-o"; Target target
|
||||
; As stubs_flags
|
||||
; Dyn (fun (_, cclibs, _, _) -> Arg_spec.quote_args "-cclib" (map_cclibs cclibs))
|
||||
; Dyn (fun (_, _, _, library_flags) -> As library_flags)
|
||||
; As (match lib.kind with
|
||||
| Normal -> []
|
||||
| Ppx_deriver | Ppx_rewriter -> ["-linkall"])
|
||||
; Dyn (fun (cm_files, _, _, _) -> Deps cm_files)
|
||||
; Hidden_targets
|
||||
(match mode with
|
||||
| Byte -> []
|
||||
| Native -> [Library.archive lib ~dir ~ext:ctx.ext_lib])
|
||||
]))
|
||||
|
||||
let build_c_file (lib : Library.t) ~scope ~dir ~includes (src, dst) =
|
||||
SC.add_rule sctx
|
||||
(SC.expand_and_eval_set sctx ~scope ~dir lib.c_flags
|
||||
~standard:(Build.return (Context.cc_g ctx))
|
||||
>>>
|
||||
Build.run ~context:ctx
|
||||
(* We have to execute the rule in the library directory as
|
||||
the .o is produced in the current directory *)
|
||||
~dir:(Path.parent_exn src)
|
||||
(Ok ctx.ocamlc)
|
||||
[ As (Utils.g ())
|
||||
; includes
|
||||
; Dyn (fun c_flags -> Arg_spec.quote_args "-ccopt" c_flags)
|
||||
; A "-o"; Target dst
|
||||
; Dep src
|
||||
]);
|
||||
dst
|
||||
|
||||
let build_cxx_file (lib : Library.t) ~scope ~dir ~includes (src, dst) =
|
||||
let open Arg_spec in
|
||||
let output_param =
|
||||
if ctx.ccomp_type = "msvc" then
|
||||
[Concat ("", [A "/Fo"; Target dst])]
|
||||
else
|
||||
[A "-o"; Target dst]
|
||||
in
|
||||
SC.add_rule sctx
|
||||
(SC.expand_and_eval_set sctx ~scope ~dir lib.cxx_flags
|
||||
~standard:(Build.return (Context.cc_g ctx))
|
||||
>>>
|
||||
Build.run ~context:ctx
|
||||
(* We have to execute the rule in the library directory as
|
||||
the .o is produced in the current directory *)
|
||||
~dir:(Path.parent_exn src)
|
||||
(SC.resolve_program ~loc:None sctx ctx.c_compiler)
|
||||
([ S [A "-I"; Path ctx.stdlib_dir]
|
||||
; As (SC.cxx_flags sctx)
|
||||
; includes
|
||||
; Dyn (fun cxx_flags -> As cxx_flags)
|
||||
] @ output_param @
|
||||
[ A "-c"; Dep src
|
||||
]));
|
||||
dst
|
||||
|
||||
(* If the compiler reads the cmi for module alias even with
|
||||
[-w -49 -no-alias-deps], we must sandbox the build of the
|
||||
alias module since the modules it references are built after. *)
|
||||
let alias_module_build_sandbox =
|
||||
Ocaml_version.always_reads_alias_cmi ctx.version
|
||||
|
||||
let library_rules (lib : Library.t) ~dir_contents ~dir ~scope
|
||||
~compile_info ~dir_kind =
|
||||
let obj_dir = Utils.library_object_directory ~dir lib.name in
|
||||
let requires = Lib.Compile.requires compile_info in
|
||||
let dep_kind =
|
||||
if lib.optional then Lib_deps_info.Kind.Optional else Required
|
||||
in
|
||||
let flags = SC.ocaml_flags sctx ~scope ~dir lib.buildable in
|
||||
let { Dir_contents.Library_modules.
|
||||
modules; main_module_name; alias_module } =
|
||||
Dir_contents.modules_of_library dir_contents ~name:(Library.best_name lib)
|
||||
in
|
||||
let source_modules = modules in
|
||||
(* Preprocess before adding the alias module as it doesn't need
|
||||
preprocessing *)
|
||||
let pp =
|
||||
Preprocessing.make sctx ~dir ~dep_kind ~scope
|
||||
~preprocess:lib.buildable.preprocess
|
||||
~preprocessor_deps:
|
||||
(SC.Deps.interpret sctx ~scope ~dir
|
||||
lib.buildable.preprocessor_deps)
|
||||
~lint:lib.buildable.lint
|
||||
~lib_name:(Some lib.name)
|
||||
~dir_kind
|
||||
in
|
||||
let modules = Preprocessing.pp_modules pp modules in
|
||||
|
||||
let modules =
|
||||
match alias_module with
|
||||
| None -> modules
|
||||
| Some m -> Module.Name.Map.add modules m.name m
|
||||
in
|
||||
|
||||
let lib_interface_module =
|
||||
if lib.wrapped then
|
||||
Module.Name.Map.find modules main_module_name
|
||||
else
|
||||
None
|
||||
in
|
||||
let cctx =
|
||||
Compilation_context.create ()
|
||||
~super_context:sctx
|
||||
~scope
|
||||
~dir
|
||||
~dir_kind
|
||||
~obj_dir
|
||||
~modules
|
||||
?alias_module
|
||||
?lib_interface_module
|
||||
~flags
|
||||
~requires
|
||||
~preprocessing:pp
|
||||
~no_keep_locs:lib.no_keep_locs
|
||||
~opaque
|
||||
in
|
||||
|
||||
let dep_graphs = Ocamldep.rules cctx in
|
||||
|
||||
Option.iter alias_module ~f:(fun m ->
|
||||
let file =
|
||||
match m.impl with
|
||||
| Some f -> f
|
||||
| None -> Option.value_exn m.intf
|
||||
in
|
||||
SC.add_rule sctx
|
||||
(Build.return
|
||||
(Module.Name.Map.values (Module.Name.Map.remove modules m.name)
|
||||
|> List.map ~f:(fun (m : Module.t) ->
|
||||
sprintf "(** @canonical %s.%s *)\n\
|
||||
module %s = %s\n"
|
||||
(Module.Name.to_string main_module_name)
|
||||
(Module.Name.to_string m.name)
|
||||
(Module.Name.to_string m.name)
|
||||
(Module.Name.to_string (Module.real_unit_name m))
|
||||
)
|
||||
|> String.concat ~sep:"\n")
|
||||
>>> Build.write_file_dyn file.path));
|
||||
|
||||
|
||||
let dynlink = lib.dynlink in
|
||||
let js_of_ocaml = lib.buildable.js_of_ocaml in
|
||||
Module_compilation.build_modules cctx ~js_of_ocaml ~dynlink ~dep_graphs;
|
||||
Option.iter alias_module ~f:(fun m ->
|
||||
let cctx = Compilation_context.for_alias_module cctx in
|
||||
Module_compilation.build_module cctx m
|
||||
~js_of_ocaml
|
||||
~dynlink
|
||||
~sandbox:alias_module_build_sandbox
|
||||
~dep_graphs:(Ocamldep.Dep_graphs.dummy m));
|
||||
|
||||
if Library.has_stubs lib then begin
|
||||
let all_dirs = Dir_contents.dirs dir_contents in
|
||||
let h_files =
|
||||
List.fold_left all_dirs ~init:[] ~f:(fun acc dc ->
|
||||
String.Set.fold (Dir_contents.text_files dc) ~init:acc
|
||||
~f:(fun fn acc ->
|
||||
if String.is_suffix fn ~suffix:".h" then
|
||||
Path.relative (Dir_contents.dir dc) fn :: acc
|
||||
else
|
||||
acc))
|
||||
in
|
||||
let all_dirs = Path.Set.of_list (List.map all_dirs ~f:Dir_contents.dir) in
|
||||
let resolve_name ~ext (loc, fn) =
|
||||
let p = Path.relative dir (fn ^ ext) in
|
||||
if not (match Path.parent p with
|
||||
| None -> false
|
||||
| Some p -> Path.Set.mem all_dirs p) then
|
||||
Loc.fail loc
|
||||
"File %a is not part of the current directory group. \
|
||||
This is not allowed."
|
||||
Path.pp (Path.drop_optional_build_context p)
|
||||
;
|
||||
(p, Path.relative dir (fn ^ ctx.ext_obj))
|
||||
in
|
||||
let o_files =
|
||||
let includes =
|
||||
Arg_spec.S
|
||||
[ Hidden_deps h_files
|
||||
; Arg_spec.of_result_map requires ~f:(fun libs ->
|
||||
S [ Lib.L.c_include_flags libs ~stdlib_dir:ctx.stdlib_dir
|
||||
; Hidden_deps (SC.Libs.file_deps sctx libs ~ext:".h")
|
||||
])
|
||||
]
|
||||
in
|
||||
List.map lib.c_names ~f:(fun name ->
|
||||
build_c_file lib ~scope ~dir ~includes (resolve_name name ~ext:".c")
|
||||
) @ List.map lib.cxx_names ~f:(fun name ->
|
||||
build_cxx_file lib ~scope ~dir ~includes (resolve_name name ~ext:".cpp")
|
||||
)
|
||||
in
|
||||
match lib.self_build_stubs_archive with
|
||||
| Some _ -> ()
|
||||
| None ->
|
||||
let ocamlmklib ~sandbox ~custom ~targets =
|
||||
SC.add_rule sctx ~sandbox
|
||||
(SC.expand_and_eval_set sctx ~scope ~dir
|
||||
lib.c_library_flags ~standard:(Build.return [])
|
||||
>>>
|
||||
Build.run ~context:ctx
|
||||
(Ok ctx.ocamlmklib)
|
||||
[ As (Utils.g ())
|
||||
; if custom then A "-custom" else As []
|
||||
; A "-o"
|
||||
; Path (Path.relative dir (sprintf "%s_stubs" lib.name))
|
||||
; Deps o_files
|
||||
; Dyn (fun cclibs ->
|
||||
(* https://github.com/ocaml/dune/issues/119 *)
|
||||
if ctx.ccomp_type = "msvc" then
|
||||
let cclibs = msvc_hack_cclibs cclibs in
|
||||
Arg_spec.quote_args "-ldopt" cclibs
|
||||
else
|
||||
As cclibs
|
||||
)
|
||||
; Hidden_targets targets
|
||||
])
|
||||
in
|
||||
let static = Library.stubs_archive lib ~dir ~ext_lib:ctx.ext_lib in
|
||||
let dynamic = Library.dll lib ~dir ~ext_dll:ctx.ext_dll in
|
||||
let modes =
|
||||
Mode_conf.Set.eval lib.modes
|
||||
~has_native:(Option.is_some ctx.ocamlopt)
|
||||
in
|
||||
if modes.native &&
|
||||
modes.byte &&
|
||||
lib.dynlink
|
||||
then begin
|
||||
(* If we build for both modes and support dynlink, use a
|
||||
single invocation to build both the static and dynamic
|
||||
libraries *)
|
||||
ocamlmklib ~sandbox:false ~custom:false ~targets:[static; dynamic]
|
||||
end else begin
|
||||
ocamlmklib ~sandbox:false ~custom:true ~targets:[static];
|
||||
(* We can't tell ocamlmklib to build only the dll, so we
|
||||
sandbox the action to avoid overriding the static archive *)
|
||||
ocamlmklib ~sandbox:true ~custom:false ~targets:[dynamic]
|
||||
end
|
||||
end;
|
||||
|
||||
List.iter Cm_kind.all ~f:(fun cm_kind ->
|
||||
let files =
|
||||
Module.Name.Map.fold modules ~init:Path.Set.empty ~f:(fun m acc ->
|
||||
match Module.cm_file m ~obj_dir cm_kind with
|
||||
| None -> acc
|
||||
| Some fn -> Path.Set.add acc fn)
|
||||
in
|
||||
SC.Libs.setup_file_deps_alias sctx ~dir lib ~ext:(Cm_kind.ext cm_kind)
|
||||
files);
|
||||
SC.Libs.setup_file_deps_group_alias sctx ~dir lib ~exts:[".cmi"; ".cmx"];
|
||||
SC.Libs.setup_file_deps_alias sctx ~dir lib ~ext:".h"
|
||||
(List.map lib.install_c_headers ~f:(fun header ->
|
||||
Path.relative dir (header ^ ".h"))
|
||||
|> Path.Set.of_list);
|
||||
|
||||
(let modules =
|
||||
Module.Name.Map.fold modules ~init:[] ~f:(fun m acc ->
|
||||
if Module.has_impl m then
|
||||
m :: acc
|
||||
else
|
||||
acc)
|
||||
in
|
||||
let top_sorted_modules =
|
||||
Ocamldep.Dep_graph.top_closed_implementations dep_graphs.impl modules
|
||||
in
|
||||
List.iter Mode.all ~f:(fun mode ->
|
||||
build_lib lib ~scope ~flags ~dir ~obj_dir ~mode ~top_sorted_modules
|
||||
~modules));
|
||||
(* Build *.cma.js *)
|
||||
SC.add_rules sctx (
|
||||
let src = Library.archive lib ~dir ~ext:(Mode.compiled_lib_ext Mode.Byte) in
|
||||
let target = Path.extend_basename src ~suffix:".js" in
|
||||
Js_of_ocaml_rules.build_cm cctx
|
||||
~js_of_ocaml:lib.buildable.js_of_ocaml ~src ~target);
|
||||
|
||||
if ctx.natdynlink_supported then
|
||||
Option.iter ctx.ocamlopt ~f:(fun ocamlopt ->
|
||||
let src = Library.archive lib ~dir ~ext:(Mode.compiled_lib_ext Native) in
|
||||
let dst = Library.archive lib ~dir ~ext:".cmxs" in
|
||||
let build =
|
||||
Build.dyn_paths (Build.arr (fun () ->
|
||||
[Library.archive lib ~dir ~ext:ctx.ext_lib]))
|
||||
>>>
|
||||
Ocaml_flags.get flags Native
|
||||
>>>
|
||||
Build.run ~context:ctx
|
||||
(Ok ocamlopt)
|
||||
[ Dyn (fun flags -> As flags)
|
||||
; A "-shared"; A "-linkall"
|
||||
; A "-I"; Path dir
|
||||
; A "-o"; Target dst
|
||||
; Dep src
|
||||
]
|
||||
in
|
||||
let build =
|
||||
if Library.has_stubs lib then
|
||||
Build.path (Library.stubs_archive ~dir lib ~ext_lib:ctx.ext_lib)
|
||||
>>>
|
||||
build
|
||||
else
|
||||
build
|
||||
in
|
||||
SC.add_rule sctx build
|
||||
);
|
||||
|
||||
Odoc.setup_library_odoc_rules lib ~requires ~modules ~dep_graphs ~scope;
|
||||
|
||||
let flags =
|
||||
match alias_module with
|
||||
| None -> Ocaml_flags.common flags
|
||||
| Some m ->
|
||||
Ocaml_flags.prepend_common ["-open"; Module.Name.to_string m.name] flags
|
||||
|> Ocaml_flags.common
|
||||
in
|
||||
|
||||
Sub_system.gen_rules
|
||||
{ super_context = sctx
|
||||
; dir
|
||||
; stanza = lib
|
||||
; scope
|
||||
; source_modules
|
||||
; compile_info
|
||||
};
|
||||
|
||||
(cctx,
|
||||
Merlin.make ()
|
||||
~requires:(Lib.Compile.requires compile_info)
|
||||
~flags
|
||||
~preprocess:(Buildable.single_preprocess lib.buildable)
|
||||
~libname:lib.name
|
||||
~objs_dirs:(Path.Set.singleton obj_dir))
|
||||
|
||||
let rules (lib : Library.t) ~dir_contents ~dir ~scope
|
||||
~dir_kind : Compilation_context.t * Merlin.t =
|
||||
let compile_info =
|
||||
Lib.DB.get_compile_info (Scope.libs scope) lib.name
|
||||
~allow_overlaps:lib.buildable.allow_overlapping_dependencies
|
||||
in
|
||||
SC.Libs.gen_select_rules sctx compile_info ~dir;
|
||||
SC.Libs.with_lib_deps sctx compile_info ~dir
|
||||
~f:(fun () ->
|
||||
library_rules lib ~dir_contents ~dir ~scope ~compile_info
|
||||
~dir_kind)
|
||||
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
open Stdune
|
||||
open Dune_file
|
||||
|
||||
module Gen (S : sig val sctx : Super_context.t end) : sig
|
||||
|
||||
module Odoc : sig
|
||||
val init : unit -> unit
|
||||
val gen_rules : dir:Path.t -> string list -> unit
|
||||
end
|
||||
|
||||
val rules
|
||||
: Library.t
|
||||
-> dir_contents:Dir_contents.t
|
||||
-> dir:Path.t
|
||||
-> scope:Scope.t
|
||||
-> dir_kind:Usexp.syntax
|
||||
-> Compilation_context.t * Merlin.t
|
||||
end
|
|
@ -939,3 +939,7 @@ module Action = struct
|
|||
| [] -> build
|
||||
| fail :: _ -> Build.fail fail >>> build
|
||||
end
|
||||
|
||||
let opaque t =
|
||||
t.context.profile = "dev"
|
||||
&& Ocaml_version.supports_opaque_for_mli t.context.version
|
||||
|
|
|
@ -271,3 +271,5 @@ module Scope_key : sig
|
|||
|
||||
val to_string : string -> Dune_project.Name.t -> string
|
||||
end
|
||||
|
||||
val opaque : t -> bool
|
||||
|
|
Loading…
Reference in New Issue