permit multiline comments and strings in macros
[bpt/coccinelle.git] / myocamlbuild.ml.in
1 # 2 "myocamlbuild.ml.in"
2
3 (*
4 * This file is a plugin that provides the needed customization of
5 * calls to the ocaml compiler needed for components of coccinelle.
6 * The classification of particular components is done by tags, which
7 * are specified in the _tags file.
8 *
9 * This file is also a compromise: some aspects of coccocinelle's
10 * build process are somehwat complicated due to packaging some
11 * bundled software, having no requirement on ocamlfind, etc.
12 * We therefore let 'configure' find out the configuration and
13 * paths to tools and libraries, and this plugin is transformed
14 * by that configuration to customize ocamlbuild accordingly.
15 *)
16
17 (* Some useful commandline arguments to ocamlbuild are:
18 * -yaccflag -v verbose ocamlyacc and menhir output
19 * -classic-display see the individual build steps
20 * -j 0 parallel building
21 * -tag "-custom" pure bytecode building
22 * -tag "-dtypes" no type annotation generation
23 *)
24
25
26 (* Configuration of this build plugin
27 *)
28
29 let ocamlc_path = "@OCAMLC@"
30 let ocamlopt_path = "@OCAMLOPT@"
31 let ocamldep_path = "@OCAMLDEP@"
32 let ocamldoc_path = "@OCAMLDOC@"
33 let ocamlyacc_path = "@OCAMLYACC@"
34 let ocamllex_path = "@OCAMLLEX@"
35 let ocamlmklib_path = "@OCAMLMKLIB@"
36 let ocamlmktop_path = "@OCAMLMKTOP@"
37 let camlp4o_path = "@CAMLP4O@"
38 let menhir_path = "@MENHIR@"
39
40 let pycaml_path = "@PATH_pycaml@"
41 let pcre_path = "@PATH_pcre@"
42 let menhirLib_path = "@PATH_menhirLib@"
43 let dynlink_path = "@PATH_dynlink@"
44
45 let pcre_cflags = "@PCRE_CFLAGS@"
46 let pcre_ldflags = "@PCRE_LIBS@"
47 let python_cflags = "@PYTHON_CFLAGS@"
48 let python_ldflags = "@PYTHON_LIBS@"
49 let python_major_version = "@PYVER_MAJOR@"
50
51 let profiling_modules = "@MODULES_profiling@"
52
53
54 (* The plugin code starts here. *)
55 open Ocamlbuild_plugin
56 open Command
57
58 (* Removes double separators and single dots from
59 * a path. It does not resolve symlinks or turn
60 * relative paths in absolute paths.
61 *)
62 let rec normalize_path path =
63 let parent = Pathname.dirname path in
64 if Pathname.equal path "/" || Pathname.equal parent "/" || Pathname.equal parent path
65 then path
66 else let name = Pathname.basename path in
67 if Pathname.equal name "."
68 then normalize_path parent
69 else normalize_path parent / name
70
71 (* Makes path relative and implicit, if it is a child of the
72 * current directory. Relative paths are a must when dealing
73 * with the build directory.
74 * Todo: find out if there is a library function for
75 * exactly this purpose.
76 *)
77 let relative_path path =
78 let current = normalize_path Pathname.pwd in
79 let target = normalize_path path in
80 if Pathname.is_prefix current target
81 then let len_current = String.length current in
82 let len_target = String.length target in
83 if len_current == len_target
84 then "."
85 else let len_tail = len_target - len_current - 1 in
86 let ind_tail = len_current + 1 in
87 String.sub target ind_tail len_tail
88 else target
89
90 let add_flags flag_ref flags =
91 flag_ref := List.append flags !flag_ref
92
93
94 let mk_use_tag name = "use_" ^ name
95
96
97 (* Sets up a tag for compiling c and library files against
98 * an external c library.
99 *)
100 let setup_clib name compile_flags link_flags =
101 let tag = mk_use_tag name in
102 flag [tag; "c"; "compile"] (S[A "-ccopt"; A compile_flags]);
103 flag [tag; "c"; "ocamlmklib"] (S[A "-ldopt"; A link_flags]);
104 flag [tag; "ocaml"; "link"] (S[A "-ccopt"; A compile_flags]);
105 flag [tag; "ocaml"; "link"] (S[A "-ccopt"; A link_flags])
106
107 (* Sets up a tag for declaring a dependency on a stubs library,
108 * and linking it in. The dependency includes both a .a archive
109 * and a .so dll.
110 *)
111 let setup_stubs name stubs_dir =
112 let tag = mk_use_tag name in
113 let path_a = Printf.sprintf "%s/lib%s_stubs.a" stubs_dir name in
114 if not (Pathname.exists path_a) then
115 dep [tag; "link"; "ocaml"] [path_a];
116 let stubs_arg = Printf.sprintf "-l%s_stubs" name in
117 flag [tag; "ocaml"; "link"; "byte"]
118 (S[A "-I"; P stubs_dir; A "-dllib"; A stubs_arg; A "-cclib"; A stubs_arg]);
119 flag [tag; "ocaml"; "link"; "native"]
120 (S[A "-I"; P stubs_dir; A "-cclib"; A stubs_arg]);
121 flag [tag; "ocaml"; "doc"]
122 (S[A "-I"; P stubs_dir])
123
124 (* The use of bundled software is simply the
125 * inclusion of the appropriate source directory.
126 * The build system can find automatically how to
127 * deal with the bundled sources.
128 *)
129 let setup_bundle rootdir =
130 tag_file rootdir ["include"; "traverse"]
131
132 (* Sets up a tag that adds the given module directory and module
133 * as additional argument to ocaml when it processes a
134 * file with that tag.
135 * Todo: it may be beneficial to add a dependency on the target
136 * module.
137 *)
138 let setup_module name modname rootdir =
139 let tag = mk_use_tag name in
140 let link_args isNative = S [A "-I"; P rootdir; A (modname isNative) ] in
141 let compile_args = S [A "-I"; P rootdir] in
142 flag [tag; "ocaml"; "compile"] compile_args;
143 flag [tag; "ocaml"; "byte"; "link"; "program"] (link_args false);
144 flag [tag; "ocaml"; "native"; "link"; "program"] (link_args true);
145 flag [tag; "ocaml"; "doc"] (S[A "-I"; P rootdir])
146
147 (* Sets up the use of either a bundled source package or precompiled module. *)
148 let setup_package name modname rootdir =
149 let exists_path isNative = Pathname.exists (rootdir / modname isNative) in
150 let is_binary = exists_path false || exists_path true in
151 if is_binary
152 then setup_module name modname rootdir
153 else setup_bundle rootdir
154
155
156 (* Most files depend on these standard modules, hence we setup a
157 * single tag for them.
158 * This setup routine should be called before the others to ensure
159 * that these modules appear first on the ocaml commandlines.
160 *)
161 let setup_basic_libs use_dynlink =
162 ocaml_lib ~extern:true ~tag_name:"use_base" "unix";
163 ocaml_lib ~extern:true ~tag_name:"use_base" "str";
164 ocaml_lib ~extern:true ~tag_name:"use_base" "nums";
165 ocaml_lib ~extern:true ~tag_name:"use_base" "bigarray";
166 if use_dynlink then
167 ocaml_lib ~extern:true ~tag_name:"use_base" "dynlink";
168 ()
169
170 (* The menhir package provides individual object files
171 * instead of an archive.
172 *)
173 let setup_menhirLib () =
174 let menhirLib_dir = relative_path menhirLib_path in
175 let modname isNative = match isNative with
176 true -> "menhirLib.cmx"
177 | false -> "menhirLib.cmo" in
178
179 setup_package "menhirLib" modname menhirLib_dir
180
181 (* Pycaml is a stubs library with some conditional
182 * code that depends on the python version. We
183 * additionally introduce a tag pp_pycaml which
184 * runs the appropriate preprocessors.
185 *)
186 let setup_pycaml () =
187 let pycaml_dir = relative_path pycaml_path in
188 let modname isNative = match isNative with
189 true -> "pycaml.cmxa"
190 | false -> "pycaml.cma" in
191
192 setup_package "pycaml" modname pycaml_dir;
193 setup_stubs "pycaml" pycaml_dir;
194 setup_clib "pycaml" python_cflags python_ldflags;
195
196 let macrodef = Printf.sprintf "-D PYMAJOR%s" python_major_version in
197 flag ["pp_pycaml"; "c"; "compile"] (S[A "-ccopt"; A macrodef]);
198 let camlp4cmd = Printf.sprintf "%s -parser Camlp4MacroParser -D PYMAJOR%s"
199 camlp4o_path python_major_version in
200 flag ["pp_pycaml"; "ocaml"; "pp"] (Sh camlp4cmd)
201
202 (* Pcre is a standard stub library. *)
203 let setup_pcre () =
204 let pcre_dir = relative_path pcre_path in
205 let modname isNative = match isNative with
206 true -> "pcre.cmxa"
207 | false -> "pcre.cma" in
208
209 setup_package "pcre" modname pcre_dir;
210 setup_stubs "pcre" pcre_dir;
211 setup_clib "pcre" pcre_cflags pcre_ldflags
212
213 (* Some utility code on strings and paths. *)
214 let any_non_space str =
215 let have_non_space = ref false in
216 String.iter begin
217 fun c -> match c with
218 ' ' -> ()
219 | '\t' -> ()
220 | _ -> have_non_space := true
221 end str;
222 !have_non_space
223
224 let not_empty str =
225 String.length str > 0 && any_non_space str
226
227 let is_path_configured path =
228 not_empty path && Pathname.exists path
229
230 (* Note: the setup of the modules is done before the hygiene phase
231 * in order to benefit from additional "include" tags that may be
232 * given to directories.
233 *)
234 let _ = dispatch begin
235 function
236 | Before_options ->
237 Options.hygiene := true;
238 Options.sanitize := true;
239 Options.make_links := false;
240 Options.catch_errors := true;
241 Options.use_menhir := true;
242
243 let menhir_wrapper = Printf.sprintf
244 "%s/setup/wrapper-menhir.sh" Pathname.pwd in
245 Options.ocamlc := Sh ocamlc_path;
246 Options.ocamlopt := Sh ocamlopt_path;
247 Options.ocamldep := Sh ocamldep_path;
248 Options.ocamldoc := Sh ocamldoc_path;
249 Options.ocamlyacc := S[P menhir_wrapper; P ocamlyacc_path; P menhir_path];
250 Options.ocamllex := Sh ocamllex_path;
251 Options.ocamlmklib := Sh ocamlmklib_path;
252 Options.ocamlmktop := Sh ocamlmktop_path;
253 ()
254
255 | Before_hygiene ->
256 let use_dynlink = is_path_configured dynlink_path in
257 setup_basic_libs use_dynlink;
258
259 if is_path_configured menhirLib_path then
260 setup_menhirLib ();
261
262 if is_path_configured pcre_path then
263 setup_pcre ();
264
265 if is_path_configured pycaml_path then
266 setup_pycaml ();
267 ()
268
269 | After_rules ->
270 (* produces a slightly faster native version *)
271 (* flag ["ocaml"; "compile"; "native"] (A "-unsafe"); *)
272
273 (* adds debugging info (including exception backtraces) *)
274 flag ["ocaml"; "compile"] (A "-g");
275
276 (* flags to parameterize ocamldoc to produce web pages *)
277 flag ["gen_html"; "ocaml"; "doc"]
278 (S [A "-colorize-code"; A "-short-functors"; A "-all-params"]);
279 flag ["gen_man"; "ocaml"; "doc"]
280 (S [A "-man"; A "-man-mini"]);
281
282 (* when profiling, link with profiling.cmo *)
283 if not_empty profiling_modules then
284 flag ["ocaml"; "link"; "byte"]
285 (S [A profiling_modules]);
286
287 (* the warning about unused function arguments are disabled
288 * for files with this tag. *)
289 flag ["nowarn20"; "ocaml"; "compile"] (S [A "-w"; A "-20"]);
290
291 (* adds the custom option, unless 'nocustom' is given as a tag *)
292 if not (Tags.mem "nocustom" (tags_of_pathname "myocamlbuild.ml"))
293 then flag ["ocaml"; "link"; "byte"] (A "-custom");
294 ()
295
296 | _ -> ()
297 end