3 # Thomas Nagy, 2006-2010 (ita)
8 from waflib import Utils, Task
9 from waflib.Logs import error
10 from waflib.TaskGen import feature, before_method, after_method, extension
18 open_re = re.compile('^\s*open\s+([a-zA-Z]+)(;;){0,1}$', re.M)
19 foo = re.compile(r"""(\(\*)|(\*\))|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^()*"'\\]*)""", re.M)
20 def filter_comments(txt):
30 return foo.sub(repl, txt)
34 code = filter_comments(node.read())
38 import_iterator = open_re.finditer(code)
40 for import_match in import_iterator:
41 names.append(import_match.group(1))
46 for x in self.incpaths:
47 nd = x.find_resource(name.lower()+'.ml')
49 nd = x.find_resource(name+'.ml')
56 return (found_lst, raw_lst)
58 native_lst=['native', 'all', 'c_object']
59 bytecode_lst=['bytecode', 'all']
66 bld_incpaths_lst = [],
81 @after_method('init_ml')
82 def init_envs_ml(self):
84 self.islibrary = getattr(self, 'islibrary', False)
86 global native_lst, bytecode_lst
87 self.native_env = None
88 if self.type in native_lst:
89 self.native_env = self.env.derive()
91 self.native_env['OCALINKFLAGS'] = '-a'
93 self.bytecode_env = None
94 if self.type in bytecode_lst:
95 self.bytecode_env = self.env.derive()
97 self.bytecode_env['OCALINKFLAGS'] = '-a'
99 if self.type == 'c_object':
100 self.native_env.append_unique('OCALINKFLAGS_OPT', '-output-obj')
103 @before_method('apply_vars_ml')
104 @after_method('init_envs_ml')
105 def apply_incpaths_ml(self):
106 inc_lst = self.includes.split()
107 lst = self.incpaths_lst
109 node = self.path.find_dir(dir)
111 error("node not found: " + str(dir))
115 self.bld_incpaths_lst.append(node)
116 # now the nodes are added to self.incpaths_lst
119 @before_method('process_source')
120 def apply_vars_ml(self):
121 for i in self.incpaths_lst:
122 if self.bytecode_env:
123 app = self.bytecode_env.append_value
124 app('OCAMLPATH', ['-I', i.bldpath(), '-I', i.srcpath()])
127 app = self.native_env.append_value
128 app('OCAMLPATH', ['-I', i.bldpath(), '-I', i.srcpath()])
130 varnames = ['INCLUDES', 'OCAMLFLAGS', 'OCALINKFLAGS', 'OCALINKFLAGS_OPT']
131 for name in self.uselib.split():
132 for vname in varnames:
133 cnt = self.env[vname+'_'+name]
135 if self.bytecode_env:
136 self.bytecode_env.append_value(vname, cnt)
138 self.native_env.append_value(vname, cnt)
141 @after_method('process_source')
142 def apply_link_ml(self):
144 if self.bytecode_env:
145 ext = self.islibrary and '.cma' or '.run'
147 linktask = self.create_task('ocalink')
148 linktask.bytecode = 1
149 linktask.set_outputs(self.path.find_or_declare(self.target + ext))
150 linktask.env = self.bytecode_env
151 self.linktasks.append(linktask)
154 if self.type == 'c_object':
161 linktask = self.create_task('ocalinkx')
162 linktask.set_outputs(self.path.find_or_declare(self.target + ext))
163 linktask.env = self.native_env
164 self.linktasks.append(linktask)
166 # we produce a .o file to be used by gcc
167 self.compiled_tasks.append(linktask)
170 def mll_hook(self, node):
171 mll_task = self.create_task('ocamllex', node, node.change_ext('.ml'))
172 mll_task.env = self.native_env.derive()
173 self.mlltasks.append(mll_task)
175 self.source.append(mll_task.outputs[0])
178 def mly_hook(self, node):
179 mly_task = self.create_task('ocamlyacc', node, [node.change_ext('.ml'), node.change_ext('.mli')])
180 mly_task.env = self.native_env.derive()
181 self.mlytasks.append(mly_task)
182 self.source.append(mly_task.outputs[0])
184 task = self.create_task('ocamlcmi', mly_task.outputs[1], mly_task.outputs[1].change_ext('.cmi'))
185 task.env = self.native_env.derive()
188 def mli_hook(self, node):
189 task = self.create_task('ocamlcmi', node, node.change_ext('.cmi'))
190 task.env = self.native_env.derive()
191 self.mlitasks.append(task)
194 def mlc_hook(self, node):
195 task = self.create_task('ocamlcc', node, node.change_ext('.o'))
196 task.env = self.native_env.derive()
197 self.compiled_tasks.append(task)
200 def ml_hook(self, node):
202 task = self.create_task('ocamlx', node, node.change_ext('.cmx'))
203 task.env = self.native_env.derive()
204 task.incpaths = self.bld_incpaths_lst
205 self.native_tasks.append(task)
207 if self.bytecode_env:
208 task = self.create_task('ocaml', node, node.change_ext('.cmo'))
209 task.env = self.bytecode_env.derive()
211 task.incpaths = self.bld_incpaths_lst
212 self.bytecode_tasks.append(task)
214 def compile_may_start(self):
216 if not getattr(self, 'flag_deps', ''):
219 # the evil part is that we can only compute the dependencies after the
220 # source files can be read (this means actually producing the source files)
221 if getattr(self, 'bytecode', ''):
222 alltasks = self.generator.bytecode_tasks
224 alltasks = self.generator.native_tasks
226 self.signature() # ensure that files are scanned - unfortunately
227 tree = self.generator.bld
228 for node in self.inputs:
229 lst = tree.node_deps[self.uid()]
234 if depnode in t.inputs:
235 self.set_run_after(t)
237 # TODO necessary to get the signature right - for now
238 delattr(self, 'cache_sig')
241 return Task.Task.runnable_status(self)
243 class ocamlx(Task.Task):
244 """native caml compilation"""
246 run_str = '${OCAMLOPT} ${OCAMLPATH} ${OCAMLFLAGS} ${OCAMLINCLUDES} -c -o ${TGT} ${SRC}'
248 runnable_status = compile_may_start
250 class ocaml(Task.Task):
251 """bytecode caml compilation"""
253 run_str = '${OCAMLC} ${OCAMLPATH} ${OCAMLFLAGS} ${OCAMLINCLUDES} -c -o ${TGT} ${SRC}'
255 runnable_status = compile_may_start
257 class ocamlcmi(Task.Task):
258 """interface generator (the .i files?)"""
260 run_str = '${OCAMLC} ${OCAMLPATH} ${OCAMLINCLUDES} -o ${TGT} -c ${SRC}'
261 before = ['ocamlcc', 'ocaml', 'ocamlcc']
263 class ocamlcc(Task.Task):
264 """ocaml to c interfaces"""
266 run_str = 'cd ${TGT[0].bld_dir()} && ${OCAMLOPT} ${OCAMLFLAGS} ${OCAMLPATH} ${OCAMLINCLUDES} -c ${SRC[0].abspath()}'
268 class ocamllex(Task.Task):
269 """lexical generator"""
271 run_str = '${OCAMLLEX} ${SRC} -o ${TGT}'
272 before = ['ocamlcmi', 'ocaml', 'ocamlcc']
274 class ocamlyacc(Task.Task):
275 """parser generator"""
277 run_str = '${OCAMLYACC} -b ${tsk.base()} ${SRC}'
278 before = ['ocamlcmi', 'ocaml', 'ocamlcc']
281 node = self.outputs[0]
282 s = os.path.splitext(node.name)[0]
283 return node.bld_dir() + os.sep + s
285 def link_may_start(self):
287 if getattr(self, 'bytecode', 0):
288 alltasks = self.generator.bytecode_tasks
290 alltasks = self.generator.native_tasks
294 return Task.ASK_LATER
296 if not getattr(self, 'order', ''):
298 # now reorder the inputs given the task dependencies
299 # this part is difficult, we do not have a total order on the tasks
300 # if the dependencies are wrong, this may not stop
302 pendant = []+alltasks
304 task = pendant.pop(0)
307 for x in task.run_after:
313 self.inputs = [x.outputs[0] for x in seen]
315 return Task.Task.runnable_status(self)
317 class ocalink(Task.Task):
318 """bytecode caml link"""
320 run_str = '${OCAMLC} -o ${TGT} ${OCAMLINCLUDES} ${OCALINKFLAGS} ${SRC}'
321 runnable_status = link_may_start
322 after = ['ocaml', 'ocamlcc']
324 class ocalinkx(Task.Task):
325 """native caml link"""
327 run_str = '${OCAMLOPT} -o ${TGT} ${OCAMLINCLUDES} ${OCALINKFLAGS_OPT} ${SRC}'
328 runnable_status = link_may_start
329 after = ['ocamlx', 'ocamlcc']
332 opt = conf.find_program('ocamlopt', var='OCAMLOPT', mandatory=False)
333 occ = conf.find_program('ocamlc', var='OCAMLC', mandatory=False)
334 if (not opt) or (not occ):
335 conf.fatal('The objective caml compiler was not found:\ninstall it or make it available in your PATH')
340 v['OCAMLLEX'] = conf.find_program('ocamllex', var='OCAMLLEX', mandatory=False)
341 v['OCAMLYACC'] = conf.find_program('ocamlyacc', var='OCAMLYACC', mandatory=False)
343 where = conf.cmd_and_log(conf.env.OCAMLC + ['-where']).strip()+os.sep
344 v['OCAMLLIB'] = where
345 v['LIBPATH_OCAML'] = where
346 v['INCLUDES_OCAML'] = where
347 v['LIB_OCAML'] = 'camlrun'