| Description | When a .c or .h file uses '#include "foo.h"', ocamlbuild has to copy that file in the _build directory but currently it doesn't, making the build fail.
The attached patch (against today's svn) uses cpp to get these dependencies for .c and .h files: .c files are "allowed" to include .c and .h files while .h files can only include .h files.
The reason for this limitation is that the whole list would be: c, cpp, cxx, cp, C, h, hpp, hxx, hxp, H, CPP, HPP, c++, h++, tcc, hh, cc and I took a conservative approach.
The patch also changes Lexers.ocamldep_output to make it work with cpp's output (or ocamldep without -modules). |
| Attached Files | ocamlbuild-track-c-local-includes.patch [^] (7,186 bytes) 2010-07-18 12:59 [Show Content] [Hide Content]diff --git a/ocamlbuild/lexers.mll b/ocamlbuild/lexers.mll
index 7b191b0..56e7422 100644
--- a/ocamlbuild/lexers.mll
+++ b/ocamlbuild/lexers.mll
@@ -39,7 +39,18 @@ let variable = [ 'a'-'z' 'A'-'Z' '_' '-' '0'-'9' ]*
let pattern = ([^ '(' ')' '\\' ] | '\\' [ '(' ')' ])*
rule ocamldep_output = parse
- | ([^ ':' '\n' '\r' ]+ as k) ':' { let x = (k, space_sep_strings_nl lexbuf) in x :: ocamldep_output lexbuf }
+ | ([^ ':' '\n' '\r' ]+ as k) ':' {
+ let get_filename x =
+ let x = Filename.basename x in
+ try Filename.chop_extension x with Invalid_argument _ -> x
+ in
+ (* cpp outputs 'foo.o: foo.c bar.h': remove 'foo.c' to prevent a cycle *)
+ match space_sep_strings_nl lexbuf with
+ | h :: t when get_filename k = get_filename h ->
+ (* cpp includes the file extension while ocamldep doesn't *)
+ (k, List.map get_filename t) :: ocamldep_output lexbuf
+ | deps -> (k, deps) :: ocamldep_output lexbuf
+ }
| eof { [] }
| _ { raise (Error "Expecting colon followed by space-separated module name list") }
diff --git a/ocamlbuild/ocaml_compiler.ml b/ocamlbuild/ocaml_compiler.ml
index e4d168b..4f791b1 100644
--- a/ocamlbuild/ocaml_compiler.ml
+++ b/ocamlbuild/ocaml_compiler.ml
@@ -105,12 +105,12 @@ let native_shared_lib_linker tags =
let native_lib_linker_tags tags = tags++"ocaml"++"link"++"native"++"library"
-let prepare_compile build ml =
- let dir = Pathname.dirname ml in
+let prepare_compile ?(exts=["cmi"]) build ml_or_c =
+ let dir = Pathname.dirname ml_or_c in
let include_dirs = Pathname.include_dirs_of dir in
- let modules = path_dependencies_of ml in
+ let modules = path_dependencies_of ml_or_c in
let results =
- build (List.map (fun (_, x) -> expand_module include_dirs x ["cmi"]) modules) in
+ build (List.map (fun (_, x) -> expand_module include_dirs x exts) modules) in
List.iter2 begin fun (mandatory, name) res ->
match mandatory, res with
| _, Good _ -> ()
diff --git a/ocamlbuild/ocaml_compiler.mli b/ocamlbuild/ocaml_compiler.mli
index 608f033..2b99294 100644
--- a/ocamlbuild/ocaml_compiler.mli
+++ b/ocamlbuild/ocaml_compiler.mli
@@ -24,7 +24,7 @@ val ocamlopt_link_prog : Tags.t -> Pathname.t list -> Pathname.t -> Command.t
val ocamlopt_p : Tags.t -> Pathname.t list -> Pathname.t -> Command.t
val ocamlmklib : Tags.t -> Pathname.t list -> Pathname.t -> Command.t
val ocamlmktop : Tags.t -> Pathname.t list -> Pathname.t -> Command.t
-val prepare_compile : Rule.builder -> Pathname.t -> unit
+val prepare_compile : ?exts:string list -> Rule.builder -> Pathname.t -> unit
val byte_compile_ocaml_interf : string -> string -> Rule.action
val byte_compile_ocaml_implem : ?tag:string -> string -> string -> Rule.action
val prepare_link :
diff --git a/ocamlbuild/ocaml_specific.ml b/ocamlbuild/ocaml_specific.ml
index 500cacf..d88accd 100644
--- a/ocamlbuild/ocaml_specific.ml
+++ b/ocamlbuild/ocaml_specific.ml
@@ -336,12 +336,33 @@ end else
~dep:"%.mly"
(Ocaml_tools.ocamlyacc "%.mly");;
+(* NOTE: creating a precompiled-header might be better than using a virtual rule
+ * but these aren't supported everywhere (gcc <= 3.3 and some sun compilers *)
+rule "ocaml C stubs dependencies: h -> h.virtual_rule"
+ ~dep:"%.h.depends"
+ ~stamp:"%.h.virtual_rule"
+ begin fun env build ->
+ Ocaml_compiler.prepare_compile ~exts:["h.virtual_rule"] build (env "%.h");
+ Nop
+ end;;
+
+rule "ocaml C stubs dependencies: h -> h.depends"
+ ~prod:"%.h.depends"
+ ~dep:"%.h"
+ (Ocaml_tools.cppdep_command "%.h" "%.h.depends");;
+
+rule "ocaml C stubs dependencies: c -> c.depends"
+ ~prod:"%.c.depends"
+ ~dep:"%.c"
+ (Ocaml_tools.cppdep_command "%.c" "%.c.depends");;
+
rule "ocaml C stubs: c -> o"
~prod:x_o
- ~dep:"%.c"
- begin fun env _build ->
+ ~deps:["%.c"; "%.c.depends"]
+ begin fun env build ->
let c = env "%.c" in
let o = env x_o in
+ Ocaml_compiler.prepare_compile ~exts:["h.virtual_rule"; "c"] build c;
let comp = if Tags.mem "native" (tags_of_pathname c) then !Options.ocamlopt else !Options.ocamlc in
let cc = Cmd(S[comp; T(tags_of_pathname c++"c"++"compile"); A"-c"; Px c]) in
if Pathname.dirname o = Pathname.current_dir_name then cc
diff --git a/ocamlbuild/ocaml_tools.ml b/ocamlbuild/ocaml_tools.ml
index f66c127..dad9afe 100644
--- a/ocamlbuild/ocaml_tools.ml
+++ b/ocamlbuild/ocaml_tools.ml
@@ -24,6 +24,11 @@ let ocamldep_command' tags =
let tags' = tags++"ocaml"++"ocamldep" in
S [!Options.ocamldep; T tags'; ocaml_ppflags (tags++"pp:dep"); A "-modules"]
+(* TODO: msvc-compatible version with something like 'cl /Zs /showIncludes' *)
+let cppdep_command' tags =
+ let tags' = tags++"ocaml"++"cpp" in
+ S [!Options.cppdep; T tags'; A "-P"; A "-MM"; A "-MG"]
+
let menhir_ocamldep_command' tags ~menhir_spec out =
let menhir = if !Options.ocamlyacc = N then V"MENHIR" else !Options.ocamlyacc in
Cmd(S[menhir; T tags; A"--raw-depend";
@@ -79,6 +84,11 @@ let ocamldep_command arg out env _build =
let tags = tags_of_pathname arg in
Cmd(S[ocamldep_command' tags; P arg; Sh ">"; Px out])
+let cppdep_command arg out env _build =
+ let arg = env arg and out = env out in
+ let tags = tags_of_pathname arg in
+ Cmd(S[cppdep_command' tags; P arg; Sh ">"; Px out])
+
let ocamlyacc mly env _build =
let mly = env mly in
let ocamlyacc = if !Options.ocamlyacc = N then V"OCAMLYACC" else !Options.ocamlyacc in
diff --git a/ocamlbuild/ocaml_tools.mli b/ocamlbuild/ocaml_tools.mli
index 542573d..079e092 100644
--- a/ocamlbuild/ocaml_tools.mli
+++ b/ocamlbuild/ocaml_tools.mli
@@ -17,6 +17,7 @@ val ocamldoc_l_dir : Tags.t -> string list -> string -> string -> Command.t
val ocamldoc_l_file : Tags.t -> string list -> string -> string -> Command.t
val ocamldep_command : string -> string -> Rule.action
+val cppdep_command : string -> string -> Rule.action
val menhir_ocamldep_command : string -> string -> Rule.action
val menhir_modular_ocamldep_command : string -> string -> Rule.action
val menhir_modular : string -> string -> string -> Rule.action
diff --git a/ocamlbuild/options.ml b/ocamlbuild/options.ml
index 0256d43..a7d828f 100644
--- a/ocamlbuild/options.ml
+++ b/ocamlbuild/options.ml
@@ -62,11 +62,12 @@ let mk_virtual_solvers =
let () =
mk_virtual_solvers
- ["ocamlc"; "ocamlopt"; "ocamldep"; "ocamldoc";
+ ["ocamlc"; "ocamlopt"; "ocamldep"; "cpp"; "ocamldoc";
"ocamlyacc"; "menhir"; "ocamllex"; "ocamlmklib"; "ocamlmktop"; "ocamlfind"]
let ocamlc = ref (V"OCAMLC")
let ocamlopt = ref (V"OCAMLOPT")
let ocamldep = ref (V"OCAMLDEP")
+let cppdep = ref (V"CPP")
let ocamldoc = ref (V"OCAMLDOC")
let ocamlyacc = ref N
let ocamllex = ref (V"OCAMLLEX")
diff --git a/ocamlbuild/signatures.mli b/ocamlbuild/signatures.mli
index cb343bd..bcf8436 100644
--- a/ocamlbuild/signatures.mli
+++ b/ocamlbuild/signatures.mli
@@ -359,6 +359,7 @@ module type OPTIONS = sig
val ocamlc : command_spec ref
val ocamlopt : command_spec ref
val ocamldep : command_spec ref
+ val cppdep : command_spec ref
val ocamldoc : command_spec ref
val ocamlyacc : command_spec ref
val ocamllex : command_spec ref
ocamlbuild-track-c-local-includes2_multiline.patch [^] (7,667 bytes) 2010-07-19 23:00 [Show Content] [Hide Content]diff --git a/ocamlbuild/lexers.mll b/ocamlbuild/lexers.mll
index 7b191b0..c77b63a 100644
--- a/ocamlbuild/lexers.mll
+++ b/ocamlbuild/lexers.mll
@@ -39,14 +39,26 @@ let variable = [ 'a'-'z' 'A'-'Z' '_' '-' '0'-'9' ]*
let pattern = ([^ '(' ')' '\\' ] | '\\' [ '(' ')' ])*
rule ocamldep_output = parse
- | ([^ ':' '\n' '\r' ]+ as k) ':' { let x = (k, space_sep_strings_nl lexbuf) in x :: ocamldep_output lexbuf }
+ | ([^ ':' '\n' '\r' ]+ as k) ':' {
+ let get_filename x =
+ let x = Filename.basename x in
+ try Filename.chop_extension x with Invalid_argument _ -> x
+ in
+ (* cpp outputs 'foo.o: foo.c bar.h': remove 'foo.c' to prevent a cycle *)
+ match space_sep_strings_nl lexbuf with
+ | h :: t when get_filename k = get_filename h ->
+ (* cpp includes the file extension while ocamldep doesn't *)
+ (k, List.map get_filename t) :: ocamldep_output lexbuf
+ | deps -> (k, deps) :: ocamldep_output lexbuf
+ }
| eof { [] }
| _ { raise (Error "Expecting colon followed by space-separated module name list") }
and space_sep_strings_nl = parse
+ | space* '\\' space* newline { space_sep_strings_nl lexbuf }
| space* (not_blank+ as word) { word :: space_sep_strings_nl lexbuf }
| space* newline { [] }
- | _ { raise (Error "Expecting space-separated strings terminated with newline") }
+ | _ { raise (Error "Expecting space-separated strings terminated with unescaped newline") }
and space_sep_strings = parse
| space* (not_blank+ as word) { word :: space_sep_strings lexbuf }
diff --git a/ocamlbuild/ocaml_compiler.ml b/ocamlbuild/ocaml_compiler.ml
index e4d168b..4f791b1 100644
--- a/ocamlbuild/ocaml_compiler.ml
+++ b/ocamlbuild/ocaml_compiler.ml
@@ -105,12 +105,12 @@ let native_shared_lib_linker tags =
let native_lib_linker_tags tags = tags++"ocaml"++"link"++"native"++"library"
-let prepare_compile build ml =
- let dir = Pathname.dirname ml in
+let prepare_compile ?(exts=["cmi"]) build ml_or_c =
+ let dir = Pathname.dirname ml_or_c in
let include_dirs = Pathname.include_dirs_of dir in
- let modules = path_dependencies_of ml in
+ let modules = path_dependencies_of ml_or_c in
let results =
- build (List.map (fun (_, x) -> expand_module include_dirs x ["cmi"]) modules) in
+ build (List.map (fun (_, x) -> expand_module include_dirs x exts) modules) in
List.iter2 begin fun (mandatory, name) res ->
match mandatory, res with
| _, Good _ -> ()
diff --git a/ocamlbuild/ocaml_compiler.mli b/ocamlbuild/ocaml_compiler.mli
index 608f033..2b99294 100644
--- a/ocamlbuild/ocaml_compiler.mli
+++ b/ocamlbuild/ocaml_compiler.mli
@@ -24,7 +24,7 @@ val ocamlopt_link_prog : Tags.t -> Pathname.t list -> Pathname.t -> Command.t
val ocamlopt_p : Tags.t -> Pathname.t list -> Pathname.t -> Command.t
val ocamlmklib : Tags.t -> Pathname.t list -> Pathname.t -> Command.t
val ocamlmktop : Tags.t -> Pathname.t list -> Pathname.t -> Command.t
-val prepare_compile : Rule.builder -> Pathname.t -> unit
+val prepare_compile : ?exts:string list -> Rule.builder -> Pathname.t -> unit
val byte_compile_ocaml_interf : string -> string -> Rule.action
val byte_compile_ocaml_implem : ?tag:string -> string -> string -> Rule.action
val prepare_link :
diff --git a/ocamlbuild/ocaml_specific.ml b/ocamlbuild/ocaml_specific.ml
index 500cacf..d88accd 100644
--- a/ocamlbuild/ocaml_specific.ml
+++ b/ocamlbuild/ocaml_specific.ml
@@ -336,12 +336,33 @@ end else
~dep:"%.mly"
(Ocaml_tools.ocamlyacc "%.mly");;
+(* NOTE: creating a precompiled-header might be better than using a virtual rule
+ * but these aren't supported everywhere (gcc <= 3.3 and some sun compilers *)
+rule "ocaml C stubs dependencies: h -> h.virtual_rule"
+ ~dep:"%.h.depends"
+ ~stamp:"%.h.virtual_rule"
+ begin fun env build ->
+ Ocaml_compiler.prepare_compile ~exts:["h.virtual_rule"] build (env "%.h");
+ Nop
+ end;;
+
+rule "ocaml C stubs dependencies: h -> h.depends"
+ ~prod:"%.h.depends"
+ ~dep:"%.h"
+ (Ocaml_tools.cppdep_command "%.h" "%.h.depends");;
+
+rule "ocaml C stubs dependencies: c -> c.depends"
+ ~prod:"%.c.depends"
+ ~dep:"%.c"
+ (Ocaml_tools.cppdep_command "%.c" "%.c.depends");;
+
rule "ocaml C stubs: c -> o"
~prod:x_o
- ~dep:"%.c"
- begin fun env _build ->
+ ~deps:["%.c"; "%.c.depends"]
+ begin fun env build ->
let c = env "%.c" in
let o = env x_o in
+ Ocaml_compiler.prepare_compile ~exts:["h.virtual_rule"; "c"] build c;
let comp = if Tags.mem "native" (tags_of_pathname c) then !Options.ocamlopt else !Options.ocamlc in
let cc = Cmd(S[comp; T(tags_of_pathname c++"c"++"compile"); A"-c"; Px c]) in
if Pathname.dirname o = Pathname.current_dir_name then cc
diff --git a/ocamlbuild/ocaml_tools.ml b/ocamlbuild/ocaml_tools.ml
index f66c127..024861f 100644
--- a/ocamlbuild/ocaml_tools.ml
+++ b/ocamlbuild/ocaml_tools.ml
@@ -24,6 +24,11 @@ let ocamldep_command' tags =
let tags' = tags++"ocaml"++"ocamldep" in
S [!Options.ocamldep; T tags'; ocaml_ppflags (tags++"pp:dep"); A "-modules"]
+(* TODO: msvc-compatible version with something like 'cl /Zs /showIncludes' *)
+let cppdep_command' tags =
+ let tags' = tags++"ocaml"++"cpp" in
+ S [!Options.cppdep; T tags'; A "-P"; A "-MM"; A "-MG"]
+
let menhir_ocamldep_command' tags ~menhir_spec out =
let menhir = if !Options.ocamlyacc = N then V"MENHIR" else !Options.ocamlyacc in
Cmd(S[menhir; T tags; A"--raw-depend";
@@ -79,6 +84,11 @@ let ocamldep_command arg out env _build =
let tags = tags_of_pathname arg in
Cmd(S[ocamldep_command' tags; P arg; Sh ">"; Px out])
+let cppdep_command arg out env _build =
+ let arg = env arg and out = env out in
+ let tags = tags_of_pathname arg in
+ Cmd(S[cppdep_command' tags; P arg; Sh ">"; Px out])
+
let ocamlyacc mly env _build =
let mly = env mly in
let ocamlyacc = if !Options.ocamlyacc = N then V"OCAMLYACC" else !Options.ocamlyacc in
diff --git a/ocamlbuild/ocaml_tools.mli b/ocamlbuild/ocaml_tools.mli
index 542573d..079e092 100644
--- a/ocamlbuild/ocaml_tools.mli
+++ b/ocamlbuild/ocaml_tools.mli
@@ -17,6 +17,7 @@ val ocamldoc_l_dir : Tags.t -> string list -> string -> string -> Command.t
val ocamldoc_l_file : Tags.t -> string list -> string -> string -> Command.t
val ocamldep_command : string -> string -> Rule.action
+val cppdep_command : string -> string -> Rule.action
val menhir_ocamldep_command : string -> string -> Rule.action
val menhir_modular_ocamldep_command : string -> string -> Rule.action
val menhir_modular : string -> string -> string -> Rule.action
diff --git a/ocamlbuild/options.ml b/ocamlbuild/options.ml
index 0256d43..a7d828f 100644
--- a/ocamlbuild/options.ml
+++ b/ocamlbuild/options.ml
@@ -62,11 +62,12 @@ let mk_virtual_solvers =
let () =
mk_virtual_solvers
- ["ocamlc"; "ocamlopt"; "ocamldep"; "ocamldoc";
+ ["ocamlc"; "ocamlopt"; "ocamldep"; "cpp"; "ocamldoc";
"ocamlyacc"; "menhir"; "ocamllex"; "ocamlmklib"; "ocamlmktop"; "ocamlfind"]
let ocamlc = ref (V"OCAMLC")
let ocamlopt = ref (V"OCAMLOPT")
let ocamldep = ref (V"OCAMLDEP")
+let cppdep = ref (V"CPP")
let ocamldoc = ref (V"OCAMLDOC")
let ocamlyacc = ref N
let ocamllex = ref (V"OCAMLLEX")
diff --git a/ocamlbuild/signatures.mli b/ocamlbuild/signatures.mli
index cb343bd..bcf8436 100644
--- a/ocamlbuild/signatures.mli
+++ b/ocamlbuild/signatures.mli
@@ -359,6 +359,7 @@ module type OPTIONS = sig
val ocamlc : command_spec ref
val ocamlopt : command_spec ref
val ocamldep : command_spec ref
+ val cppdep : command_spec ref
val ocamldoc : command_spec ref
val ocamlyacc : command_spec ref
val ocamllex : command_spec ref
|