expandlibs.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. # This Source Code Form is subject to the terms of the Mozilla Public
  2. # License, v. 2.0. If a copy of the MPL was not distributed with this
  3. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. '''Expandlibs is a system that allows to replace some libraries with a
  5. descriptor file containing some linking information about them.
  6. The descriptor file format is as follows:
  7. ---8<-----
  8. OBJS = a.o b.o ...
  9. LIBS = libfoo.a libbar.a ...
  10. --->8-----
  11. (In the example above, OBJ_SUFFIX is o and LIB_SUFFIX is a).
  12. Expandlibs also canonicalizes how to pass libraries to the linker, such
  13. that only the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} form needs to be used:
  14. given a list of files, expandlibs will replace items with the form
  15. ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} following these rules:
  16. - If a ${DLL_PREFIX}${ROOT}.${DLL_SUFFIX} or
  17. ${DLL_PREFIX}${ROOT}.${IMPORT_LIB_SUFFIX} file exists, use that instead
  18. - If the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} file exists, use it
  19. - If a ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX}.${LIB_DESC_SUFFIX} file exists,
  20. replace ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} with the OBJS and LIBS the
  21. descriptor contains. And for each of these LIBS, also apply the same
  22. rules.
  23. '''
  24. from __future__ import with_statement
  25. import sys, os, errno
  26. import expandlibs_config as conf
  27. def ensureParentDir(file):
  28. '''Ensures the directory parent to the given file exists'''
  29. dir = os.path.dirname(file)
  30. if dir and not os.path.exists(dir):
  31. try:
  32. os.makedirs(dir)
  33. except OSError, error:
  34. if error.errno != errno.EEXIST:
  35. raise
  36. def relativize(path):
  37. '''Returns a path relative to the current working directory, if it is
  38. shorter than the given path'''
  39. def splitpath(path):
  40. dir, file = os.path.split(path)
  41. if os.path.splitdrive(dir)[1] == os.sep:
  42. return [file]
  43. return splitpath(dir) + [file]
  44. if not os.path.exists(path):
  45. return path
  46. curdir = splitpath(os.path.abspath(os.curdir))
  47. abspath = splitpath(os.path.abspath(path))
  48. while curdir and abspath and curdir[0] == abspath[0]:
  49. del curdir[0]
  50. del abspath[0]
  51. if not curdir and not abspath:
  52. return '.'
  53. relpath = os.path.join(*[os.pardir for i in curdir] + abspath)
  54. if len(path) > len(relpath):
  55. return relpath
  56. return path
  57. def isObject(path):
  58. '''Returns whether the given path points to an object file, that is,
  59. ends with OBJ_SUFFIX or .i_o'''
  60. return os.path.splitext(path)[1] in [conf.OBJ_SUFFIX, '.i_o']
  61. def isDynamicLib(path):
  62. '''Returns whether the given path points to a dynamic library, that is,
  63. ends with DLL_SUFFIX.'''
  64. # On mac, the xul library is named XUL, instead of libxul.dylib. Assume any
  65. # file by that name is a dynamic library.
  66. return os.path.splitext(path)[1] == conf.DLL_SUFFIX or os.path.basename(path) == 'XUL'
  67. class LibDescriptor(dict):
  68. KEYS = ['OBJS', 'LIBS']
  69. def __init__(self, content=None):
  70. '''Creates an instance of a lib descriptor, initialized with contents
  71. from a list of strings when given. This is intended for use with
  72. file.readlines()'''
  73. if isinstance(content, list) and all([isinstance(item, str) for item in content]):
  74. pass
  75. elif content is not None:
  76. raise TypeError("LibDescriptor() arg 1 must be None or a list of strings")
  77. super(LibDescriptor, self).__init__()
  78. for key in self.KEYS:
  79. self[key] = []
  80. if not content:
  81. return
  82. for key, value in [(s.strip() for s in item.split('=', 2)) for item in content if item.find('=') >= 0]:
  83. if key in self.KEYS:
  84. self[key] = value.split()
  85. def __str__(self):
  86. '''Serializes the lib descriptor'''
  87. return '\n'.join('%s = %s' % (k, ' '.join(self[k])) for k in self.KEYS if len(self[k]))
  88. class ExpandArgs(list):
  89. def __init__(self, args):
  90. '''Creates a clone of the |args| list and performs file expansion on
  91. each item it contains'''
  92. super(ExpandArgs, self).__init__()
  93. self._descs = set()
  94. for arg in args:
  95. self += self._expand(arg)
  96. def _expand(self, arg):
  97. '''Internal function doing the actual work'''
  98. (root, ext) = os.path.splitext(arg)
  99. if ext != conf.LIB_SUFFIX or not os.path.basename(root).startswith(conf.LIB_PREFIX):
  100. return [relativize(arg)]
  101. if conf.LIB_PREFIX:
  102. dll = root.replace(conf.LIB_PREFIX, conf.DLL_PREFIX, 1) + conf.DLL_SUFFIX
  103. else:
  104. dll = root + conf.DLL_SUFFIX
  105. if os.path.exists(dll):
  106. if conf.IMPORT_LIB_SUFFIX:
  107. return [relativize(root + conf.IMPORT_LIB_SUFFIX)]
  108. else:
  109. return [relativize(dll)]
  110. return self._expand_desc(arg)
  111. def _expand_desc(self, arg):
  112. '''Internal function taking care of lib descriptor expansion only'''
  113. desc = os.path.abspath(arg + conf.LIBS_DESC_SUFFIX)
  114. if os.path.exists(desc):
  115. if desc in self._descs:
  116. return []
  117. self._descs.add(desc)
  118. with open(desc, 'r') as f:
  119. desc = LibDescriptor(f.readlines())
  120. objs = [relativize(o) for o in desc['OBJS']]
  121. for lib in desc['LIBS']:
  122. objs += self._expand(lib)
  123. return objs
  124. return [relativize(arg)]
  125. if __name__ == '__main__':
  126. print " ".join(ExpandArgs(sys.argv[1:]))