s4:lib/messaging: terminate the irpc_servers_byname() result with server_id_set_disco...
[Samba/gebeck_regimport.git] / buildtools / wafadmin / Tools / ocaml.py
blob20c926969a129b61604a45d16d09fbe2f8560309
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2006 (ita)
5 "ocaml support"
7 import os, re
8 import TaskGen, Utils, Task, Build
9 from Logs import error
10 from TaskGen import taskgen, feature, before, after, extension
12 EXT_MLL = ['.mll']
13 EXT_MLY = ['.mly']
14 EXT_MLI = ['.mli']
15 EXT_MLC = ['.c']
16 EXT_ML = ['.ml']
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):
21 meh = [0]
22 def repl(m):
23 if m.group(1): meh[0] += 1
24 elif m.group(2): meh[0] -= 1
25 elif not meh[0]: return m.group(0)
26 return ''
27 return foo.sub(repl, txt)
29 def scan(self):
30 node = self.inputs[0]
31 code = filter_comments(node.read(self.env))
33 global open_re
34 names = []
35 import_iterator = open_re.finditer(code)
36 if import_iterator:
37 for import_match in import_iterator:
38 names.append(import_match.group(1))
39 found_lst = []
40 raw_lst = []
41 for name in names:
42 nd = None
43 for x in self.incpaths:
44 nd = x.find_resource(name.lower()+'.ml')
45 if not nd: nd = x.find_resource(name+'.ml')
46 if nd:
47 found_lst.append(nd)
48 break
49 else:
50 raw_lst.append(name)
52 return (found_lst, raw_lst)
54 native_lst=['native', 'all', 'c_object']
55 bytecode_lst=['bytecode', 'all']
56 class ocaml_taskgen(TaskGen.task_gen):
57 def __init__(self, *k, **kw):
58 TaskGen.task_gen.__init__(self, *k, **kw)
60 @feature('ocaml')
61 def init_ml(self):
62 Utils.def_attrs(self,
63 type = 'all',
64 incpaths_lst = [],
65 bld_incpaths_lst = [],
66 mlltasks = [],
67 mlytasks = [],
68 mlitasks = [],
69 native_tasks = [],
70 bytecode_tasks = [],
71 linktasks = [],
72 bytecode_env = None,
73 native_env = None,
74 compiled_tasks = [],
75 includes = '',
76 uselib = '',
77 are_deps_set = 0)
79 @feature('ocaml')
80 @after('init_ml')
81 def init_envs_ml(self):
83 self.islibrary = getattr(self, 'islibrary', False)
85 global native_lst, bytecode_lst
86 self.native_env = None
87 if self.type in native_lst:
88 self.native_env = self.env.copy()
89 if self.islibrary: self.native_env['OCALINKFLAGS'] = '-a'
91 self.bytecode_env = None
92 if self.type in bytecode_lst:
93 self.bytecode_env = self.env.copy()
94 if self.islibrary: self.bytecode_env['OCALINKFLAGS'] = '-a'
96 if self.type == 'c_object':
97 self.native_env.append_unique('OCALINKFLAGS_OPT', '-output-obj')
99 @feature('ocaml')
100 @before('apply_vars_ml')
101 @after('init_envs_ml')
102 def apply_incpaths_ml(self):
103 inc_lst = self.includes.split()
104 lst = self.incpaths_lst
105 for dir in inc_lst:
106 node = self.path.find_dir(dir)
107 if not node:
108 error("node not found: " + str(dir))
109 continue
110 self.bld.rescan(node)
111 if not node in lst: lst.append(node)
112 self.bld_incpaths_lst.append(node)
113 # now the nodes are added to self.incpaths_lst
115 @feature('ocaml')
116 @before('apply_core')
117 def apply_vars_ml(self):
118 for i in self.incpaths_lst:
119 if self.bytecode_env:
120 app = self.bytecode_env.append_value
121 app('OCAMLPATH', '-I')
122 app('OCAMLPATH', i.srcpath(self.env))
123 app('OCAMLPATH', '-I')
124 app('OCAMLPATH', i.bldpath(self.env))
126 if self.native_env:
127 app = self.native_env.append_value
128 app('OCAMLPATH', '-I')
129 app('OCAMLPATH', i.bldpath(self.env))
130 app('OCAMLPATH', '-I')
131 app('OCAMLPATH', i.srcpath(self.env))
133 varnames = ['INCLUDES', 'OCAMLFLAGS', 'OCALINKFLAGS', 'OCALINKFLAGS_OPT']
134 for name in self.uselib.split():
135 for vname in varnames:
136 cnt = self.env[vname+'_'+name]
137 if cnt:
138 if self.bytecode_env: self.bytecode_env.append_value(vname, cnt)
139 if self.native_env: self.native_env.append_value(vname, cnt)
141 @feature('ocaml')
142 @after('apply_core')
143 def apply_link_ml(self):
145 if self.bytecode_env:
146 ext = self.islibrary and '.cma' or '.run'
148 linktask = self.create_task('ocalink')
149 linktask.bytecode = 1
150 linktask.set_outputs(self.path.find_or_declare(self.target + ext))
151 linktask.obj = self
152 linktask.env = self.bytecode_env
153 self.linktasks.append(linktask)
155 if self.native_env:
156 if self.type == 'c_object': ext = '.o'
157 elif self.islibrary: ext = '.cmxa'
158 else: ext = ''
160 linktask = self.create_task('ocalinkx')
161 linktask.set_outputs(self.path.find_or_declare(self.target + ext))
162 linktask.obj = self
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)
169 @extension(EXT_MLL)
170 def mll_hook(self, node):
171 mll_task = self.create_task('ocamllex', node, node.change_ext('.ml'), env=self.native_env)
172 self.mlltasks.append(mll_task)
174 self.allnodes.append(mll_task.outputs[0])
176 @extension(EXT_MLY)
177 def mly_hook(self, node):
178 mly_task = self.create_task('ocamlyacc', node, [node.change_ext('.ml'), node.change_ext('.mli')], env=self.native_env)
179 self.mlytasks.append(mly_task)
180 self.allnodes.append(mly_task.outputs[0])
182 task = self.create_task('ocamlcmi', mly_task.outputs[1], mly_task.outputs[1].change_ext('.cmi'), env=self.native_env)
184 @extension(EXT_MLI)
185 def mli_hook(self, node):
186 task = self.create_task('ocamlcmi', node, node.change_ext('.cmi'), env=self.native_env)
187 self.mlitasks.append(task)
189 @extension(EXT_MLC)
190 def mlc_hook(self, node):
191 task = self.create_task('ocamlcc', node, node.change_ext('.o'), env=self.native_env)
192 self.compiled_tasks.append(task)
194 @extension(EXT_ML)
195 def ml_hook(self, node):
196 if self.native_env:
197 task = self.create_task('ocamlx', node, node.change_ext('.cmx'), env=self.native_env)
198 task.obj = self
199 task.incpaths = self.bld_incpaths_lst
200 self.native_tasks.append(task)
202 if self.bytecode_env:
203 task = self.create_task('ocaml', node, node.change_ext('.cmo'), env=self.bytecode_env)
204 task.obj = self
205 task.bytecode = 1
206 task.incpaths = self.bld_incpaths_lst
207 self.bytecode_tasks.append(task)
209 def compile_may_start(self):
210 if not getattr(self, 'flag_deps', ''):
211 self.flag_deps = 1
213 # the evil part is that we can only compute the dependencies after the
214 # source files can be read (this means actually producing the source files)
215 if getattr(self, 'bytecode', ''): alltasks = self.obj.bytecode_tasks
216 else: alltasks = self.obj.native_tasks
218 self.signature() # ensure that files are scanned - unfortunately
219 tree = self.generator.bld
220 env = self.env
221 for node in self.inputs:
222 lst = tree.node_deps[self.unique_id()]
223 for depnode in lst:
224 for t in alltasks:
225 if t == self: continue
226 if depnode in t.inputs:
227 self.set_run_after(t)
229 # TODO necessary to get the signature right - for now
230 delattr(self, 'cache_sig')
231 self.signature()
233 return Task.Task.runnable_status(self)
235 b = Task.simple_task_type
236 cls = b('ocamlx', '${OCAMLOPT} ${OCAMLPATH} ${OCAMLFLAGS} ${INCLUDES} -c -o ${TGT} ${SRC}', color='GREEN', shell=False)
237 cls.runnable_status = compile_may_start
238 cls.scan = scan
240 b = Task.simple_task_type
241 cls = b('ocaml', '${OCAMLC} ${OCAMLPATH} ${OCAMLFLAGS} ${INCLUDES} -c -o ${TGT} ${SRC}', color='GREEN', shell=False)
242 cls.runnable_status = compile_may_start
243 cls.scan = scan
246 b('ocamlcmi', '${OCAMLC} ${OCAMLPATH} ${INCLUDES} -o ${TGT} -c ${SRC}', color='BLUE', before="ocaml ocamlcc ocamlx")
247 b('ocamlcc', 'cd ${TGT[0].bld_dir(env)} && ${OCAMLOPT} ${OCAMLFLAGS} ${OCAMLPATH} ${INCLUDES} -c ${SRC[0].abspath(env)}', color='GREEN')
249 b('ocamllex', '${OCAMLLEX} ${SRC} -o ${TGT}', color='BLUE', before="ocamlcmi ocaml ocamlcc")
250 b('ocamlyacc', '${OCAMLYACC} -b ${TGT[0].bld_base(env)} ${SRC}', color='BLUE', before="ocamlcmi ocaml ocamlcc")
253 def link_may_start(self):
254 if not getattr(self, 'order', ''):
256 # now reorder the inputs given the task dependencies
257 if getattr(self, 'bytecode', 0): alltasks = self.obj.bytecode_tasks
258 else: alltasks = self.obj.native_tasks
260 # this part is difficult, we do not have a total order on the tasks
261 # if the dependencies are wrong, this may not stop
262 seen = []
263 pendant = []+alltasks
264 while pendant:
265 task = pendant.pop(0)
266 if task in seen: continue
267 for x in task.run_after:
268 if not x in seen:
269 pendant.append(task)
270 break
271 else:
272 seen.append(task)
273 self.inputs = [x.outputs[0] for x in seen]
274 self.order = 1
275 return Task.Task.runnable_status(self)
277 act = b('ocalink', '${OCAMLC} -o ${TGT} ${INCLUDES} ${OCALINKFLAGS} ${SRC}', color='YELLOW', after="ocaml ocamlcc")
278 act.runnable_status = link_may_start
279 act = b('ocalinkx', '${OCAMLOPT} -o ${TGT} ${INCLUDES} ${OCALINKFLAGS_OPT} ${SRC}', color='YELLOW', after="ocamlx ocamlcc")
280 act.runnable_status = link_may_start
282 def detect(conf):
283 opt = conf.find_program('ocamlopt', var='OCAMLOPT')
284 occ = conf.find_program('ocamlc', var='OCAMLC')
285 if (not opt) or (not occ):
286 conf.fatal('The objective caml compiler was not found:\ninstall it or make it available in your PATH')
288 v = conf.env
289 v['OCAMLC'] = occ
290 v['OCAMLOPT'] = opt
291 v['OCAMLLEX'] = conf.find_program('ocamllex', var='OCAMLLEX')
292 v['OCAMLYACC'] = conf.find_program('ocamlyacc', var='OCAMLYACC')
293 v['OCAMLFLAGS'] = ''
294 v['OCAMLLIB'] = Utils.cmd_output(conf.env['OCAMLC']+' -where').strip()+os.sep
295 v['LIBPATH_OCAML'] = Utils.cmd_output(conf.env['OCAMLC']+' -where').strip()+os.sep
296 v['CPPPATH_OCAML'] = Utils.cmd_output(conf.env['OCAMLC']+' -where').strip()+os.sep
297 v['LIB_OCAML'] = 'camlrun'