waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

ocaml.py (9528B)


      1 #!/usr/bin/env python
      2 # encoding: utf-8
      3 # Thomas Nagy, 2006-2010 (ita)
      4 
      5 "ocaml support"
      6 
      7 import os, re
      8 from waflib import Utils, Task
      9 from waflib.Logs import error
     10 from waflib.TaskGen import feature, before_method, after_method, extension
     11 
     12 EXT_MLL = ['.mll']
     13 EXT_MLY = ['.mly']
     14 EXT_MLI = ['.mli']
     15 EXT_MLC = ['.c']
     16 EXT_ML  = ['.ml']
     17 
     18 open_re = re.compile(r'^\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):
     24 			meh[0] += 1
     25 		elif m.group(2):
     26 			meh[0] -= 1
     27 		elif not meh[0]:
     28 			return m.group()
     29 		return ''
     30 	return foo.sub(repl, txt)
     31 
     32 def scan(self):
     33 	node = self.inputs[0]
     34 	code = filter_comments(node.read())
     35 
     36 	global open_re
     37 	names = []
     38 	import_iterator = open_re.finditer(code)
     39 	if import_iterator:
     40 		for import_match in import_iterator:
     41 			names.append(import_match.group(1))
     42 	found_lst = []
     43 	raw_lst = []
     44 	for name in names:
     45 		nd = None
     46 		for x in self.incpaths:
     47 			nd = x.find_resource(name.lower()+'.ml')
     48 			if not nd:
     49 				nd = x.find_resource(name+'.ml')
     50 			if nd:
     51 				found_lst.append(nd)
     52 				break
     53 		else:
     54 			raw_lst.append(name)
     55 
     56 	return (found_lst, raw_lst)
     57 
     58 native_lst=['native', 'all', 'c_object']
     59 bytecode_lst=['bytecode', 'all']
     60 
     61 @feature('ocaml')
     62 def init_ml(self):
     63 	Utils.def_attrs(self,
     64 		type = 'all',
     65 		incpaths_lst = [],
     66 		bld_incpaths_lst = [],
     67 		mlltasks = [],
     68 		mlytasks = [],
     69 		mlitasks = [],
     70 		native_tasks = [],
     71 		bytecode_tasks = [],
     72 		linktasks = [],
     73 		bytecode_env = None,
     74 		native_env = None,
     75 		compiled_tasks = [],
     76 		includes = '',
     77 		uselib = '',
     78 		are_deps_set = 0)
     79 
     80 @feature('ocaml')
     81 @after_method('init_ml')
     82 def init_envs_ml(self):
     83 
     84 	self.islibrary = getattr(self, 'islibrary', False)
     85 
     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()
     90 		if self.islibrary:
     91 			self.native_env['OCALINKFLAGS']   = '-a'
     92 
     93 	self.bytecode_env = None
     94 	if self.type in bytecode_lst:
     95 		self.bytecode_env = self.env.derive()
     96 		if self.islibrary:
     97 			self.bytecode_env['OCALINKFLAGS'] = '-a'
     98 
     99 	if self.type == 'c_object':
    100 		self.native_env.append_unique('OCALINKFLAGS_OPT', '-output-obj')
    101 
    102 @feature('ocaml')
    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
    108 	for dir in inc_lst:
    109 		node = self.path.find_dir(dir)
    110 		if not node:
    111 			error("node not found: " + str(dir))
    112 			continue
    113 		if not node in lst:
    114 			lst.append(node)
    115 		self.bld_incpaths_lst.append(node)
    116 	# now the nodes are added to self.incpaths_lst
    117 
    118 @feature('ocaml')
    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()])
    125 
    126 		if self.native_env:
    127 			app = self.native_env.append_value
    128 			app('OCAMLPATH', ['-I', i.bldpath(), '-I', i.srcpath()])
    129 
    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]
    134 			if cnt:
    135 				if self.bytecode_env:
    136 					self.bytecode_env.append_value(vname, cnt)
    137 				if self.native_env:
    138 					self.native_env.append_value(vname, cnt)
    139 
    140 @feature('ocaml')
    141 @after_method('process_source')
    142 def apply_link_ml(self):
    143 
    144 	if self.bytecode_env:
    145 		ext = self.islibrary and '.cma' or '.run'
    146 
    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)
    152 
    153 	if self.native_env:
    154 		if self.type == 'c_object':
    155 			ext = '.o'
    156 		elif self.islibrary:
    157 			ext = '.cmxa'
    158 		else:
    159 			ext = ''
    160 
    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)
    165 
    166 		# we produce a .o file to be used by gcc
    167 		self.compiled_tasks.append(linktask)
    168 
    169 @extension(*EXT_MLL)
    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)
    174 
    175 	self.source.append(mll_task.outputs[0])
    176 
    177 @extension(*EXT_MLY)
    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])
    183 
    184 	task = self.create_task('ocamlcmi', mly_task.outputs[1], mly_task.outputs[1].change_ext('.cmi'))
    185 	task.env = self.native_env.derive()
    186 
    187 @extension(*EXT_MLI)
    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)
    192 
    193 @extension(*EXT_MLC)
    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)
    198 
    199 @extension(*EXT_ML)
    200 def ml_hook(self, node):
    201 	if self.native_env:
    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)
    206 
    207 	if self.bytecode_env:
    208 		task = self.create_task('ocaml', node, node.change_ext('.cmo'))
    209 		task.env = self.bytecode_env.derive()
    210 		task.bytecode = 1
    211 		task.incpaths = self.bld_incpaths_lst
    212 		self.bytecode_tasks.append(task)
    213 
    214 def compile_may_start(self):
    215 
    216 	if not getattr(self, 'flag_deps', ''):
    217 		self.flag_deps = 1
    218 
    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
    223 		else:
    224 			alltasks = self.generator.native_tasks
    225 
    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()]
    230 			for depnode in lst:
    231 				for t in alltasks:
    232 					if t == self:
    233 						continue
    234 					if depnode in t.inputs:
    235 						self.set_run_after(t)
    236 
    237 		# TODO necessary to get the signature right - for now
    238 		delattr(self, 'cache_sig')
    239 		self.signature()
    240 
    241 	return Task.Task.runnable_status(self)
    242 
    243 class ocamlx(Task.Task):
    244 	"""native caml compilation"""
    245 	color   = 'GREEN'
    246 	run_str = '${OCAMLOPT} ${OCAMLPATH} ${OCAMLFLAGS} ${OCAMLINCLUDES} -c -o ${TGT} ${SRC}'
    247 	scan    = scan
    248 	runnable_status = compile_may_start
    249 
    250 class ocaml(Task.Task):
    251 	"""bytecode caml compilation"""
    252 	color   = 'GREEN'
    253 	run_str = '${OCAMLC} ${OCAMLPATH} ${OCAMLFLAGS} ${OCAMLINCLUDES} -c -o ${TGT} ${SRC}'
    254 	scan    = scan
    255 	runnable_status = compile_may_start
    256 
    257 class ocamlcmi(Task.Task):
    258 	"""interface generator (the .i files?)"""
    259 	color   = 'BLUE'
    260 	run_str = '${OCAMLC} ${OCAMLPATH} ${OCAMLINCLUDES} -o ${TGT} -c ${SRC}'
    261 	before  = ['ocamlcc', 'ocaml', 'ocamlcc']
    262 
    263 class ocamlcc(Task.Task):
    264 	"""ocaml to c interfaces"""
    265 	color   = 'GREEN'
    266 	run_str = 'cd ${TGT[0].bld_dir()} && ${OCAMLOPT} ${OCAMLFLAGS} ${OCAMLPATH} ${OCAMLINCLUDES} -c ${SRC[0].abspath()}'
    267 
    268 class ocamllex(Task.Task):
    269 	"""lexical generator"""
    270 	color   = 'BLUE'
    271 	run_str = '${OCAMLLEX} ${SRC} -o ${TGT}'
    272 	before  = ['ocamlcmi', 'ocaml', 'ocamlcc']
    273 
    274 class ocamlyacc(Task.Task):
    275 	"""parser generator"""
    276 	color   = 'BLUE'
    277 	run_str = '${OCAMLYACC} -b ${tsk.base()} ${SRC}'
    278 	before  = ['ocamlcmi', 'ocaml', 'ocamlcc']
    279 
    280 	def base(self):
    281 		node = self.outputs[0]
    282 		s = os.path.splitext(node.name)[0]
    283 		return node.bld_dir() + os.sep + s
    284 
    285 def link_may_start(self):
    286 
    287 	if getattr(self, 'bytecode', 0):
    288 		alltasks = self.generator.bytecode_tasks
    289 	else:
    290 		alltasks = self.generator.native_tasks
    291 
    292 	for x in alltasks:
    293 		if not x.hasrun:
    294 			return Task.ASK_LATER
    295 
    296 	if not getattr(self, 'order', ''):
    297 
    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
    301 		seen = []
    302 		pendant = []+alltasks
    303 		while pendant:
    304 			task = pendant.pop(0)
    305 			if task in seen:
    306 				continue
    307 			for x in task.run_after:
    308 				if not x in seen:
    309 					pendant.append(task)
    310 					break
    311 			else:
    312 				seen.append(task)
    313 		self.inputs = [x.outputs[0] for x in seen]
    314 		self.order = 1
    315 	return Task.Task.runnable_status(self)
    316 
    317 class ocalink(Task.Task):
    318 	"""bytecode caml link"""
    319 	color   = 'YELLOW'
    320 	run_str = '${OCAMLC} -o ${TGT} ${OCAMLINCLUDES} ${OCALINKFLAGS} ${SRC}'
    321 	runnable_status = link_may_start
    322 	after = ['ocaml', 'ocamlcc']
    323 
    324 class ocalinkx(Task.Task):
    325 	"""native caml link"""
    326 	color   = 'YELLOW'
    327 	run_str = '${OCAMLOPT} -o ${TGT} ${OCAMLINCLUDES} ${OCALINKFLAGS_OPT} ${SRC}'
    328 	runnable_status = link_may_start
    329 	after = ['ocamlx', 'ocamlcc']
    330 
    331 def configure(conf):
    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')
    336 
    337 	v = conf.env
    338 	v['OCAMLC']       = occ
    339 	v['OCAMLOPT']     = opt
    340 	v['OCAMLLEX']     = conf.find_program('ocamllex', var='OCAMLLEX', mandatory=False)
    341 	v['OCAMLYACC']    = conf.find_program('ocamlyacc', var='OCAMLYACC', mandatory=False)
    342 	v['OCAMLFLAGS']   = ''
    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'
    348