Back to index

enigmail  1.4.3
expandlibs.py
Go to the documentation of this file.
00001 # ***** BEGIN LICENSE BLOCK *****
00002 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003 #
00004 # The contents of this file are subject to the Mozilla Public License Version
00005 # 1.1 (the "License"); you may not use this file except in compliance with
00006 # the License. You may obtain a copy of the License at
00007 # http://www.mozilla.org/MPL/
00008 #
00009 # Software distributed under the License is distributed on an "AS IS" basis,
00010 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011 # for the specific language governing rights and limitations under the
00012 # License.
00013 #
00014 # The Original Code is a build helper for libraries
00015 #
00016 # The Initial Developer of the Original Code is
00017 # the Mozilla Foundation
00018 # Portions created by the Initial Developer are Copyright (C) 2011
00019 # the Initial Developer. All Rights Reserved.
00020 #
00021 # Contributor(s):
00022 # Mike Hommey <mh@glandium.org>
00023 #
00024 # Alternatively, the contents of this file may be used under the terms of
00025 # either the GNU General Public License Version 2 or later (the "GPL"), or
00026 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027 # in which case the provisions of the GPL or the LGPL are applicable instead
00028 # of those above. If you wish to allow use of your version of this file only
00029 # under the terms of either the GPL or the LGPL, and not to allow others to
00030 # use your version of this file under the terms of the MPL, indicate your
00031 # decision by deleting the provisions above and replace them with the notice
00032 # and other provisions required by the GPL or the LGPL. If you do not delete
00033 # the provisions above, a recipient may use your version of this file under
00034 # the terms of any one of the MPL, the GPL or the LGPL.
00035 #
00036 # ***** END LICENSE BLOCK *****
00037 
00038 '''Expandlibs is a system that allows to replace some libraries with a
00039 descriptor file containing some linking information about them.
00040 
00041 The descriptor file format is as follows:
00042 ---8<-----
00043 OBJS = a.o b.o ...
00044 LIBS = libfoo.a libbar.a ...
00045 --->8-----
00046 
00047 (In the example above, OBJ_SUFFIX is o and LIB_SUFFIX is a).
00048 
00049 Expandlibs also canonicalizes how to pass libraries to the linker, such
00050 that only the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} form needs to be used:
00051 given a list of files, expandlibs will replace items with the form
00052 ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} following these rules:
00053 
00054 - If a ${DLL_PREFIX}${ROOT}.${DLL_SUFFIX} or
00055   ${DLL_PREFIX}${ROOT}.${IMPORT_LIB_SUFFIX} file exists, use that instead
00056 - If the ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} file exists, use it
00057 - If a ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX}.${LIB_DESC_SUFFIX} file exists,
00058   replace ${LIB_PREFIX}${ROOT}.${LIB_SUFFIX} with the OBJS and LIBS the
00059   descriptor contains. And for each of these LIBS, also apply the same
00060   rules.
00061 '''
00062 from __future__ import with_statement
00063 import sys, os
00064 import expandlibs_config as conf
00065 
00066 def relativize(path):
00067     '''Returns a path relative to the current working directory, if it is
00068     shorter than the given path'''
00069     def splitpath(path):
00070         dir, file = os.path.split(path)
00071         if os.path.splitdrive(dir)[1] == os.sep:
00072             return [file]
00073         return splitpath(dir) + [file]
00074 
00075     if not os.path.exists(path):
00076         return path
00077     curdir = splitpath(os.path.abspath(os.curdir))
00078     abspath = splitpath(os.path.abspath(path))
00079     while curdir and abspath and curdir[0] == abspath[0]:
00080         del curdir[0]
00081         del abspath[0]
00082     if not curdir and not abspath:
00083         return '.'
00084     relpath = os.path.join(*[os.pardir for i in curdir] + abspath)
00085     if len(path) > len(relpath):
00086         return relpath
00087     return path
00088 
00089 def isObject(path):
00090     '''Returns whether the given path points to an object file, that is,
00091     ends with OBJ_SUFFIX or .i_o'''
00092     return os.path.splitext(path)[1] in [conf.OBJ_SUFFIX, '.i_o']
00093 
00094 class LibDescriptor(dict):
00095     KEYS = ['OBJS', 'LIBS']
00096 
00097     def __init__(self, content=None):
00098         '''Creates an instance of a lib descriptor, initialized with contents
00099         from a list of strings when given. This is intended for use with
00100         file.readlines()'''
00101         if isinstance(content, list) and all([isinstance(item, str) for item in content]):
00102             pass
00103         elif content is not None:
00104             raise TypeError("LibDescriptor() arg 1 must be None or a list of strings")
00105         super(LibDescriptor, self).__init__()
00106         for key in self.KEYS:
00107             self[key] = []
00108         if not content:
00109             return
00110         for key, value in [(s.strip() for s in item.split('=', 2)) for item in content if item.find('=') >= 0]:
00111             if key in self.KEYS:
00112                 self[key] = value.split()
00113 
00114     def __str__(self):
00115         '''Serializes the lib descriptor'''
00116         return '\n'.join('%s = %s' % (k, ' '.join(self[k])) for k in self.KEYS if len(self[k]))
00117 
00118 class ExpandArgs(list):
00119     def __init__(self, args):
00120         '''Creates a clone of the |args| list and performs file expansion on
00121         each item it contains'''
00122         super(ExpandArgs, self).__init__()
00123         for arg in args:
00124             self += self._expand(arg)
00125 
00126     def _expand(self, arg):
00127         '''Internal function doing the actual work'''
00128         (root, ext) = os.path.splitext(arg)
00129         if ext != conf.LIB_SUFFIX or not os.path.basename(root).startswith(conf.LIB_PREFIX):
00130             return [relativize(arg)]
00131         if len(conf.IMPORT_LIB_SUFFIX):
00132             dll = root + conf.IMPORT_LIB_SUFFIX
00133         else:
00134             dll = root.replace(conf.LIB_PREFIX, conf.DLL_PREFIX, 1) + conf.DLL_SUFFIX
00135         if os.path.exists(dll):
00136             return [relativize(dll)]
00137         if os.path.exists(arg):
00138             return [relativize(arg)]
00139         return self._expand_desc(arg)
00140 
00141     def _expand_desc(self, arg):
00142         '''Internal function taking care of lib descriptor expansion only'''
00143         if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
00144             with open(arg + conf.LIBS_DESC_SUFFIX, 'r') as f:
00145                 desc = LibDescriptor(f.readlines())
00146             objs = [relativize(o) for o in desc['OBJS']]
00147             for lib in desc['LIBS']:
00148                 objs += self._expand(lib)
00149             return objs
00150         return [arg]
00151 
00152 if __name__ == '__main__':
00153     print " ".join(ExpandArgs(sys.argv[1:]))