1#!/usr/bin/env python 2# encoding: utf-8 3# Thomas Nagy, 2005-2018 (ita) 4 5""" 6Classes and methods shared by tools providing support for C-like language such 7as C/C++/D/Assembly/Go (this support module is almost never used alone). 8""" 9 10import os, re 11from waflib import Task, Utils, Node, Errors, Logs 12from waflib.TaskGen import after_method, before_method, feature, taskgen_method, extension 13from waflib.Tools import c_aliases, c_preproc, c_config, c_osx, c_tests 14from waflib.Configure import conf 15 16SYSTEM_LIB_PATHS = ['/usr/lib64', '/usr/lib', '/usr/local/lib64', '/usr/local/lib'] 17 18USELIB_VARS = Utils.defaultdict(set) 19""" 20Mapping for features to :py:class:`waflib.ConfigSet.ConfigSet` variables. See :py:func:`waflib.Tools.ccroot.propagate_uselib_vars`. 21""" 22 23USELIB_VARS['c'] = set(['INCLUDES', 'FRAMEWORKPATH', 'DEFINES', 'CPPFLAGS', 'CCDEPS', 'CFLAGS', 'ARCH']) 24USELIB_VARS['cxx'] = set(['INCLUDES', 'FRAMEWORKPATH', 'DEFINES', 'CPPFLAGS', 'CXXDEPS', 'CXXFLAGS', 'ARCH']) 25USELIB_VARS['d'] = set(['INCLUDES', 'DFLAGS']) 26USELIB_VARS['includes'] = set(['INCLUDES', 'FRAMEWORKPATH', 'ARCH']) 27 28USELIB_VARS['cprogram'] = USELIB_VARS['cxxprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'FRAMEWORK', 'FRAMEWORKPATH', 'ARCH', 'LDFLAGS']) 29USELIB_VARS['cshlib'] = USELIB_VARS['cxxshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'FRAMEWORK', 'FRAMEWORKPATH', 'ARCH', 'LDFLAGS']) 30USELIB_VARS['cstlib'] = USELIB_VARS['cxxstlib'] = set(['ARFLAGS', 'LINKDEPS']) 31 32USELIB_VARS['dprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS']) 33USELIB_VARS['dshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS']) 34USELIB_VARS['dstlib'] = set(['ARFLAGS', 'LINKDEPS']) 35 36USELIB_VARS['asm'] = set(['ASFLAGS']) 37 38# ================================================================================================= 39 40@taskgen_method 41def create_compiled_task(self, name, node): 42 """ 43 Create the compilation task: c, cxx, asm, etc. The output node is created automatically (object file with a typical **.o** extension). 44 The task is appended to the list *compiled_tasks* which is then used by :py:func:`waflib.Tools.ccroot.apply_link` 45 46 :param name: name of the task class 47 :type name: string 48 :param node: the file to compile 49 :type node: :py:class:`waflib.Node.Node` 50 :return: The task created 51 :rtype: :py:class:`waflib.Task.Task` 52 """ 53 out = '%s.%d.o' % (node.name, self.idx) 54 task = self.create_task(name, node, node.parent.find_or_declare(out)) 55 try: 56 self.compiled_tasks.append(task) 57 except AttributeError: 58 self.compiled_tasks = [task] 59 return task 60 61@taskgen_method 62def to_incnodes(self, inlst): 63 """ 64 Task generator method provided to convert a list of string/nodes into a list of includes folders. 65 66 The paths are assumed to be relative to the task generator path, except if they begin by **#** 67 in which case they are searched from the top-level directory (``bld.srcnode``). 68 The folders are simply assumed to be existing. 69 70 The node objects in the list are returned in the output list. The strings are converted 71 into node objects if possible. The node is searched from the source directory, and if a match is found, 72 the equivalent build directory is created and added to the returned list too. When a folder cannot be found, it is ignored. 73 74 :param inlst: list of folders 75 :type inlst: space-delimited string or a list of string/nodes 76 :rtype: list of :py:class:`waflib.Node.Node` 77 :return: list of include folders as nodes 78 """ 79 lst = [] 80 seen = set() 81 for x in self.to_list(inlst): 82 if x in seen or not x: 83 continue 84 seen.add(x) 85 86 # with a real lot of targets, it is sometimes interesting to cache the results below 87 if isinstance(x, Node.Node): 88 lst.append(x) 89 else: 90 if os.path.isabs(x): 91 lst.append(self.bld.root.make_node(x) or x) 92 else: 93 if x[0] == '#': 94 p = self.bld.bldnode.make_node(x[1:]) 95 v = self.bld.srcnode.make_node(x[1:]) 96 else: 97 p = self.path.get_bld().make_node(x) 98 v = self.path.make_node(x) 99 if p.is_child_of(self.bld.bldnode): 100 p.mkdir() 101 lst.append(p) 102 lst.append(v) 103 return lst 104 105@feature('c', 'cxx', 'd', 'asm', 'fc', 'includes') 106@after_method('propagate_uselib_vars', 'process_source') 107def apply_incpaths(self): 108 """ 109 Task generator method that processes the attribute *includes*:: 110 111 tg = bld(features='includes', includes='.') 112 113 The folders only need to be relative to the current directory, the equivalent build directory is 114 added automatically (for headers created in the build directory). This enables using a build directory 115 or not (``top == out``). 116 117 This method will add a list of nodes read by :py:func:`waflib.Tools.ccroot.to_incnodes` in ``tg.env.INCPATHS``, 118 and the list of include paths in ``tg.env.INCLUDES``. 119 """ 120 121 lst = self.to_incnodes(self.to_list(getattr(self, 'includes', [])) + self.env.INCLUDES) 122 self.includes_nodes = lst 123 cwd = self.get_cwd() 124 self.env.INCPATHS = [x.path_from(cwd) for x in lst] 125 126class link_task(Task.Task): 127 """ 128 Base class for all link tasks. A task generator is supposed to have at most one link task bound in the attribute *link_task*. See :py:func:`waflib.Tools.ccroot.apply_link`. 129 130 .. inheritance-diagram:: waflib.Tools.ccroot.stlink_task waflib.Tools.c.cprogram waflib.Tools.c.cshlib waflib.Tools.cxx.cxxstlib waflib.Tools.cxx.cxxprogram waflib.Tools.cxx.cxxshlib waflib.Tools.d.dprogram waflib.Tools.d.dshlib waflib.Tools.d.dstlib waflib.Tools.ccroot.fake_shlib waflib.Tools.ccroot.fake_stlib waflib.Tools.asm.asmprogram waflib.Tools.asm.asmshlib waflib.Tools.asm.asmstlib 131 """ 132 color = 'YELLOW' 133 134 weight = 3 135 """Try to process link tasks as early as possible""" 136 137 inst_to = None 138 """Default installation path for the link task outputs, or None to disable""" 139 140 chmod = Utils.O755 141 """Default installation mode for the link task outputs""" 142 143 def add_target(self, target): 144 """ 145 Process the *target* attribute to add the platform-specific prefix/suffix such as *.so* or *.exe*. 146 The settings are retrieved from ``env.clsname_PATTERN`` 147 """ 148 if isinstance(target, str): 149 base = self.generator.path 150 if target.startswith('#'): 151 # for those who like flat structures 152 target = target[1:] 153 base = self.generator.bld.bldnode 154 155 pattern = self.env[self.__class__.__name__ + '_PATTERN'] 156 if not pattern: 157 pattern = '%s' 158 folder, name = os.path.split(target) 159 160 if self.__class__.__name__.find('shlib') > 0 and getattr(self.generator, 'vnum', None): 161 nums = self.generator.vnum.split('.') 162 if self.env.DEST_BINFMT == 'pe': 163 # include the version in the dll file name, 164 # the import lib file name stays unversioned. 165 name = name + '-' + nums[0] 166 elif self.env.DEST_OS == 'openbsd': 167 pattern = '%s.%s' % (pattern, nums[0]) 168 if len(nums) >= 2: 169 pattern += '.%s' % nums[1] 170 171 if folder: 172 tmp = folder + os.sep + pattern % name 173 else: 174 tmp = pattern % name 175 target = base.find_or_declare(tmp) 176 self.set_outputs(target) 177 178 def exec_command(self, *k, **kw): 179 ret = super(link_task, self).exec_command(*k, **kw) 180 if not ret and self.env.DO_MANIFEST: 181 ret = self.exec_mf() 182 return ret 183 184 def exec_mf(self): 185 """ 186 Create manifest files for VS-like compilers (msvc, ifort, ...) 187 """ 188 if not self.env.MT: 189 return 0 190 191 manifest = None 192 for out_node in self.outputs: 193 if out_node.name.endswith('.manifest'): 194 manifest = out_node.abspath() 195 break 196 else: 197 # Should never get here. If we do, it means the manifest file was 198 # never added to the outputs list, thus we don't have a manifest file 199 # to embed, so we just return. 200 return 0 201 202 # embedding mode. Different for EXE's and DLL's. 203 # see: http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx 204 mode = '' 205 for x in Utils.to_list(self.generator.features): 206 if x in ('cprogram', 'cxxprogram', 'fcprogram', 'fcprogram_test'): 207 mode = 1 208 elif x in ('cshlib', 'cxxshlib', 'fcshlib'): 209 mode = 2 210 211 Logs.debug('msvc: embedding manifest in mode %r', mode) 212 213 lst = [] + self.env.MT 214 lst.extend(Utils.to_list(self.env.MTFLAGS)) 215 lst.extend(['-manifest', manifest]) 216 lst.append('-outputresource:%s;%s' % (self.outputs[0].abspath(), mode)) 217 218 return super(link_task, self).exec_command(lst) 219 220class stlink_task(link_task): 221 """ 222 Base for static link tasks, which use *ar* most of the time. 223 The target is always removed before being written. 224 """ 225 run_str = '${AR} ${ARFLAGS} ${AR_TGT_F}${TGT} ${AR_SRC_F}${SRC}' 226 227 chmod = Utils.O644 228 """Default installation mode for the static libraries""" 229 230def rm_tgt(cls): 231 old = cls.run 232 def wrap(self): 233 try: 234 os.remove(self.outputs[0].abspath()) 235 except OSError: 236 pass 237 return old(self) 238 setattr(cls, 'run', wrap) 239rm_tgt(stlink_task) 240 241@feature('skip_stlib_link_deps') 242@before_method('process_use') 243def apply_skip_stlib_link_deps(self): 244 """ 245 This enables an optimization in the :py:func:wafilb.Tools.ccroot.processes_use: method that skips dependency and 246 link flag optimizations for targets that generate static libraries (via the :py:class:Tools.ccroot.stlink_task task). 247 The actual behavior is implemented in :py:func:wafilb.Tools.ccroot.processes_use: method so this feature only tells waf 248 to enable the new behavior. 249 """ 250 self.env.SKIP_STLIB_LINK_DEPS = True 251 252@feature('c', 'cxx', 'd', 'fc', 'asm') 253@after_method('process_source') 254def apply_link(self): 255 """ 256 Collect the tasks stored in ``compiled_tasks`` (created by :py:func:`waflib.Tools.ccroot.create_compiled_task`), and 257 use the outputs for a new instance of :py:class:`waflib.Tools.ccroot.link_task`. The class to use is the first link task 258 matching a name from the attribute *features*, for example:: 259 260 def build(bld): 261 tg = bld(features='cxx cxxprogram cprogram', source='main.c', target='app') 262 263 will create the task ``tg.link_task`` as a new instance of :py:class:`waflib.Tools.cxx.cxxprogram` 264 """ 265 266 for x in self.features: 267 if x == 'cprogram' and 'cxx' in self.features: # limited compat 268 x = 'cxxprogram' 269 elif x == 'cshlib' and 'cxx' in self.features: 270 x = 'cxxshlib' 271 272 if x in Task.classes: 273 if issubclass(Task.classes[x], link_task): 274 link = x 275 break 276 else: 277 return 278 279 objs = [t.outputs[0] for t in getattr(self, 'compiled_tasks', [])] 280 self.link_task = self.create_task(link, objs) 281 self.link_task.add_target(self.target) 282 283 # remember that the install paths are given by the task generators 284 try: 285 inst_to = self.install_path 286 except AttributeError: 287 inst_to = self.link_task.inst_to 288 if inst_to: 289 # install a copy of the node list we have at this moment (implib not added) 290 self.install_task = self.add_install_files( 291 install_to=inst_to, install_from=self.link_task.outputs[:], 292 chmod=self.link_task.chmod, task=self.link_task) 293 294@taskgen_method 295def use_rec(self, name, **kw): 296 """ 297 Processes the ``use`` keyword recursively. This method is kind of private and only meant to be used from ``process_use`` 298 """ 299 300 if name in self.tmp_use_not or name in self.tmp_use_seen: 301 return 302 303 try: 304 y = self.bld.get_tgen_by_name(name) 305 except Errors.WafError: 306 self.uselib.append(name) 307 self.tmp_use_not.add(name) 308 return 309 310 self.tmp_use_seen.append(name) 311 y.post() 312 313 # bind temporary attributes on the task generator 314 y.tmp_use_objects = objects = kw.get('objects', True) 315 y.tmp_use_stlib = stlib = kw.get('stlib', True) 316 try: 317 link_task = y.link_task 318 except AttributeError: 319 y.tmp_use_var = '' 320 else: 321 objects = False 322 if not isinstance(link_task, stlink_task): 323 stlib = False 324 y.tmp_use_var = 'LIB' 325 else: 326 y.tmp_use_var = 'STLIB' 327 328 p = self.tmp_use_prec 329 for x in self.to_list(getattr(y, 'use', [])): 330 if self.env["STLIB_" + x]: 331 continue 332 try: 333 p[x].append(name) 334 except KeyError: 335 p[x] = [name] 336 self.use_rec(x, objects=objects, stlib=stlib) 337 338@feature('c', 'cxx', 'd', 'use', 'fc') 339@before_method('apply_incpaths', 'propagate_uselib_vars') 340@after_method('apply_link', 'process_source') 341def process_use(self): 342 """ 343 Process the ``use`` attribute which contains a list of task generator names:: 344 345 def build(bld): 346 bld.shlib(source='a.c', target='lib1') 347 bld.program(source='main.c', target='app', use='lib1') 348 349 See :py:func:`waflib.Tools.ccroot.use_rec`. 350 """ 351 352 use_not = self.tmp_use_not = set() 353 self.tmp_use_seen = [] # we would like an ordered set 354 use_prec = self.tmp_use_prec = {} 355 self.uselib = self.to_list(getattr(self, 'uselib', [])) 356 self.includes = self.to_list(getattr(self, 'includes', [])) 357 names = self.to_list(getattr(self, 'use', [])) 358 359 for x in names: 360 self.use_rec(x) 361 362 for x in use_not: 363 if x in use_prec: 364 del use_prec[x] 365 366 # topological sort 367 out = self.tmp_use_sorted = [] 368 tmp = [] 369 for x in self.tmp_use_seen: 370 for k in use_prec.values(): 371 if x in k: 372 break 373 else: 374 tmp.append(x) 375 376 while tmp: 377 e = tmp.pop() 378 out.append(e) 379 try: 380 nlst = use_prec[e] 381 except KeyError: 382 pass 383 else: 384 del use_prec[e] 385 for x in nlst: 386 for y in use_prec: 387 if x in use_prec[y]: 388 break 389 else: 390 tmp.append(x) 391 if use_prec: 392 raise Errors.WafError('Cycle detected in the use processing %r' % use_prec) 393 out.reverse() 394 395 link_task = getattr(self, 'link_task', None) 396 for x in out: 397 y = self.bld.get_tgen_by_name(x) 398 var = y.tmp_use_var 399 if var and link_task: 400 if self.env.SKIP_STLIB_LINK_DEPS and isinstance(link_task, stlink_task): 401 # If the skip_stlib_link_deps feature is enabled then we should 402 # avoid adding lib deps to the stlink_task instance. 403 pass 404 elif var == 'LIB' or y.tmp_use_stlib or x in names: 405 self.env.append_value(var, [y.target[y.target.rfind(os.sep) + 1:]]) 406 self.link_task.dep_nodes.extend(y.link_task.outputs) 407 tmp_path = y.link_task.outputs[0].parent.path_from(self.get_cwd()) 408 self.env.append_unique(var + 'PATH', [tmp_path]) 409 else: 410 if y.tmp_use_objects: 411 self.add_objects_from_tgen(y) 412 413 if getattr(y, 'export_includes', None): 414 # self.includes may come from a global variable #2035 415 self.includes = self.includes + y.to_incnodes(y.export_includes) 416 417 if getattr(y, 'export_defines', None): 418 self.env.append_value('DEFINES', self.to_list(y.export_defines)) 419 420 421 # and finally, add the use variables (no recursion needed) 422 for x in names: 423 try: 424 y = self.bld.get_tgen_by_name(x) 425 except Errors.WafError: 426 if not self.env['STLIB_' + x] and not x in self.uselib: 427 self.uselib.append(x) 428 else: 429 for k in self.to_list(getattr(y, 'use', [])): 430 if not self.env['STLIB_' + k] and not k in self.uselib: 431 self.uselib.append(k) 432 433@taskgen_method 434def accept_node_to_link(self, node): 435 """ 436 PRIVATE INTERNAL USE ONLY 437 """ 438 return not node.name.endswith('.pdb') 439 440@taskgen_method 441def add_objects_from_tgen(self, tg): 442 """ 443 Add the objects from the depending compiled tasks as link task inputs. 444 445 Some objects are filtered: for instance, .pdb files are added 446 to the compiled tasks but not to the link tasks (to avoid errors) 447 PRIVATE INTERNAL USE ONLY 448 """ 449 try: 450 link_task = self.link_task 451 except AttributeError: 452 pass 453 else: 454 for tsk in getattr(tg, 'compiled_tasks', []): 455 for x in tsk.outputs: 456 if self.accept_node_to_link(x): 457 link_task.inputs.append(x) 458 459@taskgen_method 460def get_uselib_vars(self): 461 """ 462 :return: the *uselib* variables associated to the *features* attribute (see :py:attr:`waflib.Tools.ccroot.USELIB_VARS`) 463 :rtype: list of string 464 """ 465 _vars = set() 466 for x in self.features: 467 if x in USELIB_VARS: 468 _vars |= USELIB_VARS[x] 469 return _vars 470 471@feature('c', 'cxx', 'd', 'fc', 'javac', 'cs', 'uselib', 'asm') 472@after_method('process_use') 473def propagate_uselib_vars(self): 474 """ 475 Process uselib variables for adding flags. For example, the following target:: 476 477 def build(bld): 478 bld.env.AFLAGS_aaa = ['bar'] 479 from waflib.Tools.ccroot import USELIB_VARS 480 USELIB_VARS['aaa'] = ['AFLAGS'] 481 482 tg = bld(features='aaa', aflags='test') 483 484 The *aflags* attribute will be processed and this method will set:: 485 486 tg.env.AFLAGS = ['bar', 'test'] 487 """ 488 _vars = self.get_uselib_vars() 489 env = self.env 490 app = env.append_value 491 feature_uselib = self.features + self.to_list(getattr(self, 'uselib', [])) 492 for var in _vars: 493 y = var.lower() 494 val = getattr(self, y, []) 495 if val: 496 app(var, self.to_list(val)) 497 498 for x in feature_uselib: 499 val = env['%s_%s' % (var, x)] 500 if val: 501 app(var, val) 502 503# ============ the code above must not know anything about import libs ========== 504 505@feature('cshlib', 'cxxshlib', 'fcshlib') 506@after_method('apply_link') 507def apply_implib(self): 508 """ 509 Handle dlls and their import libs on Windows-like systems. 510 511 A ``.dll.a`` file called *import library* is generated. 512 It must be installed as it is required for linking the library. 513 """ 514 if not self.env.DEST_BINFMT == 'pe': 515 return 516 517 dll = self.link_task.outputs[0] 518 if isinstance(self.target, Node.Node): 519 name = self.target.name 520 else: 521 name = os.path.split(self.target)[1] 522 implib = self.env.implib_PATTERN % name 523 implib = dll.parent.find_or_declare(implib) 524 self.env.append_value('LINKFLAGS', self.env.IMPLIB_ST % implib.bldpath()) 525 self.link_task.outputs.append(implib) 526 527 if getattr(self, 'defs', None) and self.env.DEST_BINFMT == 'pe': 528 node = self.path.find_resource(self.defs) 529 if not node: 530 raise Errors.WafError('invalid def file %r' % self.defs) 531 if self.env.def_PATTERN: 532 self.env.append_value('LINKFLAGS', self.env.def_PATTERN % node.path_from(self.get_cwd())) 533 self.link_task.dep_nodes.append(node) 534 else: 535 # gcc for windows takes *.def file as input without any special flag 536 self.link_task.inputs.append(node) 537 538 # where to put the import library 539 if getattr(self, 'install_task', None): 540 try: 541 # user has given a specific installation path for the import library 542 inst_to = self.install_path_implib 543 except AttributeError: 544 try: 545 # user has given an installation path for the main library, put the import library in it 546 inst_to = self.install_path 547 except AttributeError: 548 # else, put the library in BINDIR and the import library in LIBDIR 549 inst_to = '${IMPLIBDIR}' 550 self.install_task.install_to = '${BINDIR}' 551 if not self.env.IMPLIBDIR: 552 self.env.IMPLIBDIR = self.env.LIBDIR 553 self.implib_install_task = self.add_install_files(install_to=inst_to, install_from=implib, 554 chmod=self.link_task.chmod, task=self.link_task) 555 556# ============ the code above must not know anything about vnum processing on unix platforms ========= 557 558re_vnum = re.compile('^([1-9]\\d*|0)([.]([1-9]\\d*|0)){0,2}?$') 559@feature('cshlib', 'cxxshlib', 'dshlib', 'fcshlib', 'vnum') 560@after_method('apply_link', 'propagate_uselib_vars') 561def apply_vnum(self): 562 """ 563 Enforce version numbering on shared libraries. The valid version numbers must have either zero or two dots:: 564 565 def build(bld): 566 bld.shlib(source='a.c', target='foo', vnum='14.15.16') 567 568 In this example on Linux platform, ``libfoo.so`` is installed as ``libfoo.so.14.15.16``, and the following symbolic links are created: 569 570 * ``libfoo.so → libfoo.so.14.15.16`` 571 * ``libfoo.so.14 → libfoo.so.14.15.16`` 572 573 By default, the library will be assigned SONAME ``libfoo.so.14``, effectively declaring ABI compatibility between all minor and patch releases for the major version of the library. When necessary, the compatibility can be explicitly defined using `cnum` parameter: 574 575 def build(bld): 576 bld.shlib(source='a.c', target='foo', vnum='14.15.16', cnum='14.15') 577 578 In this case, the assigned SONAME will be ``libfoo.so.14.15`` with ABI compatibility only between path releases for a specific major and minor version of the library. 579 580 On OS X platform, install-name parameter will follow the above logic for SONAME with exception that it also specifies an absolute path (based on install_path) of the library. 581 """ 582 if not getattr(self, 'vnum', '') or os.name != 'posix' or self.env.DEST_BINFMT not in ('elf', 'mac-o'): 583 return 584 585 link = self.link_task 586 if not re_vnum.match(self.vnum): 587 raise Errors.WafError('Invalid vnum %r for target %r' % (self.vnum, getattr(self, 'name', self))) 588 nums = self.vnum.split('.') 589 node = link.outputs[0] 590 591 cnum = getattr(self, 'cnum', str(nums[0])) 592 cnums = cnum.split('.') 593 if len(cnums)>len(nums) or nums[0:len(cnums)] != cnums: 594 raise Errors.WafError('invalid compatibility version %s' % cnum) 595 596 libname = node.name 597 if libname.endswith('.dylib'): 598 name3 = libname.replace('.dylib', '.%s.dylib' % self.vnum) 599 name2 = libname.replace('.dylib', '.%s.dylib' % cnum) 600 else: 601 name3 = libname + '.' + self.vnum 602 name2 = libname + '.' + cnum 603 604 # add the so name for the ld linker - to disable, just unset env.SONAME_ST 605 if self.env.SONAME_ST: 606 v = self.env.SONAME_ST % name2 607 self.env.append_value('LINKFLAGS', v.split()) 608 609 # the following task is just to enable execution from the build dir :-/ 610 if self.env.DEST_OS != 'openbsd': 611 outs = [node.parent.make_node(name3)] 612 if name2 != name3: 613 outs.append(node.parent.make_node(name2)) 614 self.create_task('vnum', node, outs) 615 616 if getattr(self, 'install_task', None): 617 self.install_task.hasrun = Task.SKIPPED 618 self.install_task.no_errcheck_out = True 619 path = self.install_task.install_to 620 if self.env.DEST_OS == 'openbsd': 621 libname = self.link_task.outputs[0].name 622 t1 = self.add_install_as(install_to='%s/%s' % (path, libname), install_from=node, chmod=self.link_task.chmod) 623 self.vnum_install_task = (t1,) 624 else: 625 t1 = self.add_install_as(install_to=path + os.sep + name3, install_from=node, chmod=self.link_task.chmod) 626 t3 = self.add_symlink_as(install_to=path + os.sep + libname, install_from=name3) 627 if name2 != name3: 628 t2 = self.add_symlink_as(install_to=path + os.sep + name2, install_from=name3) 629 self.vnum_install_task = (t1, t2, t3) 630 else: 631 self.vnum_install_task = (t1, t3) 632 633 if '-dynamiclib' in self.env.LINKFLAGS: 634 # this requires after(propagate_uselib_vars) 635 try: 636 inst_to = self.install_path 637 except AttributeError: 638 inst_to = self.link_task.inst_to 639 if inst_to: 640 p = Utils.subst_vars(inst_to, self.env) 641 path = os.path.join(p, name2) 642 self.env.append_value('LINKFLAGS', ['-install_name', path]) 643 self.env.append_value('LINKFLAGS', '-Wl,-compatibility_version,%s' % cnum) 644 self.env.append_value('LINKFLAGS', '-Wl,-current_version,%s' % self.vnum) 645 646class vnum(Task.Task): 647 """ 648 Create the symbolic links for a versioned shared library. Instances are created by :py:func:`waflib.Tools.ccroot.apply_vnum` 649 """ 650 color = 'CYAN' 651 ext_in = ['.bin'] 652 def keyword(self): 653 return 'Symlinking' 654 def run(self): 655 for x in self.outputs: 656 path = x.abspath() 657 try: 658 os.remove(path) 659 except OSError: 660 pass 661 662 try: 663 os.symlink(self.inputs[0].name, path) 664 except OSError: 665 return 1 666 667class fake_shlib(link_task): 668 """ 669 Task used for reading a system library and adding the dependency on it 670 """ 671 def runnable_status(self): 672 for t in self.run_after: 673 if not t.hasrun: 674 return Task.ASK_LATER 675 return Task.SKIP_ME 676 677class fake_stlib(stlink_task): 678 """ 679 Task used for reading a system library and adding the dependency on it 680 """ 681 def runnable_status(self): 682 for t in self.run_after: 683 if not t.hasrun: 684 return Task.ASK_LATER 685 return Task.SKIP_ME 686 687@conf 688def read_shlib(self, name, paths=[], export_includes=[], export_defines=[]): 689 """ 690 Read a system shared library, enabling its use as a local library. Will trigger a rebuild if the file changes:: 691 692 def build(bld): 693 bld.read_shlib('m') 694 bld.program(source='main.c', use='m') 695 """ 696 return self(name=name, features='fake_lib', lib_paths=paths, lib_type='shlib', export_includes=export_includes, export_defines=export_defines) 697 698@conf 699def read_stlib(self, name, paths=[], export_includes=[], export_defines=[]): 700 """ 701 Read a system static library, enabling a use as a local library. Will trigger a rebuild if the file changes. 702 """ 703 return self(name=name, features='fake_lib', lib_paths=paths, lib_type='stlib', export_includes=export_includes, export_defines=export_defines) 704 705lib_patterns = { 706 'shlib' : ['lib%s.so', '%s.so', 'lib%s.dylib', 'lib%s.dll', '%s.dll'], 707 'stlib' : ['lib%s.a', '%s.a', 'lib%s.dll', '%s.dll', 'lib%s.lib', '%s.lib'], 708} 709 710@feature('fake_lib') 711def process_lib(self): 712 """ 713 Find the location of a foreign library. Used by :py:class:`waflib.Tools.ccroot.read_shlib` and :py:class:`waflib.Tools.ccroot.read_stlib`. 714 """ 715 node = None 716 717 names = [x % self.name for x in lib_patterns[self.lib_type]] 718 for x in self.lib_paths + [self.path] + SYSTEM_LIB_PATHS: 719 if not isinstance(x, Node.Node): 720 x = self.bld.root.find_node(x) or self.path.find_node(x) 721 if not x: 722 continue 723 724 for y in names: 725 node = x.find_node(y) 726 if node: 727 try: 728 Utils.h_file(node.abspath()) 729 except EnvironmentError: 730 raise ValueError('Could not read %r' % y) 731 break 732 else: 733 continue 734 break 735 else: 736 raise Errors.WafError('could not find library %r' % self.name) 737 self.link_task = self.create_task('fake_%s' % self.lib_type, [], [node]) 738 self.target = self.name 739 740 741class fake_o(Task.Task): 742 def runnable_status(self): 743 return Task.SKIP_ME 744 745@extension('.o', '.obj') 746def add_those_o_files(self, node): 747 tsk = self.create_task('fake_o', [], node) 748 try: 749 self.compiled_tasks.append(tsk) 750 except AttributeError: 751 self.compiled_tasks = [tsk] 752 753@feature('fake_obj') 754@before_method('process_source') 755def process_objs(self): 756 """ 757 Puts object files in the task generator outputs 758 """ 759 for node in self.to_nodes(self.source): 760 self.add_those_o_files(node) 761 self.source = [] 762 763@conf 764def read_object(self, obj): 765 """ 766 Read an object file, enabling injection in libs/programs. Will trigger a rebuild if the file changes. 767 768 :param obj: object file path, as string or Node 769 """ 770 if not isinstance(obj, self.path.__class__): 771 obj = self.path.find_resource(obj) 772 return self(features='fake_obj', source=obj, name=obj.name) 773 774@feature('cxxprogram', 'cprogram') 775@after_method('apply_link', 'process_use') 776def set_full_paths_hpux(self): 777 """ 778 On hp-ux, extend the libpaths and static library paths to absolute paths 779 """ 780 if self.env.DEST_OS != 'hp-ux': 781 return 782 base = self.bld.bldnode.abspath() 783 for var in ['LIBPATH', 'STLIBPATH']: 784 lst = [] 785 for x in self.env[var]: 786 if x.startswith('/'): 787 lst.append(x) 788 else: 789 lst.append(os.path.normpath(os.path.join(base, x))) 790 self.env[var] = lst 791 792