(****************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Nicolas Pouillard, 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 GNU Library General Public License, with the special *)
(* exception on linking described in LICENSE at the top of the Camlp4 *)
(* source tree. *)
(* *)
(****************************************************************************)
open Ocamlbuild_plugin
open Command
open Arch
open Format
module C = Myocamlbuild_config
let () = mark_tag_used "windows"
let windows = C.os_type = "Win32"
let () = if windows then tag_any ["windows"]
let add_exe a =
if not windows || Pathname.check_extension a "exe" then a
else a-.-"exe"
module Camlp4deps = struct
let lexer = Genlex.make_lexer ["INCLUDE"; ";"; "="; ":"]
let rec parse strm =
match Stream.peek strm with
| None -> []
| Some(Genlex.Kwd "INCLUDE") ->
Stream.junk strm;
begin match Stream.peek strm with
| Some(Genlex.String s) ->
Stream.junk strm;
s :: parse strm
| _ -> invalid_arg "Camlp4deps parse failure"
end
| Some _ ->
Stream.junk strm;
parse strm
let parse_file file =
with_input_file file begin fun ic ->
let strm = Stream.of_channel ic in
parse (lexer strm)
end
let build_deps build file =
let includes = parse_file file in
List.iter Outcome.ignore_good (build (List.map (fun i -> [i]) includes));
end
let () =
dispatch begin function
| Before_options ->
Options.use_ocamlfind := false
| After_options ->
Options.use_ocamlfind := false;
Options.make_links := false
| After_rules ->
let use_external_camlp4boot =
try ignore (Sys.getenv "EXT_CAMLP4BOOT" : string); true with Not_found -> false
in
let hot_camlp4boot_dep, hot_camlp4boot_cmd =
if use_external_camlp4boot then
(None, "camlp4boot")
else
let exe =
"camlp4boot" ^
if C.ocamlnat then
(* If we are using a native plugin, we might as well use a native
preprocessor. *)
".native"
else
".byte"
in
let dep = "camlp4"/"boot"/exe in
let cmd =
let ( / ) = Filename.concat in
(*
* Workaround ocamlbuild problems on Windows by double-escaping.
* On systems using forward-slash, the calls to String.escaped will be
* no-ops anyway and the code will continue to work even once ocamlbuild
* correctly escapes output (the issue is trying to escape output for both cmd
* and bash)
*)
String.escaped (String.escaped ("camlp4"/"boot"/exe))
in
(Some dep, cmd)
in
flag ["ocaml"; "ocamlyacc"] (A"-v");
flag ["ocaml"; "compile"; "strict_sequence"] (A"-strict-sequence");
let add_extensions extensions modules =
List.fold_right begin fun x ->
List.fold_right begin fun ext acc ->
(x^ext) :: acc
end extensions
end modules []
in
(match hot_camlp4boot_dep with
| Some fn ->
dep ["camlp4boot"] [fn]
| None -> ());
flag ["ocaml"; "pp"; "camlp4boot"] (P hot_camlp4boot_cmd);
flag ["ocaml"; "pp"; "camlp4boot"; "native"] (S[A"-D"; A"OPT"]);
flag ["ocaml"; "pp"; "camlp4boot"; "pp:dep"] (S[A"-D"; A"OPT"]);
flag ["ocaml"; "pp"; "camlp4boot"; "pp:doc"] (S[A"-printer"; A"o"]);
let exn_tracer = Pathname.pwd/"camlp4"/"boot"/"Camlp4ExceptionTracer.cmo" in
if Pathname.exists exn_tracer then
flag ["ocaml"; "pp"; "camlp4boot"; "exntracer"] (P exn_tracer);
use_lib "camlp4/mkcamlp4" "camlp4/camlp4lib";
ocaml_lib ~extern:true ~dir:"+compiler-libs" "ocamlcommon";
let setup_arch arch =
let annotated_arch = annotate arch in
let (_include_dirs_table, _for_pack_table) = mk_tables annotated_arch in
(* Format.eprintf "%a@." (Ocaml_arch.print_table (List.print pp_print_string)) include_dirs_table; *)
iter_info begin fun i ->
Pathname.define_context i.current_path i.include_dirs
end annotated_arch
in
let camlp4_arch =
dir "" [
dir "camlp4" [
dir "config" [];
dir_pack "Camlp4" [
dir_pack "Struct" [
dir_pack "Grammar" [];
];
dir_pack "Printers" [];
];
dir_pack "Camlp4Top" [];
];
]
in
setup_arch camlp4_arch;
Pathname.define_context "camlp4" ["camlp4/config"];
Pathname.define_context "camlp4/boot" ["camlp4/config"];
Pathname.define_context "camlp4/Camlp4Parsers" ["camlp4"; "camlp4/config"];
Pathname.define_context "camlp4/Camlp4Printers" ["camlp4"; "camlp4/config"];
Pathname.define_context "camlp4/Camlp4Filters" ["camlp4"; "camlp4/config"];
Pathname.define_context "camlp4/Camlp4Top" ["camlp4"; "camlp4/config"];
(* Some modules of camlp4 have the same names as ones from compiler-libs. For this
reason we can't just -I +compiler-libs. Instead we copy the .cmi of the few modules
of compiler-libs we are using to a local directory. *)
let import = [
"warnings.cmi";
"location.cmi";
"longident.cmi";
"asttypes.cmi";
"parsetree.cmi";
"outcometree.cmi";
"oprint.cmi";
"toploop.cmi";
"topdirs.cmi";
] in
let import =
if Myocamlbuild_config.ocamlnat then
"opttoploop.cmi" :: "opttopdirs.cmi" :: import
else
import
in
List.iter
(fun fn ->
let prod = "camlp4" / "import" / fn in
rule (Printf.sprintf "copy %s" fn)
~dep:"camlp4/import/.keepme"
~prod
(fun _ _ -> cp (C.standard_library / "compiler-libs" / fn) prod))
import;
flag ["ocaml"; "compile"; "use_import"] & S[A "-I"; A "camlp4/import"];
dep ["ocaml"; "compile"; "use_import"]
(List.map (fun fn -> "camlp4" / "import" / fn) import);
let gen_import = add_exe "camlp4/config/gen_import.byte" in
let camlp4_import = "camlp4/config/Camlp4_import.ml" in
rule "generate Camlp4_import.ml"
~dep:gen_import
~prod:camlp4_import
(fun _ _ ->
Cmd (S [Px gen_import; A Myocamlbuild_config.libdir]));
copy_rule "% -> %.exe" ~insert:`bottom "%" "%.exe";
if windows then flag ["ocamlmklib"] (A"-custom");
let p4 = Pathname.concat "camlp4" in
let pa = Pathname.concat (p4 "Camlp4Parsers") in
let pr = Pathname.concat (p4 "Camlp4Printers") in
(* let fi = Pathname.concat (p4 "Camlp4Filters") in*)
let top = Pathname.concat (p4 "Camlp4Top") in
let pa_r = pa "Camlp4OCamlRevisedParser" in
let pa_o = pa "Camlp4OCamlParser" in
let pa_q = pa "Camlp4QuotationExpander" in
let pa_qc = pa "Camlp4QuotationCommon" in
let pa_rq = pa "Camlp4OCamlRevisedQuotationExpander" in
let pa_oq = pa "Camlp4OCamlOriginalQuotationExpander" in
let pa_rp = pa "Camlp4OCamlRevisedParserParser" in
let pa_op = pa "Camlp4OCamlParserParser" in
let pa_g = pa "Camlp4GrammarParser" in
let pa_l = pa "Camlp4ListComprehension" in
let pa_macro = pa "Camlp4MacroParser" in
let pa_debug = pa "Camlp4DebugParser" in
let pr_dump = pr "Camlp4OCamlAstDumper" in
(* let pr_r = pr "Camlp4OCamlRevisedPrinter" in
let pr_o = pr "Camlp4OCamlPrinter" in*)
let pr_a = pr "Camlp4AutoPrinter" in
(* let fi_exc = fi "Camlp4ExceptionTracer" in*)
(* let fi_meta = fi "MetaGenerator" in*)
let camlp4_bin = p4 "Camlp4Bin" in
let top_rprint = top "Rprint" in
let top_top = top "Top" in
let top_optrprint = top "OptRprint" in
let top_opttop = top "OptTop" in
let camlp4Profiler = p4 "Camlp4Profiler" in
let camlp4lib_cma = p4 "camlp4lib.cma" in
let camlp4lib_cmxa = p4 "camlp4lib.cmxa" in
let camlp4lib_lib = p4 ("camlp4lib"^C.ext_lib) in
let camlp4lib_mllib = p4 ("camlp4lib.mllib") in
let special_modules =
if Sys.file_exists "./boot/Profiler.cmo" then [camlp4Profiler] else []
in
List.iter
(fun (src, dst) ->
let src = src-.-"ml" in
let dst = dst-.-"ml" in
rule dst
~dep:src
~prod:dst
(fun _ _ ->
Cmd(S[P"sed"; A"s/Toploop/Opttoploop/g;s/Topdirs/Opttopdirs/g";
P src; Sh ">"; P dst])))
[ (top_top, top_opttop)
; (top_rprint, top_optrprint)
];
let mk_camlp4_top_lib name modules =
let name = "camlp4"/name in
let cma = name-.-"cma" in
let deps = special_modules @ modules @ [top_top] in
let cmos = add_extensions [".cmo"] deps in
rule cma
~deps:(camlp4lib_cma::cmos)
~prods:[cma]
~insert:(`before "ocaml: mllib & cmo* -> cma")
begin fun _ _ ->
Cmd(S[!Options.ocamlc; A"-a"; T(tags_of_pathname cma++"ocaml"++"link"++"byte");
P camlp4lib_cma; A"-linkall"; atomize cmos; A"-o"; Px cma])
end;
if Myocamlbuild_config.ocamlnat then begin
let cmxa = name-.-"cmxa" in
let deps =
List.map
(fun dep ->
if dep = top_top then
top_opttop
else if dep = top_rprint then
top_optrprint
else
dep)
deps
in
let cmxs = add_extensions [".cmx"] deps in
rule cmxa
~deps:(camlp4lib_cmxa::camlp4lib_mllib::cmxs)
~prods:[cmxa]
~insert:(`before "ocaml: mllib & cmx* & o* -> cmxa & a")
begin fun _ _ ->
let camlp4lib_cmxs =
List.map
(fun m -> p4 (m-.-"cmx"))
(string_list_of_file camlp4lib_mllib)
in
Cmd(S[!Options.ocamlopt; A"-a";
T(tags_of_pathname cma++"ocaml"++"link"++"native");
atomize camlp4lib_cmxs; A"-linkall"; atomize cmxs; A"-o"; Px cmxa])
end
end
in
let mk_camlp4_bin name modules =
let name = "camlp4"/name in
let byte = name-.-"byte" in
let native = name-.-"native" in
let deps = special_modules @ modules @ [camlp4_bin] in
let cmos = add_extensions [".cmo"] deps in
let cmxs = add_extensions [".cmx"] deps in
let objs = add_extensions [C.ext_obj] deps in
rule byte
~deps:(camlp4lib_cma::cmos)
~prod:(add_exe byte)
~insert:(`before "ocaml: cmo* -> byte")
begin fun _ _ ->
Cmd(S[!Options.ocamlc; T(tags_of_pathname byte++"ocaml"++"link"++"byte");
P camlp4lib_cma; A"-linkall"; atomize cmos; A"-o"; Px (add_exe byte)])
end;
rule native
~deps:(camlp4lib_cmxa :: camlp4lib_lib :: (cmxs @ objs))
~prod:(add_exe native)
~insert:(`before "ocaml: cmx* & o* -> native")
begin fun _ _ ->
Cmd(S[!Options.ocamlopt; T(tags_of_pathname native++"ocaml"++"link"++"native");
P camlp4lib_cmxa; A"-linkall"; atomize cmxs; A"-o"; Px (add_exe native)])
end
in
let mk_camlp4 name modules bin_mods top_mods =
mk_camlp4_bin name (modules @ bin_mods);
mk_camlp4_top_lib name (modules @ top_mods);
in
copy_rule "camlp4: boot/Camlp4Ast.ml -> Camlp4/Struct/Camlp4Ast.ml"
~insert:`top "camlp4/boot/Camlp4Ast.ml" "camlp4/Camlp4/Struct/Camlp4Ast.ml";
dep ["ocaml"; "file:camlp4/Camlp4/Sig.ml"]
["camlp4/Camlp4/Camlp4Ast.partial.ml"];
dep ["ocaml"; "compile"; "file:camlp4/Camlp4/Sig.ml"]
["camlp4/Camlp4/Camlp4Ast.partial.ml"];
mk_camlp4_bin "camlp4" [];
mk_camlp4 "camlp4boot"
[pa_r; pa_qc; pa_q; pa_rp; pa_g; pa_macro; pa_debug; pa_l] [pr_dump] [top_rprint];
mk_camlp4 "camlp4r"
[pa_r; pa_rp] [pr_a] [top_rprint];
mk_camlp4 "camlp4rf"
[pa_r; pa_qc; pa_q; pa_rp; pa_g; pa_macro; pa_l] [pr_a] [top_rprint];
mk_camlp4 "camlp4o"
[pa_r; pa_o; pa_rp; pa_op] [pr_a] [];
mk_camlp4 "camlp4of"
[pa_r; pa_qc; pa_q; pa_o; pa_rp; pa_op; pa_g; pa_macro; pa_l] [pr_a] [];
mk_camlp4 "camlp4oof"
[pa_r; pa_o; pa_rp; pa_op; pa_qc; pa_oq; pa_g; pa_macro; pa_l] [pr_a] [];
mk_camlp4 "camlp4orf"
[pa_r; pa_o; pa_rp; pa_op; pa_qc; pa_rq; pa_g; pa_macro; pa_l] [pr_a] []
| _ ->
()
end