1 (***********************************************************************)
4 (* Nicolas Pouillard, Berke Durak, projet Gallium, INRIA Rocquencourt *)
6 (* Copyright 2007 Institut National de Recherche en Informatique et *)
7 (* en Automatique. All rights reserved. This file is distributed *)
8 (* under the terms of the Q Public License version 1.0. *)
10 (***********************************************************************)
13 (* Original author: Berke Durak *)
16 open Pathname.Operators
23 exception Exit_build_error of string
24 exception Exit_silently
28 Shell.rm_rf !Options.build_dir;
29 if !Options.make_links then begin
31 Slurp.map (fun _ _ _ -> true)
32 (Slurp.slurp Filename.current_dir_name)
34 Slurp.force (Resource.clean_up_links entry)
40 List.iter begin fun path ->
41 Log.eprintf "@[<2>Tags for %S:@ {. %a .}@]" path Tags.print (tags_of_pathname path)
42 end !Options.show_tags
45 let show_documentation () =
46 let rules = Rule.get_rules () in
47 let flags = Flags.get_flags () in
48 let pp fmt = Log.raw_dprintf (-1) fmt in
49 List.iter begin fun rule ->
50 pp "%a@\n@\n" (Rule.pretty_print Resource.print_pattern) rule
52 List.iter begin fun (tags, flag) ->
53 let sflag = Command.string_of_command_spec flag in
54 pp "@[<2>flag@ {. %a .}@ %S@]@\n@\n" Tags.print tags sflag
60 Hooks.call_hook Hooks.Before_options;
62 if !Options.must_clean then clean ();
63 Hooks.call_hook Hooks.After_options;
64 Plugin.execute_plugin_if_needed ();
66 if !Options.targets = []
67 && !Options.show_tags = []
68 && not !Options.show_documentation
69 then raise Exit_silently;
71 let target_dirs = List.union [] (List.map Pathname.dirname !Options.targets) in
73 Configuration.parse_string
74 "<**/*.ml> or <**/*.mli> or <**/*.mlpack> or <**/*.ml.depends>: ocaml
75 <**/*.byte>: ocaml, byte, program
76 <**/*.odoc>: ocaml, doc
77 <**/*.native>: ocaml, native, program
78 <**/*.cma>: ocaml, byte, library
79 <**/*.cmxa>: ocaml, native, library
80 <**/*.cmo>: ocaml, byte
81 <**/*.cmi>: ocaml, byte, native
82 <**/*.cmx>: ocaml, native
85 Configuration.tag_any !Options.tags;
87 || Sys.file_exists (* authorized since we're not in build *) "_tags"
88 || Sys.file_exists (* authorized since we're not in build *) "myocamlbuild.ml"
89 then Configuration.tag_any ["traverse"];
91 let newpwd = Sys.getcwd () in
92 Sys.chdir Pathname.pwd;
93 let entry_include_dirs = ref [] in
96 begin fun path name _ ->
98 if path = Filename.current_dir_name then
103 let path_name = path/name in
104 if name = "_tags" then
105 ignore (Configuration.parse_file ?dir path_name);
107 (String.length name > 0 && name.[0] <> '_' && name <> !Options.build_dir && not (List.mem name !Options.exclude_dirs))
109 if path_name <> Filename.current_dir_name && Pathname.is_directory path_name then
110 let tags = tags_of_pathname path_name in
111 if Tags.mem "include" tags
112 || List.mem path_name !Options.include_dirs then
113 (entry_include_dirs := path_name :: !entry_include_dirs; true)
115 Tags.mem "traverse" tags
116 || List.exists (Pathname.is_prefix path_name) !Options.include_dirs
117 || List.exists (Pathname.is_prefix path_name) target_dirs
121 (Slurp.slurp Filename.current_dir_name)
123 Hooks.call_hook Hooks.Before_hygiene;
125 Slurp.map begin fun path name () ->
126 let tags = tags_of_pathname (path/name) in
127 not (Tags.mem "not_hygienic" tags) && not (Tags.mem "precious" tags)
129 if !Options.hygiene then
130 Fda.inspect hygiene_entry
132 Slurp.force hygiene_entry;
133 let entry = hygiene_entry in
134 Hooks.call_hook Hooks.After_hygiene;
135 Options.include_dirs := Pathname.current_dir_name :: List.rev !entry_include_dirs;
136 dprintf 3 "include directories are:@ %a" print_string_list !Options.include_dirs;
137 Options.entry := Some entry;
139 List.iter Configuration.parse_string !Options.tag_lines;
141 Hooks.call_hook Hooks.Before_rules;
142 Ocaml_specific.init ();
143 Hooks.call_hook Hooks.After_rules;
146 (*let () = dprintf 0 "source_dir_path_set:@ %a" StringSet.print source_dir_path_set*)
148 if !Options.show_documentation then begin
149 show_documentation ();
152 Digest_cache.init ();
154 Sys.catch_break true;
159 List.map begin fun starget ->
160 let starget = Resource.import starget in
161 let target = path_and_context_of_string starget in
162 let ext = Pathname.get_extension starget in
163 (target, starget, ext)
164 end !Options.targets in
168 List.map begin fun (target, starget, ext) ->
169 Shell.mkdir_p (Pathname.dirname starget);
170 let target = Solver.solve_target starget target in
174 Command.dump_parallel_stats ();
178 Shell.chdir Pathname.pwd;
180 let call spec = sys_command (Command.string_of_command_spec spec) in
183 List.fold_right begin fun (target, ext) acc ->
184 let cmd = !Options.build_dir/target in
186 if !Options.make_links then ignore (call (S [A"ln"; A"-sf"; P x; A Pathname.current_dir_name])) in
188 | "byte" | "native" | "top" ->
191 link (Pathname.dirname cmd); acc
193 if !Options.program_to_execute then
194 eprintf "Warning: Won't execute %s whose extension is neither .byte nor .native" cmd;
198 if !Options.program_to_execute then
200 match List.rev cmds with
201 | [] -> raise (Exit_usage "Using -- requires one target");
203 if rest <> [] then dprintf 0 "Warning: Using -- only run the last target";
204 let cmd_spec = S [P cmd; atomize !Options.program_args] in
205 dprintf 3 "Running the user command:@ %a" Pathname.print cmd;
206 raise (Exit_with_code (call cmd_spec)) (* Exit with the exit code of the called command *)
211 | Ocaml_dependencies.Circular_dependencies(seen, p) ->
214 (sbprintf "@[<2>Circular dependencies: %S already seen in@ %a@]@." p pp_l seen))
221 Log.finish ~how:(if rc <> 0 then `Error else `Success) ();
227 if !Options.catch_errors then
229 | Exit_OK -> exit rc_ok
230 | Fda.Exit_hygiene_failed ->
231 Log.eprintf "Exiting due to hygiene violations.";
234 Log.eprintf "Usage:@ %s." u;
236 | Exit_system_error msg ->
237 Log.eprintf "System error:@ %s." msg;
239 | Exit_with_code rc ->
242 Log.finish ~how:`Quiet ();
243 Pervasives.exit rc_ok
244 | Exit_silently_with_code rc ->
245 Log.finish ~how:`Quiet ();
247 | Solver.Failed backtrace ->
248 Log.raw_dprintf (-1) "@[<v0>@[<2>Solver failed:@ %a@]@\n@[<v2>Backtrace:%a@]@]@."
249 Report.print_backtrace_analyze backtrace Report.print_backtrace backtrace;
250 exit rc_solver_failed
252 Log.eprintf "Failure:@ %s." s;
254 | Solver.Circular(r, rs) ->
255 Log.eprintf "Circular build detected@ (%a already seen in %a)"
256 Resource.print r (List.print Resource.print) rs;
258 | Invalid_argument s ->
260 "INTERNAL ERROR: Invalid argument %s\n\
261 This is likely to be a bug, please report this to the ocamlbuild\n\
263 exit rc_invalid_argument
264 | Ocaml_utils.Ocamldep_error msg ->
265 Log.eprintf "Ocamldep error: %s" msg;
266 exit rc_ocamldep_error
267 | Lexers.Error msg ->
268 Log.eprintf "Lexical analysis error: %s" msg;
271 Log.eprintf "%s" msg;
273 | Exit_build_error msg ->
274 Log.eprintf "%s" msg;
277 Log.eprintf "%s" msg;
281 Log.eprintf "%a" My_unix.report_error e;
285 Log.eprintf "Exception@ %s." (Printexc.to_string e);