(***********************************************************************) (* *) (* ocamlbuild *) (* *) (* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *) (* *) (* Copyright 2007 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed *) (* under the terms of the Q Public License version 1.0. *) (* *) (***********************************************************************) (* Original author: Nicolas Pouillard *) open My_std open Format open Log open Pathname.Operators open Tools open Command open Rule open Tags.Operators open Ocaml_utils open Rule.Common_commands open Outcome let forpack_flags arg tags = if Tags.mem "pack" tags then Ocaml_arch.forpack_flags_of_pathname arg else N let ocamlc_c tags arg out = let tags = tags++"ocaml"++"byte" in Cmd (S [!Options.ocamlc; A"-c"; T(tags++"compile"); ocaml_ppflags tags; ocaml_include_flags arg; A"-o"; Px out; P arg]) let ocamlc_link flag tags deps out = Cmd (S [!Options.ocamlc; flag; T tags; atomize_paths deps; A"-o"; Px out]) let ocamlc_link_lib = ocamlc_link (A"-a") let ocamlc_link_prog = ocamlc_link N let ocamlmklib tags deps out = Cmd (S [!Options.ocamlmklib; T tags; atomize_paths deps; A"-o"; Px (Pathname.remove_extensions out)]) let ocamlmktop tags deps out = Cmd( S [!Options.ocamlmktop; T (tags++"mktop"); atomize_paths deps; A"-o"; Px out]) let byte_lib_linker tags = if Tags.mem "ocamlmklib" tags then ocamlmklib tags else ocamlc_link_lib tags let byte_lib_linker_tags tags = tags++"ocaml"++"link"++"byte"++"library" let ocamlc_p tags deps out = Cmd (S [!Options.ocamlc; A"-pack"; T tags; atomize_paths deps; A"-o"; Px out]) let ocamlopt_c tags arg out = let tags = tags++"ocaml"++"native" in Cmd (S [!Options.ocamlopt; A"-c"; Ocaml_arch.forpack_flags_of_pathname arg; T(tags++"compile"); ocaml_ppflags tags; ocaml_include_flags arg; A"-o"; Px out (* FIXME ocamlopt bug -o cannot be after the input file *); P arg]) let ocamlopt_link flag tags deps out = Cmd (S [!Options.ocamlopt; flag; forpack_flags out tags; T tags; atomize_paths deps; A"-o"; Px out]) let ocamlopt_link_lib = ocamlopt_link (A"-a") let ocamlopt_link_shared_lib = ocamlopt_link (A"-shared") let ocamlopt_link_prog = ocamlopt_link N let ocamlopt_p tags deps out = let dirnames = List.union [] (List.map Pathname.dirname deps) in let include_flags = List.fold_right ocaml_add_include_flag dirnames [] in let mli = Pathname.update_extensions "mli" out in let cmd = S [!Options.ocamlopt; A"-pack"; forpack_flags out tags; T tags; S include_flags; atomize_paths deps; A"-o"; Px out] in if (*FIXME true ||*) Pathname.exists mli then Cmd cmd else let rm = S[A"rm"; A"-f"; P mli] in Cmd(S[A"touch"; P mli; Sh" ; if "; cmd; Sh" ; then "; rm; Sh" ; else "; rm; Sh" ; exit 1; fi"]) let native_lib_linker tags = if Tags.mem "ocamlmklib" tags then ocamlmklib tags else ocamlopt_link_lib tags let native_shared_lib_linker tags = (* ocamlmklib seems to not support -shared, is this OK? if Tags.mem "ocamlmklib" tags then ocamlmklib tags else *) ocamlopt_link_shared_lib tags let native_lib_linker_tags tags = tags++"ocaml"++"link"++"native"++"library" let prepare_compile build ml = let dir = Pathname.dirname ml in let include_dirs = Pathname.include_dirs_of dir in let modules = path_dependencies_of ml in let results = build (List.map (fun (_, x) -> expand_module include_dirs x ["cmi"]) modules) in List.iter2 begin fun (mandatory, name) res -> match mandatory, res with | _, Good _ -> () | `mandatory, Bad exn -> if !Options.ignore_auto then dprintf 3 "Warning: Failed to build the module \ %s requested by ocamldep" name else raise exn | `just_try, Bad _ -> () end modules results let byte_compile_ocaml_interf mli cmi env build = let mli = env mli and cmi = env cmi in prepare_compile build mli; ocamlc_c (tags_of_pathname mli++"interf") mli cmi (* given that .cmi can be built from either ocamlc and ocamlopt, this "agnostic" rule chooses either compilers depending on whether the "native" tag is present. This was requested during PR#4613 as way to enable using ocamlbuild in environments where only ocamlopt is available, not ocamlc. *) let compile_ocaml_interf mli cmi env build = let mli = env mli and cmi = env cmi in prepare_compile build mli; let tags = tags_of_pathname mli++"interf" in let comp_c = if Tags.mem "native" tags then ocamlopt_c else ocamlc_c in comp_c tags mli cmi let byte_compile_ocaml_implem ?tag ml cmo env build = let ml = env ml and cmo = env cmo in prepare_compile build ml; ocamlc_c (Tags.union (tags_of_pathname ml) (tags_of_pathname cmo)++"implem"+++tag) ml cmo let cache_prepare_link = Hashtbl.create 107 let rec prepare_link tag cmx extensions build = let key = (tag, cmx, extensions) in let dir = Pathname.dirname cmx in let include_dirs = Pathname.include_dirs_of dir in let ml = Pathname.update_extensions "ml" cmx in let mli = Pathname.update_extensions "mli" cmx in let modules = List.union (if Pathname.exists (ml-.-"depends") then path_dependencies_of ml else []) (if Pathname.exists (mli-.-"depends") then path_dependencies_of mli else []) in let modules = if (modules = []) && (Pathname.exists (ml^"pack")) then List.map (fun s -> (`mandatory, s)) (string_list_of_file (ml^"pack")) else modules in if modules <> [] && not (Hashtbl.mem cache_prepare_link key) then let () = Hashtbl.add cache_prepare_link key true in let modules' = List.map (fun (_, x) -> expand_module include_dirs x extensions) modules in List.iter2 begin fun (mandatory, _) result -> match mandatory, result with | _, Good p -> prepare_link tag p extensions build | `mandatory, Bad exn -> if not !Options.ignore_auto then raise exn | `just_try, Bad _ -> () end modules (build modules') let native_compile_ocaml_implem ?tag ?(cmx_ext="cmx") ml env build = let ml = env ml in let cmi = Pathname.update_extensions "cmi" ml in let cmx = Pathname.update_extensions cmx_ext ml in prepare_link cmx cmi [cmx_ext; "cmi"] build; ocamlopt_c (Tags.union (tags_of_pathname ml) (tags_of_pathname cmx)++"implem"+++tag) ml cmx let libs_of_use_lib tags = Tags.fold begin fun tag acc -> try let libpath, extern = Hashtbl.find info_libraries tag in if extern then acc else libpath :: acc with Not_found -> acc end tags [] let prepare_libs cma_ext a_ext out build = let out_no_ext = Pathname.remove_extension out in let libs1 = List.union (libraries_of out_no_ext) (libs_of_use_lib (tags_of_pathname out)) in let () = dprintf 10 "prepare_libs: %S -> %a" out pp_l libs1 in let libs = List.map (fun x -> x-.-cma_ext) libs1 in let libs2 = List.map (fun lib -> [lib-.-a_ext]) libs1 in List.iter ignore_good (build libs2); libs let library_index = Hashtbl.create 32 let package_index = Hashtbl.create 32 let hidden_packages = ref [] let hide_package_contents package = hidden_packages := package :: !hidden_packages module Ocaml_dependencies_input = struct let fold_dependencies = Resource.Cache.fold_dependencies let fold_libraries f = Hashtbl.fold f library_index let fold_packages f = Hashtbl.fold f package_index end module Ocaml_dependencies = Ocaml_dependencies.Make(Ocaml_dependencies_input) let caml_transitive_closure = Ocaml_dependencies.caml_transitive_closure let link_one_gen linker tagger cmX out env _build = let cmX = env cmX and out = env out in let tags = tagger (tags_of_pathname out) in linker tags [cmX] out let link_gen cmX_ext cma_ext a_ext extensions linker tagger cmX out env build = let cmX = env cmX and out = env out in let tags = tagger (tags_of_pathname out) in let dyndeps = Rule.build_deps_of_tags build (tags++"link_with") in let cmi = Pathname.update_extensions "cmi" cmX in prepare_link cmX cmi extensions build; let libs = prepare_libs cma_ext a_ext out build in let hidden_packages = List.map (fun x -> x-.-cmX_ext) !hidden_packages in let deps = caml_transitive_closure ~caml_obj_ext:cmX_ext ~caml_lib_ext:cma_ext ~used_libraries:libs ~hidden_packages (cmX :: dyndeps) in let deps = (List.filter (fun l -> not (List.mem l deps)) libs) @ deps in (* Hack to avoid linking twice with the standard library. *) let stdlib = "stdlib/stdlib"-.-cma_ext in let is_not_stdlib x = x <> stdlib in let deps = List.filter is_not_stdlib deps in if deps = [] then failwith "Link list cannot be empty"; let () = dprintf 6 "link: %a -o %a" print_string_list deps Pathname.print out in linker (tags++"dont_link_with") deps out let byte_link_gen = link_gen "cmo" "cma" "cma" ["cmo"; "cmi"] let byte_link = byte_link_gen ocamlc_link_prog (fun tags -> tags++"ocaml"++"link"++"byte"++"program") let byte_output_obj = byte_link_gen ocamlc_link_prog (fun tags -> tags++"ocaml"++"link"++"byte"++"output_obj") let byte_library_link = byte_link_gen byte_lib_linker byte_lib_linker_tags let byte_debug_link_gen = link_gen "d.cmo" "d.cma" "d.cma" ["d.cmo"; "cmi"] let byte_debug_link = byte_debug_link_gen ocamlc_link_prog (fun tags -> tags++"ocaml"++"link"++"byte"++"debug"++"program") let byte_debug_library_link = byte_debug_link_gen byte_lib_linker (fun tags -> byte_lib_linker_tags tags++"debug") let native_link_gen linker = link_gen "cmx" "cmxa" !Options.ext_lib [!Options.ext_obj; "cmi"] linker let native_link x = native_link_gen ocamlopt_link_prog (fun tags -> tags++"ocaml"++"link"++"native"++"program") x let native_output_obj x = native_link_gen ocamlopt_link_prog (fun tags -> tags++"ocaml"++"link"++"native"++"output_obj") x let native_library_link x = native_link_gen native_lib_linker native_lib_linker_tags x let native_profile_link_gen linker = link_gen "p.cmx" "p.cmxa" ("p" -.- !Options.ext_lib) ["p" -.- !Options.ext_obj; "cmi"] linker let native_profile_link x = native_profile_link_gen ocamlopt_link_prog (fun tags -> tags++"ocaml"++"link"++"native"++"profile"++"program") x let native_profile_library_link x = native_profile_link_gen native_lib_linker (fun tags -> native_lib_linker_tags tags++"profile") x let link_units table extensions cmX_ext cma_ext a_ext linker tagger contents_list cmX env build = let cmX = env cmX in let tags = tagger (tags_of_pathname cmX) in let _ = Rule.build_deps_of_tags build tags in let dir = let dir1 = Pathname.remove_extensions cmX in if Resource.exists_in_source_dir dir1 then dir1 else Pathname.dirname cmX in let include_dirs = Pathname.include_dirs_of dir in let extension_keys = List.map fst extensions in let libs = prepare_libs cma_ext a_ext cmX build in let results = build begin List.map begin fun module_name -> expand_module include_dirs module_name extension_keys end contents_list end in let module_paths = List.map begin function | Good p -> let extension_values = List.assoc (Pathname.get_extensions p) extensions in List.iter begin fun ext -> List.iter ignore_good (build [[Pathname.update_extensions ext p]]) end extension_values; p | Bad exn -> raise exn end results in Hashtbl.replace table cmX module_paths; let hidden_packages = List.map (fun x -> x-.-cmX_ext) !hidden_packages in let deps = caml_transitive_closure ~caml_obj_ext:cmX_ext ~caml_lib_ext:cma_ext ~hidden_packages ~pack_mode:true module_paths in let full_contents = libs @ module_paths in let deps = List.filter (fun x -> List.mem x full_contents) deps in let deps = (List.filter (fun l -> not (List.mem l deps)) libs) @ deps in (* Hack to avoid linking twice with the standard library. *) let stdlib = "stdlib/stdlib"-.-cma_ext in let is_not_stdlib x = x <> stdlib in let deps = List.filter is_not_stdlib deps in linker tags deps cmX let link_modules = link_units library_index let pack_modules = link_units package_index let link_from_file link modules_file cmX env build = let modules_file = env modules_file in let contents_list = string_list_of_file modules_file in link contents_list cmX env build let byte_library_link_modules = link_modules [("cmo",[])] "cmo" "cma" "cma" byte_lib_linker byte_lib_linker_tags let byte_library_link_mllib = link_from_file byte_library_link_modules let byte_toplevel_link_modules = link_modules [("cmo",[])] "cmo" "cma" "cma" ocamlmktop (fun tags -> tags++"ocaml"++"link"++"byte"++"toplevel") let byte_toplevel_link_mltop = link_from_file byte_toplevel_link_modules let byte_debug_library_link_modules = link_modules [("d.cmo",[])] "d.cmo" "d.cma" "d.cma" byte_lib_linker (fun tags -> byte_lib_linker_tags tags++"debug") let byte_debug_library_link_mllib = link_from_file byte_debug_library_link_modules let byte_pack_modules = pack_modules [("cmo",["cmi"]); ("cmi",[])] "cmo" "cma" "cma" ocamlc_p (fun tags -> tags++"ocaml"++"pack"++"byte") let byte_pack_mlpack = link_from_file byte_pack_modules let byte_debug_pack_modules = pack_modules [("d.cmo",["cmi"]); ("cmi",[])] "d.cmo" "d.cma" "d.cma" ocamlc_p (fun tags -> tags++"ocaml"++"pack"++"byte"++"debug") let byte_debug_pack_mlpack = link_from_file byte_debug_pack_modules let native_pack_modules x = pack_modules [("cmx",["cmi"; !Options.ext_obj]); ("cmi",[])] "cmx" "cmxa" !Options.ext_lib ocamlopt_p (fun tags -> tags++"ocaml"++"pack"++"native") x let native_pack_mlpack = link_from_file native_pack_modules let native_profile_pack_modules x = pack_modules [("p.cmx",["cmi"; "p" -.- !Options.ext_obj]); ("cmi",[])] "p.cmx" "p.cmxa" ("p" -.- !Options.ext_lib) ocamlopt_p (fun tags -> tags++"ocaml"++"pack"++"native"++"profile") x let native_profile_pack_mlpack = link_from_file native_profile_pack_modules let native_library_link_modules x = link_modules [("cmx",[!Options.ext_obj])] "cmx" "cmxa" !Options.ext_lib native_lib_linker native_lib_linker_tags x let native_shared_library_link_modules x = link_modules [("cmx",[!Options.ext_obj])] "cmx" "cmxa" !Options.ext_lib native_shared_lib_linker (fun tags -> native_lib_linker_tags tags++"shared") x let native_library_link_mllib = link_from_file native_library_link_modules let native_shared_library_link_mldylib = link_from_file native_shared_library_link_modules let native_shared_library_tags tags basetags = List.fold_left (++) (basetags++"ocaml"++"link"++"native"++"shared"++"library") tags let native_shared_library_link ?(tags = []) x = link_one_gen native_shared_lib_linker (native_shared_library_tags tags) x let native_profile_library_link_modules x = link_modules [("p.cmx",["p" -.- !Options.ext_obj])] "p.cmx" "p.cmxa" ("p" -.- !Options.ext_lib) native_lib_linker (fun tags -> native_lib_linker_tags tags++"profile") x let native_profile_shared_library_link_modules x = link_modules [("p.cmx",["p" -.- !Options.ext_obj])] "p.cmx" "p.cmxa" ("p" -.- !Options.ext_lib) native_shared_lib_linker (fun tags -> native_lib_linker_tags tags++"shared"++"profile") x let native_profile_library_link_mllib = link_from_file native_profile_library_link_modules let native_profile_shared_library_link_mldylib = link_from_file native_profile_shared_library_link_modules