Commit | Line | Data |
---|---|---|
abad11c5 C |
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 |