1# compatibility layer for building with more recent waf versions 2 3import os, shlex, sys 4from waflib import Build, Configure, Node, Utils, Options, Logs, TaskGen 5from waflib import ConfigSet 6from waflib.TaskGen import feature, after 7from waflib.Configure import conf, ConfigurationContext 8 9from waflib.Tools.flex import decide_ext 10 11# This version of flexfun runs in tsk.get_cwd() as opposed to the 12# bld.variant_dir: since input paths adjusted against tsk.get_cwd(), we have to 13# use tsk.get_cwd() for the work directory as well. 14def flexfun(tsk): 15 env = tsk.env 16 bld = tsk.generator.bld 17 def to_list(xx): 18 if isinstance(xx, str): 19 return [xx] 20 return xx 21 tsk.last_cmd = lst = [] 22 lst.extend(to_list(env.FLEX)) 23 lst.extend(to_list(env.FLEXFLAGS)) 24 inputs = [a.path_from(tsk.get_cwd()) for a in tsk.inputs] 25 if env.FLEX_MSYS: 26 inputs = [x.replace(os.sep, '/') for x in inputs] 27 lst.extend(inputs) 28 lst = [x for x in lst if x] 29 txt = bld.cmd_and_log(lst, cwd=tsk.get_cwd(), env=env.env or None, quiet=0) 30 tsk.outputs[0].write(txt.replace('\r\n', '\n').replace('\r', '\n')) # issue #1207 31 32TaskGen.declare_chain( 33 name = 'flex', 34 rule = flexfun, # issue #854 35 ext_in = '.l', 36 decider = decide_ext, 37) 38 39 40for y in (Build.BuildContext, Build.CleanContext, Build.InstallContext, Build.UninstallContext, Build.ListContext): 41 class tmp(y): 42 variant = 'default' 43 44def abspath(self, env=None): 45 if env and hasattr(self, 'children'): 46 return self.get_bld().abspath() 47 return self.old_abspath() 48Node.Node.old_abspath = Node.Node.abspath 49Node.Node.abspath = abspath 50 51def bldpath(self, env=None): 52 return self.abspath() 53 #return self.path_from(self.ctx.bldnode.parent) 54Node.Node.bldpath = bldpath 55 56def srcpath(self, env=None): 57 return self.abspath() 58 #return self.path_from(self.ctx.bldnode.parent) 59Node.Node.srcpath = srcpath 60 61def store_fast(self, filename): 62 file = open(filename, 'wb') 63 data = self.get_merged_dict() 64 try: 65 Build.cPickle.dump(data, file, -1) 66 finally: 67 file.close() 68ConfigSet.ConfigSet.store_fast = store_fast 69 70def load_fast(self, filename): 71 file = open(filename, 'rb') 72 try: 73 data = Build.cPickle.load(file) 74 finally: 75 file.close() 76 self.table.update(data) 77ConfigSet.ConfigSet.load_fast = load_fast 78 79@feature('c', 'cxx', 'd', 'asm', 'fc', 'includes') 80@after('propagate_uselib_vars', 'process_source') 81def apply_incpaths(self): 82 lst = self.to_incnodes(self.to_list(getattr(self, 'includes', [])) + self.env['INCLUDES']) 83 self.includes_nodes = lst 84 cwdx = getattr(self.bld, 'cwdx', self.bld.bldnode) 85 self.env['INCPATHS'] = [x.path_from(cwdx) for x in lst] 86 87@conf 88def define(self, key, val, quote=True, comment=None): 89 assert key and isinstance(key, str) 90 91 if val is None: 92 val = () 93 elif isinstance(val, bool): 94 val = int(val) 95 96 # waf 1.5 97 self.env[key] = val 98 99 if isinstance(val, int) or isinstance(val, float): 100 s = '%s=%s' 101 else: 102 s = quote and '%s="%s"' or '%s=%s' 103 app = s % (key, str(val)) 104 105 ban = key + '=' 106 lst = self.env.DEFINES 107 for x in lst: 108 if x.startswith(ban): 109 lst[lst.index(x)] = app 110 break 111 else: 112 self.env.append_value('DEFINES', app) 113 114 self.env.append_unique('define_key', key) 115 116# compat15 removes this but we want to keep it 117@conf 118def undefine(self, key, from_env=True, comment=None): 119 assert key and isinstance(key, str) 120 121 ban = key + '=' 122 self.env.DEFINES = [x for x in self.env.DEFINES if not x.startswith(ban)] 123 self.env.append_unique('define_key', key) 124 # waf 1.5 125 if from_env: 126 self.env[key] = () 127 128class ConfigurationContext(Configure.ConfigurationContext): 129 def init_dirs(self): 130 self.setenv('default') 131 self.env.merge_config_header = True 132 return super(ConfigurationContext, self).init_dirs() 133 134def find_program_samba(self, *k, **kw): 135 kw['mandatory'] = False 136 ret = self.find_program_old(*k, **kw) 137 return ret 138Configure.ConfigurationContext.find_program_old = Configure.ConfigurationContext.find_program 139Configure.ConfigurationContext.find_program = find_program_samba 140 141Build.BuildContext.ENFORCE_GROUP_ORDERING = Utils.nada 142Build.BuildContext.AUTOCLEANUP_STALE_FILES = Utils.nada 143 144@conf 145def check(self, *k, **kw): 146 '''Override the waf defaults to inject --with-directory options''' 147 148 # match the configuration test with speficic options, for example: 149 # --with-libiconv -> Options.options.iconv_open -> "Checking for library iconv" 150 self.validate_c(kw) 151 152 additional_dirs = [] 153 if 'msg' in kw: 154 msg = kw['msg'] 155 for x in Options.OptionsContext.parser.parser.option_list: 156 if getattr(x, 'match', None) and msg in x.match: 157 d = getattr(Options.options, x.dest, '') 158 if d: 159 additional_dirs.append(d) 160 161 # we add the additional dirs twice: once for the test data, and again if the compilation test suceeds below 162 def add_options_dir(dirs, env): 163 for x in dirs: 164 if not x in env.CPPPATH: 165 env.CPPPATH = [os.path.join(x, 'include')] + env.CPPPATH 166 if not x in env.LIBPATH: 167 env.LIBPATH = [os.path.join(x, 'lib')] + env.LIBPATH 168 169 add_options_dir(additional_dirs, kw['env']) 170 171 self.start_msg(kw['msg'], **kw) 172 ret = None 173 try: 174 ret = self.run_build(*k, **kw) 175 except self.errors.ConfigurationError: 176 self.end_msg(kw['errmsg'], 'YELLOW', **kw) 177 if Logs.verbose > 1: 178 raise 179 else: 180 self.fatal('The configuration failed') 181 else: 182 kw['success'] = ret 183 # success! time for brandy 184 add_options_dir(additional_dirs, self.env) 185 186 ret = self.post_check(*k, **kw) 187 if not ret: 188 self.end_msg(kw['errmsg'], 'YELLOW', **kw) 189 self.fatal('The configuration failed %r' % ret) 190 else: 191 self.end_msg(self.ret_msg(kw['okmsg'], kw), **kw) 192 return ret 193 194@conf 195def CHECK_LIBRARY_SUPPORT(conf, rpath=False, version_script=False, msg=None): 196 '''see if the platform supports building libraries''' 197 198 if msg is None: 199 if rpath: 200 msg = "rpath library support" 201 else: 202 msg = "building library support" 203 204 def build(bld): 205 lib_node = bld.srcnode.make_node('libdir/liblc1.c') 206 lib_node.parent.mkdir() 207 lib_node.write('int lib_func(void) { return 42; }\n', 'w') 208 main_node = bld.srcnode.make_node('main.c') 209 main_node.write('int main(void) {return !(lib_func() == 42);}', 'w') 210 linkflags = [] 211 if version_script: 212 script = bld.srcnode.make_node('ldscript') 213 script.write('TEST_1.0A2 { global: *; };\n', 'w') 214 linkflags.append('-Wl,--version-script=%s' % script.abspath()) 215 bld(features='c cshlib', source=lib_node, target='lib1', linkflags=linkflags, name='lib1') 216 o = bld(features='c cprogram', source=main_node, target='prog1', uselib_local='lib1') 217 if rpath: 218 o.rpath = [lib_node.parent.abspath()] 219 def run_app(self): 220 args = conf.SAMBA_CROSS_ARGS(msg=msg) 221 env = dict(os.environ) 222 env['LD_LIBRARY_PATH'] = self.inputs[0].parent.abspath() + os.pathsep + env.get('LD_LIBRARY_PATH', '') 223 self.generator.bld.cmd_and_log([self.inputs[0].abspath()] + args, env=env) 224 o.post() 225 bld(rule=run_app, source=o.link_task.outputs[0]) 226 227 # ok, so it builds 228 try: 229 conf.check(build_fun=build, msg='Checking for %s' % msg) 230 except conf.errors.ConfigurationError: 231 return False 232 return True 233 234@conf 235def CHECK_NEED_LC(conf, msg): 236 '''check if we need -lc''' 237 def build(bld): 238 lib_node = bld.srcnode.make_node('libdir/liblc1.c') 239 lib_node.parent.mkdir() 240 lib_node.write('#include <stdio.h>\nint lib_func(void) { FILE *f = fopen("foo", "r");}\n', 'w') 241 bld(features='c cshlib', source=[lib_node], linkflags=conf.env.EXTRA_LDFLAGS, target='liblc') 242 try: 243 conf.check(build_fun=build, msg=msg, okmsg='-lc is unnecessary', errmsg='-lc is necessary') 244 except conf.errors.ConfigurationError: 245 return False 246 return True 247 248# already implemented on "waf -v" 249def order(bld, tgt_list): 250 return True 251Build.BuildContext.check_group_ordering = order 252 253@conf 254def CHECK_CFG(self, *k, **kw): 255 if 'args' in kw: 256 kw['args'] = shlex.split(kw['args']) 257 if not 'mandatory' in kw: 258 kw['mandatory'] = False 259 kw['global_define'] = True 260 return self.check_cfg(*k, **kw) 261 262def cmd_output(cmd, **kw): 263 264 silent = False 265 if 'silent' in kw: 266 silent = kw['silent'] 267 del(kw['silent']) 268 269 if 'e' in kw: 270 tmp = kw['e'] 271 del(kw['e']) 272 kw['env'] = tmp 273 274 kw['shell'] = isinstance(cmd, str) 275 kw['stdout'] = Utils.subprocess.PIPE 276 if silent: 277 kw['stderr'] = Utils.subprocess.PIPE 278 279 try: 280 p = Utils.subprocess.Popen(cmd, **kw) 281 output = p.communicate()[0] 282 except OSError as e: 283 raise ValueError(str(e)) 284 285 if p.returncode: 286 if not silent: 287 msg = "command execution failed: %s -> %r" % (cmd, str(output)) 288 raise ValueError(msg) 289 output = '' 290 return output 291Utils.cmd_output = cmd_output 292 293 294@TaskGen.feature('c', 'cxx', 'd') 295@TaskGen.before('apply_incpaths', 'propagate_uselib_vars') 296@TaskGen.after('apply_link', 'process_source') 297def apply_uselib_local(self): 298 """ 299 process the uselib_local attribute 300 execute after apply_link because of the execution order set on 'link_task' 301 """ 302 env = self.env 303 from waflib.Tools.ccroot import stlink_task 304 305 # 1. the case of the libs defined in the project (visit ancestors first) 306 # the ancestors external libraries (uselib) will be prepended 307 self.uselib = self.to_list(getattr(self, 'uselib', [])) 308 self.includes = self.to_list(getattr(self, 'includes', [])) 309 names = self.to_list(getattr(self, 'uselib_local', [])) 310 get = self.bld.get_tgen_by_name 311 seen = set() 312 seen_uselib = set() 313 tmp = Utils.deque(names) # consume a copy of the list of names 314 if tmp: 315 if Logs.verbose: 316 Logs.warn('compat: "uselib_local" is deprecated, replace by "use"') 317 while tmp: 318 lib_name = tmp.popleft() 319 # visit dependencies only once 320 if lib_name in seen: 321 continue 322 323 y = get(lib_name) 324 y.post() 325 seen.add(lib_name) 326 327 # object has ancestors to process (shared libraries): add them to the end of the list 328 if getattr(y, 'uselib_local', None): 329 for x in self.to_list(getattr(y, 'uselib_local', [])): 330 obj = get(x) 331 obj.post() 332 if getattr(obj, 'link_task', None): 333 if not isinstance(obj.link_task, stlink_task): 334 tmp.append(x) 335 336 # link task and flags 337 if getattr(y, 'link_task', None): 338 339 link_name = y.target[y.target.rfind(os.sep) + 1:] 340 if isinstance(y.link_task, stlink_task): 341 env.append_value('STLIB', [link_name]) 342 else: 343 # some linkers can link against programs 344 env.append_value('LIB', [link_name]) 345 346 # the order 347 self.link_task.set_run_after(y.link_task) 348 349 # for the recompilation 350 self.link_task.dep_nodes += y.link_task.outputs 351 352 # add the link path too 353 tmp_path = y.link_task.outputs[0].parent.bldpath() 354 if not tmp_path in env['LIBPATH']: 355 env.prepend_value('LIBPATH', [tmp_path]) 356 357 # add ancestors uselib too - but only propagate those that have no staticlib defined 358 for v in self.to_list(getattr(y, 'uselib', [])): 359 if v not in seen_uselib: 360 seen_uselib.add(v) 361 if not env['STLIB_' + v]: 362 if not v in self.uselib: 363 self.uselib.insert(0, v) 364 365 # if the library task generator provides 'export_includes', add to the include path 366 # the export_includes must be a list of paths relative to the other library 367 if getattr(y, 'export_includes', None): 368 self.includes.extend(y.to_incnodes(y.export_includes)) 369 370@TaskGen.feature('cprogram', 'cxxprogram', 'cstlib', 'cxxstlib', 'cshlib', 'cxxshlib', 'dprogram', 'dstlib', 'dshlib') 371@TaskGen.after('apply_link') 372def apply_objdeps(self): 373 "add the .o files produced by some other object files in the same manner as uselib_local" 374 names = getattr(self, 'add_objects', []) 375 if not names: 376 return 377 names = self.to_list(names) 378 379 get = self.bld.get_tgen_by_name 380 seen = [] 381 while names: 382 x = names[0] 383 384 # visit dependencies only once 385 if x in seen: 386 names = names[1:] 387 continue 388 389 # object does not exist ? 390 y = get(x) 391 392 # object has ancestors to process first ? update the list of names 393 if getattr(y, 'add_objects', None): 394 added = 0 395 lst = y.to_list(y.add_objects) 396 lst.reverse() 397 for u in lst: 398 if u in seen: 399 continue 400 added = 1 401 names = [u]+names 402 if added: 403 continue # list of names modified, loop 404 405 # safe to process the current object 406 y.post() 407 seen.append(x) 408 409 for t in getattr(y, 'compiled_tasks', []): 410 self.link_task.inputs.extend(t.outputs) 411 412@TaskGen.after('apply_link') 413def process_obj_files(self): 414 if not hasattr(self, 'obj_files'): 415 return 416 for x in self.obj_files: 417 node = self.path.find_resource(x) 418 self.link_task.inputs.append(node) 419 420@TaskGen.taskgen_method 421def add_obj_file(self, file): 422 """Small example on how to link object files as if they were source 423 obj = bld.create_obj('cc') 424 obj.add_obj_file('foo.o')""" 425 if not hasattr(self, 'obj_files'): 426 self.obj_files = [] 427 if not 'process_obj_files' in self.meths: 428 self.meths.append('process_obj_files') 429 self.obj_files.append(file) 430