123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- #!/usr/bin/env python
- # encoding: utf-8
- # Thomas Nagy, 2006-2010 (ita)
- "ocaml support"
- import os, re
- from waflib import Utils, Task
- from waflib.Logs import error
- from waflib.TaskGen import feature, before_method, after_method, extension
- EXT_MLL = ['.mll']
- EXT_MLY = ['.mly']
- EXT_MLI = ['.mli']
- EXT_MLC = ['.c']
- EXT_ML = ['.ml']
- open_re = re.compile('^\s*open\s+([a-zA-Z]+)(;;){0,1}$', re.M)
- foo = re.compile(r"""(\(\*)|(\*\))|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^()*"'\\]*)""", re.M)
- def filter_comments(txt):
- meh = [0]
- def repl(m):
- if m.group(1):
- meh[0] += 1
- elif m.group(2):
- meh[0] -= 1
- elif not meh[0]:
- return m.group()
- return ''
- return foo.sub(repl, txt)
- def scan(self):
- node = self.inputs[0]
- code = filter_comments(node.read())
- global open_re
- names = []
- import_iterator = open_re.finditer(code)
- if import_iterator:
- for import_match in import_iterator:
- names.append(import_match.group(1))
- found_lst = []
- raw_lst = []
- for name in names:
- nd = None
- for x in self.incpaths:
- nd = x.find_resource(name.lower()+'.ml')
- if not nd:
- nd = x.find_resource(name+'.ml')
- if nd:
- found_lst.append(nd)
- break
- else:
- raw_lst.append(name)
- return (found_lst, raw_lst)
- native_lst=['native', 'all', 'c_object']
- bytecode_lst=['bytecode', 'all']
- @feature('ocaml')
- def init_ml(self):
- Utils.def_attrs(self,
- type = 'all',
- incpaths_lst = [],
- bld_incpaths_lst = [],
- mlltasks = [],
- mlytasks = [],
- mlitasks = [],
- native_tasks = [],
- bytecode_tasks = [],
- linktasks = [],
- bytecode_env = None,
- native_env = None,
- compiled_tasks = [],
- includes = '',
- uselib = '',
- are_deps_set = 0)
- @feature('ocaml')
- @after_method('init_ml')
- def init_envs_ml(self):
- self.islibrary = getattr(self, 'islibrary', False)
- global native_lst, bytecode_lst
- self.native_env = None
- if self.type in native_lst:
- self.native_env = self.env.derive()
- if self.islibrary:
- self.native_env['OCALINKFLAGS'] = '-a'
- self.bytecode_env = None
- if self.type in bytecode_lst:
- self.bytecode_env = self.env.derive()
- if self.islibrary:
- self.bytecode_env['OCALINKFLAGS'] = '-a'
- if self.type == 'c_object':
- self.native_env.append_unique('OCALINKFLAGS_OPT', '-output-obj')
- @feature('ocaml')
- @before_method('apply_vars_ml')
- @after_method('init_envs_ml')
- def apply_incpaths_ml(self):
- inc_lst = self.includes.split()
- lst = self.incpaths_lst
- for dir in inc_lst:
- node = self.path.find_dir(dir)
- if not node:
- error("node not found: " + str(dir))
- continue
- if not node in lst:
- lst.append(node)
- self.bld_incpaths_lst.append(node)
- # now the nodes are added to self.incpaths_lst
- @feature('ocaml')
- @before_method('process_source')
- def apply_vars_ml(self):
- for i in self.incpaths_lst:
- if self.bytecode_env:
- app = self.bytecode_env.append_value
- app('OCAMLPATH', ['-I', i.bldpath(), '-I', i.srcpath()])
- if self.native_env:
- app = self.native_env.append_value
- app('OCAMLPATH', ['-I', i.bldpath(), '-I', i.srcpath()])
- varnames = ['INCLUDES', 'OCAMLFLAGS', 'OCALINKFLAGS', 'OCALINKFLAGS_OPT']
- for name in self.uselib.split():
- for vname in varnames:
- cnt = self.env[vname+'_'+name]
- if cnt:
- if self.bytecode_env:
- self.bytecode_env.append_value(vname, cnt)
- if self.native_env:
- self.native_env.append_value(vname, cnt)
- @feature('ocaml')
- @after_method('process_source')
- def apply_link_ml(self):
- if self.bytecode_env:
- ext = self.islibrary and '.cma' or '.run'
- linktask = self.create_task('ocalink')
- linktask.bytecode = 1
- linktask.set_outputs(self.path.find_or_declare(self.target + ext))
- linktask.env = self.bytecode_env
- self.linktasks.append(linktask)
- if self.native_env:
- if self.type == 'c_object':
- ext = '.o'
- elif self.islibrary:
- ext = '.cmxa'
- else:
- ext = ''
- linktask = self.create_task('ocalinkx')
- linktask.set_outputs(self.path.find_or_declare(self.target + ext))
- linktask.env = self.native_env
- self.linktasks.append(linktask)
- # we produce a .o file to be used by gcc
- self.compiled_tasks.append(linktask)
- @extension(*EXT_MLL)
- def mll_hook(self, node):
- mll_task = self.create_task('ocamllex', node, node.change_ext('.ml'))
- mll_task.env = self.native_env.derive()
- self.mlltasks.append(mll_task)
- self.source.append(mll_task.outputs[0])
- @extension(*EXT_MLY)
- def mly_hook(self, node):
- mly_task = self.create_task('ocamlyacc', node, [node.change_ext('.ml'), node.change_ext('.mli')])
- mly_task.env = self.native_env.derive()
- self.mlytasks.append(mly_task)
- self.source.append(mly_task.outputs[0])
- task = self.create_task('ocamlcmi', mly_task.outputs[1], mly_task.outputs[1].change_ext('.cmi'))
- task.env = self.native_env.derive()
- @extension(*EXT_MLI)
- def mli_hook(self, node):
- task = self.create_task('ocamlcmi', node, node.change_ext('.cmi'))
- task.env = self.native_env.derive()
- self.mlitasks.append(task)
- @extension(*EXT_MLC)
- def mlc_hook(self, node):
- task = self.create_task('ocamlcc', node, node.change_ext('.o'))
- task.env = self.native_env.derive()
- self.compiled_tasks.append(task)
- @extension(*EXT_ML)
- def ml_hook(self, node):
- if self.native_env:
- task = self.create_task('ocamlx', node, node.change_ext('.cmx'))
- task.env = self.native_env.derive()
- task.incpaths = self.bld_incpaths_lst
- self.native_tasks.append(task)
- if self.bytecode_env:
- task = self.create_task('ocaml', node, node.change_ext('.cmo'))
- task.env = self.bytecode_env.derive()
- task.bytecode = 1
- task.incpaths = self.bld_incpaths_lst
- self.bytecode_tasks.append(task)
- def compile_may_start(self):
- if not getattr(self, 'flag_deps', ''):
- self.flag_deps = 1
- # the evil part is that we can only compute the dependencies after the
- # source files can be read (this means actually producing the source files)
- if getattr(self, 'bytecode', ''):
- alltasks = self.generator.bytecode_tasks
- else:
- alltasks = self.generator.native_tasks
- self.signature() # ensure that files are scanned - unfortunately
- tree = self.generator.bld
- for node in self.inputs:
- lst = tree.node_deps[self.uid()]
- for depnode in lst:
- for t in alltasks:
- if t == self:
- continue
- if depnode in t.inputs:
- self.set_run_after(t)
- # TODO necessary to get the signature right - for now
- delattr(self, 'cache_sig')
- self.signature()
- return Task.Task.runnable_status(self)
- class ocamlx(Task.Task):
- """native caml compilation"""
- color = 'GREEN'
- run_str = '${OCAMLOPT} ${OCAMLPATH} ${OCAMLFLAGS} ${OCAMLINCLUDES} -c -o ${TGT} ${SRC}'
- scan = scan
- runnable_status = compile_may_start
- class ocaml(Task.Task):
- """bytecode caml compilation"""
- color = 'GREEN'
- run_str = '${OCAMLC} ${OCAMLPATH} ${OCAMLFLAGS} ${OCAMLINCLUDES} -c -o ${TGT} ${SRC}'
- scan = scan
- runnable_status = compile_may_start
- class ocamlcmi(Task.Task):
- """interface generator (the .i files?)"""
- color = 'BLUE'
- run_str = '${OCAMLC} ${OCAMLPATH} ${OCAMLINCLUDES} -o ${TGT} -c ${SRC}'
- before = ['ocamlcc', 'ocaml', 'ocamlcc']
- class ocamlcc(Task.Task):
- """ocaml to c interfaces"""
- color = 'GREEN'
- run_str = 'cd ${TGT[0].bld_dir()} && ${OCAMLOPT} ${OCAMLFLAGS} ${OCAMLPATH} ${OCAMLINCLUDES} -c ${SRC[0].abspath()}'
- class ocamllex(Task.Task):
- """lexical generator"""
- color = 'BLUE'
- run_str = '${OCAMLLEX} ${SRC} -o ${TGT}'
- before = ['ocamlcmi', 'ocaml', 'ocamlcc']
- class ocamlyacc(Task.Task):
- """parser generator"""
- color = 'BLUE'
- run_str = '${OCAMLYACC} -b ${tsk.base()} ${SRC}'
- before = ['ocamlcmi', 'ocaml', 'ocamlcc']
- def base(self):
- node = self.outputs[0]
- s = os.path.splitext(node.name)[0]
- return node.bld_dir() + os.sep + s
- def link_may_start(self):
- if getattr(self, 'bytecode', 0):
- alltasks = self.generator.bytecode_tasks
- else:
- alltasks = self.generator.native_tasks
- for x in alltasks:
- if not x.hasrun:
- return Task.ASK_LATER
- if not getattr(self, 'order', ''):
- # now reorder the inputs given the task dependencies
- # this part is difficult, we do not have a total order on the tasks
- # if the dependencies are wrong, this may not stop
- seen = []
- pendant = []+alltasks
- while pendant:
- task = pendant.pop(0)
- if task in seen:
- continue
- for x in task.run_after:
- if not x in seen:
- pendant.append(task)
- break
- else:
- seen.append(task)
- self.inputs = [x.outputs[0] for x in seen]
- self.order = 1
- return Task.Task.runnable_status(self)
- class ocalink(Task.Task):
- """bytecode caml link"""
- color = 'YELLOW'
- run_str = '${OCAMLC} -o ${TGT} ${OCAMLINCLUDES} ${OCALINKFLAGS} ${SRC}'
- runnable_status = link_may_start
- after = ['ocaml', 'ocamlcc']
- class ocalinkx(Task.Task):
- """native caml link"""
- color = 'YELLOW'
- run_str = '${OCAMLOPT} -o ${TGT} ${OCAMLINCLUDES} ${OCALINKFLAGS_OPT} ${SRC}'
- runnable_status = link_may_start
- after = ['ocamlx', 'ocamlcc']
- def configure(conf):
- opt = conf.find_program('ocamlopt', var='OCAMLOPT', mandatory=False)
- occ = conf.find_program('ocamlc', var='OCAMLC', mandatory=False)
- if (not opt) or (not occ):
- conf.fatal('The objective caml compiler was not found:\ninstall it or make it available in your PATH')
- v = conf.env
- v['OCAMLC'] = occ
- v['OCAMLOPT'] = opt
- v['OCAMLLEX'] = conf.find_program('ocamllex', var='OCAMLLEX', mandatory=False)
- v['OCAMLYACC'] = conf.find_program('ocamlyacc', var='OCAMLYACC', mandatory=False)
- v['OCAMLFLAGS'] = ''
- where = conf.cmd_and_log(conf.env.OCAMLC + ['-where']).strip()+os.sep
- v['OCAMLLIB'] = where
- v['LIBPATH_OCAML'] = where
- v['INCLUDES_OCAML'] = where
- v['LIB_OCAML'] = 'camlrun'
|