~jacksonc/python-packager/devel

« back to all changes in this revision

Viewing changes to Build-Server/pyinstaller/windows-2.6/mf.py

  • Committer: Jackson Cooper
  • Date: 2010-04-13 03:14:06 UTC
  • Revision ID: jackson@jacksonc.com-20100413031406-415e68ruamsfc5se
Added pyinstaller for Windows

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# Copyright (C) 2005, Giovanni Bajo
 
3
#
 
4
# Based on previous work under copyright (c) 2002 McMillan Enterprises, Inc.
 
5
#
 
6
# This program is free software; you can redistribute it and/or
 
7
# modify it under the terms of the GNU General Public License
 
8
# as published by the Free Software Foundation; either version 2
 
9
# of the License, or (at your option) any later version.
 
10
#
 
11
# This program is distributed in the hope that it will be useful,
 
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
# GNU General Public License for more details.
 
15
#
 
16
# You should have received a copy of the GNU General Public License
 
17
# along with this program; if not, write to the Free Software
 
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
19
#
 
20
 
 
21
import sys, string, os, imp, marshal, dircache, glob
 
22
try:
 
23
    # zipimport is supported starting with Python 2.3
 
24
    import zipimport
 
25
except ImportError:
 
26
    zipimport = None
 
27
 
 
28
try:
 
29
    # if ctypes is present, we can enable specific dependency discovery
 
30
    import ctypes
 
31
    from ctypes.util import find_library
 
32
except ImportError:
 
33
    ctypes = None
 
34
 
 
35
import suffixes
 
36
 
 
37
try:
 
38
    STRINGTYPE = basestring
 
39
except NameError:
 
40
    STRINGTYPE = type("")
 
41
 
 
42
if not os.environ.has_key('PYTHONCASEOK') and sys.version_info >= (2, 1):
 
43
    def caseOk(filename):
 
44
        files = dircache.listdir(os.path.dirname(filename))
 
45
        return os.path.basename(filename) in files
 
46
else:
 
47
    def caseOk(filename):
 
48
        return True
 
49
 
 
50
def pyco():
 
51
    """
 
52
    Returns correct extension ending: 'c' or 'o'
 
53
    """
 
54
    if __debug__:
 
55
        return 'c'
 
56
    else:
 
57
        return 'o'
 
58
 
 
59
#=======================Owners==========================#
 
60
# An Owner does imports from a particular piece of turf
 
61
# That is, there's an Owner for each thing on sys.path
 
62
# There are owners for directories and .pyz files.
 
63
# There could be owners for zip files, or even URLs.
 
64
# Note that they replace the string in sys.path,
 
65
# but str(sys.path[n]) should yield the original string.
 
66
 
 
67
class OwnerError(Exception):
 
68
    pass
 
69
 
 
70
class Owner:
 
71
    def __init__(self, path, target_platform=None):
 
72
        self.path = path
 
73
        self.target_platform = target_platform
 
74
 
 
75
    def __str__(self):
 
76
        return self.path
 
77
 
 
78
    def getmod(self, nm):
 
79
        return None
 
80
 
 
81
class BaseDirOwner(Owner):
 
82
    def _getsuffixes(self):
 
83
        return suffixes.get_suffixes(self.target_platform)
 
84
 
 
85
    def getmod(self, nm, getsuffixes=None, loadco=marshal.loads):
 
86
        if getsuffixes is None:
 
87
            getsuffixes = self._getsuffixes
 
88
        possibles = [(nm, 0, None)]
 
89
        if self._isdir(nm) and self._caseok(nm):
 
90
            possibles.insert(0, (os.path.join(nm, '__init__'), 1, nm))
 
91
        py = pyc = None
 
92
        for pth, ispkg, pkgpth in possibles:
 
93
            for ext, mode, typ in getsuffixes():
 
94
                attempt = pth+ext
 
95
                modtime = self._modtime(attempt)
 
96
                if modtime is not None:
 
97
                    # Check case
 
98
                    if not self._caseok(attempt):
 
99
                        continue
 
100
                    if typ == imp.C_EXTENSION:
 
101
                        #print "DirOwner.getmod -> ExtensionModule(%s, %s)" % (nm, attempt)
 
102
                        return ExtensionModule(nm, os.path.join(self.path,attempt))
 
103
                    elif typ == imp.PY_SOURCE:
 
104
                        py = (attempt, modtime)
 
105
                    else:
 
106
                        pyc = (attempt, modtime)
 
107
            if py or pyc:
 
108
                break
 
109
        if py is None and pyc is None:
 
110
            #print "DirOwner.getmod -> (py == pyc == None)"
 
111
            return None
 
112
        while 1:
 
113
            # If we have no pyc or py is newer
 
114
            if pyc is None or py and pyc[1] < py[1]:
 
115
                try:
 
116
                    stuff = self._read(py[0])+'\n'
 
117
                    co = compile(string.replace(stuff, "\r\n", "\n"), py[0], 'exec')
 
118
                    pth = py[0] + pyco()
 
119
                    break
 
120
                except SyntaxError, e:
 
121
                    print "Syntax error in", py[0]
 
122
                    print e.args
 
123
                    raise
 
124
            elif pyc:
 
125
                stuff = self._read(pyc[0])
 
126
                try:
 
127
                    co = loadco(stuff[8:])
 
128
                    pth = pyc[0]
 
129
                    break
 
130
                except (ValueError, EOFError):
 
131
                    print "W: bad .pyc found (%s), will use .py" % pyc[0]
 
132
                    pyc = None
 
133
            else:
 
134
                #print "DirOwner.getmod while 1 -> None"
 
135
                return None
 
136
        pth = os.path.join(self.path, pth)
 
137
        if not os.path.isabs(pth):
 
138
            pth = os.path.abspath(pth)
 
139
        if ispkg:
 
140
            mod = self._pkgclass()(nm, pth, co)
 
141
        else:
 
142
            mod = self._modclass()(nm, pth, co)
 
143
        #print "DirOwner.getmod -> %s" % mod
 
144
        return mod
 
145
 
 
146
class DirOwner(BaseDirOwner):
 
147
    def __init__(self, path, target_platform=None):
 
148
        if path == '':
 
149
            path = os.getcwd()
 
150
        if not os.path.isdir(path):
 
151
            raise OwnerError("%s is not a directory" % repr(path))
 
152
        Owner.__init__(self, path, target_platform)
 
153
 
 
154
    def _isdir(self, fn):
 
155
        return os.path.isdir(os.path.join(self.path, fn))
 
156
    def _modtime(self, fn):
 
157
        try:
 
158
            return os.stat(os.path.join(self.path, fn))[8]
 
159
        except OSError:
 
160
            return None
 
161
    def _read(self, fn):
 
162
        return open(os.path.join(self.path, fn), 'rb').read()
 
163
    def _pkgclass(self):
 
164
        return PkgModule
 
165
    def _modclass(self):
 
166
        return PyModule
 
167
    def _caseok(self, fn):
 
168
        return caseOk(os.path.join(self.path, fn))
 
169
 
 
170
class PYZOwner(Owner):
 
171
    def __init__(self, path, target_platform=None):
 
172
        import archive
 
173
        self.pyz = archive.ZlibArchive(path)
 
174
        Owner.__init__(self, path, target_platform)
 
175
    def getmod(self, nm):
 
176
        rslt = self.pyz.extract(nm)
 
177
        if not rslt:
 
178
            return None
 
179
        ispkg, co = rslt
 
180
        if ispkg:
 
181
            return PkgInPYZModule(nm, co, self)
 
182
        return PyModule(nm, self.path, co)
 
183
 
 
184
 
 
185
ZipOwner = None
 
186
if zipimport:
 
187
    # We cannot use zipimporter here because it has a stupid bug:
 
188
    #
 
189
    # >>> z.find_module("setuptools.setuptools.setuptools.setuptools.setuptools") is not None
 
190
    # True
 
191
    #
 
192
    # So mf will go into infinite recursion.
 
193
    # Instead, we'll reuse the BaseDirOwner logic, simply changing
 
194
    # the template methods.
 
195
    class ZipOwner(BaseDirOwner):
 
196
        def __init__(self, path, target_platform=None):
 
197
            import zipfile
 
198
            try:
 
199
                self.zf = zipfile.ZipFile(path, "r")
 
200
            except IOError:
 
201
                raise OwnerError("%s is not a zipfile" % path)
 
202
            Owner.__init__(self, path, target_platform)
 
203
        def getmod(self, fn):
 
204
            fn = fn.replace(".", "/")
 
205
            return BaseDirOwner.getmod(self, fn)
 
206
        def _modtime(self, fn):
 
207
            fn = fn.replace("\\","/")
 
208
            try:
 
209
                dt = self.zf.getinfo(fn).date_time
 
210
                return dt
 
211
            except KeyError:
 
212
                return None
 
213
        def _isdir(self, fn):
 
214
            # No way to find out if "fn" is a directory
 
215
            # so just always look into it in case it is.
 
216
            return True
 
217
        def _caseok(self, fn):
 
218
            # zipfile is always case-sensitive, so surely
 
219
            # there is no case mismatch.
 
220
            return True
 
221
        def _read(self, fn):
 
222
            # zipfiles always use forward slashes
 
223
            fn = fn.replace("\\","/")
 
224
            return self.zf.read(fn)
 
225
        def _pkgclass(self):
 
226
            return lambda *args: PkgInZipModule(self, *args)
 
227
        def _modclass(self):
 
228
            return lambda *args: PyInZipModule(self, *args)
 
229
 
 
230
_globalownertypes = filter(None, [
 
231
    DirOwner,
 
232
    ZipOwner,
 
233
    PYZOwner,
 
234
    Owner,
 
235
])
 
236
 
 
237
#===================Import Directors====================================#
 
238
# ImportDirectors live on the metapath
 
239
# There's one for builtins, one for frozen modules, and one for sys.path
 
240
# Windows gets one for modules gotten from the Registry
 
241
# There should be one for Frozen modules
 
242
# Mac would have them for PY_RESOURCE modules etc.
 
243
# A generalization of Owner - their concept of "turf" is broader
 
244
 
 
245
class ImportDirector(Owner):
 
246
    pass
 
247
 
 
248
class BuiltinImportDirector(ImportDirector):
 
249
    def __init__(self):
 
250
        self.path = 'Builtins'
 
251
 
 
252
    def getmod(self, nm, isbuiltin=imp.is_builtin):
 
253
        if isbuiltin(nm):
 
254
            return BuiltinModule(nm)
 
255
        return None
 
256
 
 
257
class FrozenImportDirector(ImportDirector):
 
258
    def __init__(self):
 
259
        self.path = 'FrozenModules'
 
260
 
 
261
    def getmod(self, nm, isfrozen=imp.is_frozen):
 
262
        if isfrozen(nm):
 
263
            return FrozenModule(nm)
 
264
        return None
 
265
 
 
266
class RegistryImportDirector(ImportDirector):
 
267
    # for Windows only
 
268
    def __init__(self):
 
269
        self.path = "WindowsRegistry"
 
270
        self.map = {}
 
271
        try:
 
272
            import win32api
 
273
            import win32con
 
274
        except ImportError:
 
275
            pass
 
276
        else:
 
277
            subkey = r"Software\Python\PythonCore\%s\Modules" % sys.winver
 
278
            for root in (win32con.HKEY_CURRENT_USER, win32con.HKEY_LOCAL_MACHINE):
 
279
                try:
 
280
                    #hkey = win32api.RegOpenKeyEx(root, subkey, 0, win32con.KEY_ALL_ACCESS)
 
281
                    hkey = win32api.RegOpenKeyEx(root, subkey, 0, win32con.KEY_READ)
 
282
                except Exception, e:
 
283
                    #print "RegistryImportDirector", e
 
284
                    pass
 
285
                else:
 
286
                    numsubkeys, numvalues, lastmodified = win32api.RegQueryInfoKey(hkey)
 
287
                    for i in range(numsubkeys):
 
288
                        subkeyname = win32api.RegEnumKey(hkey, i)
 
289
                        #hskey = win32api.RegOpenKeyEx(hkey, subkeyname, 0, win32con.KEY_ALL_ACCESS)
 
290
                        hskey = win32api.RegOpenKeyEx(hkey, subkeyname, 0, win32con.KEY_READ)
 
291
                        val = win32api.RegQueryValueEx(hskey, '')
 
292
                        desc = getDescr(val[0])
 
293
                        #print " RegistryImportDirector got %s %s" % (val[0], desc)  #XXX
 
294
                        self.map[subkeyname] = (val[0], desc)
 
295
                        hskey.Close()
 
296
                    hkey.Close()
 
297
                    break
 
298
 
 
299
    def getmod(self, nm):
 
300
        stuff = self.map.get(nm)
 
301
        if stuff:
 
302
            fnm, (suffix, mode, typ) = stuff
 
303
            if typ == imp.C_EXTENSION:
 
304
                return ExtensionModule(nm, fnm)
 
305
            elif typ == imp.PY_SOURCE:
 
306
                try:
 
307
                    stuff = open(fnm, 'r').read()+'\n'
 
308
                    co = compile(string.replace(stuff, "\r\n", "\n"), fnm, 'exec')
 
309
                except SyntaxError, e:
 
310
                    print "Invalid syntax in %s" % py[0]
 
311
                    print e.args
 
312
                    raise
 
313
            else:
 
314
                stuff = open(fnm, 'rb').read()
 
315
                co = loadco(stuff[8:])
 
316
            return PyModule(nm, fnm, co)
 
317
        return None
 
318
 
 
319
class PathImportDirector(ImportDirector):
 
320
    def __init__(self, pathlist=None, importers=None, ownertypes=None,
 
321
                 target_platform=None):
 
322
        if pathlist is None:
 
323
            self.path = sys.path
 
324
        else:
 
325
            self.path = pathlist
 
326
        if ownertypes == None:
 
327
            self.ownertypes = _globalownertypes
 
328
        else:
 
329
            self.ownertypes = ownertypes
 
330
        if importers:
 
331
            self.shadowpath = importers
 
332
        else:
 
333
            self.shadowpath = {}
 
334
        self.inMakeOwner = 0
 
335
        self.building = {}
 
336
        self.target_platform = target_platform
 
337
 
 
338
    def __str__(self):
 
339
        return str(self.path)
 
340
 
 
341
    def getmod(self, nm):
 
342
        mod = None
 
343
        for thing in self.path:
 
344
            if isinstance(thing, STRINGTYPE):
 
345
                owner = self.shadowpath.get(thing, -1)
 
346
                if owner == -1:
 
347
                    owner = self.shadowpath[thing] = self.makeOwner(thing)
 
348
                if owner:
 
349
                    mod = owner.getmod(nm)
 
350
            else:
 
351
                mod = thing.getmod(nm)
 
352
            if mod:
 
353
                break
 
354
        return mod
 
355
 
 
356
    def makeOwner(self, path):
 
357
        if self.building.get(path):
 
358
            return None
 
359
        self.building[path] = 1
 
360
        owner = None
 
361
        for klass in self.ownertypes:
 
362
            try:
 
363
                # this may cause an import, which may cause recursion
 
364
                # hence the protection
 
365
                owner = klass(path, self.target_platform)
 
366
            except OwnerError:
 
367
                pass
 
368
            except Exception, e:
 
369
                #print "FIXME: Wrong exception", e
 
370
                pass
 
371
            else:
 
372
                break
 
373
        del self.building[path]
 
374
        return owner
 
375
 
 
376
 
 
377
def getDescr(fnm):
 
378
    ext = os.path.splitext(fnm)[1]
 
379
    for (suffix, mode, typ) in imp.get_suffixes():
 
380
        if suffix == ext:
 
381
            return (suffix, mode, typ)
 
382
 
 
383
#=================Import Tracker============================#
 
384
# This one doesn't really import, just analyzes
 
385
# If it *were* importing, it would be the one-and-only ImportManager
 
386
# ie, the builtin import
 
387
 
 
388
UNTRIED = -1
 
389
 
 
390
imptyps = ['top-level', 'conditional', 'delayed', 'delayed, conditional']
 
391
import hooks
 
392
 
 
393
if __debug__:
 
394
    import sys
 
395
    import UserDict
 
396
    class LogDict(UserDict.UserDict):
 
397
        count = 0
 
398
        def __init__(self, *args):
 
399
            UserDict.UserDict.__init__(self, *args)
 
400
            LogDict.count += 1
 
401
            self.logfile = open("logdict%s-%d.log" % (".".join(map(str, sys.version_info)),
 
402
                                                      LogDict.count), "w")
 
403
        def __setitem__(self, key, value):
 
404
            self.logfile.write("%s: %s -> %s\n" % (key, self.data.get(key), value))
 
405
            UserDict.UserDict.__setitem__(self, key, value)
 
406
        def __delitem__(self, key):
 
407
            self.logfile.write("  DEL %s\n" % key)
 
408
            UserDict.UserDict.__delitem__(self, key)
 
409
else:
 
410
    LogDict = dict
 
411
 
 
412
 
 
413
class ImportTracker:
 
414
    # really the equivalent of builtin import
 
415
    def __init__(self, xpath=None, hookspath=None, excludes=None,
 
416
                 target_platform=None):
 
417
        self.path = []
 
418
        self.warnings = {}
 
419
        if xpath:
 
420
            self.path = xpath
 
421
        self.path.extend(sys.path)
 
422
        self.modules = LogDict()
 
423
        self.metapath = [
 
424
            BuiltinImportDirector(),
 
425
            FrozenImportDirector(),
 
426
            RegistryImportDirector(),
 
427
            PathImportDirector(self.path, target_platform=target_platform)
 
428
        ]
 
429
        if hookspath:
 
430
            hooks.__path__.extend(hookspath)
 
431
        self.excludes = excludes
 
432
        if excludes is None:
 
433
            self.excludes = []
 
434
        self.target_platform = target_platform
 
435
 
 
436
    def analyze_r(self, nm, importernm=None):
 
437
        importer = importernm
 
438
        if importer is None:
 
439
            importer = '__main__'
 
440
        seen = {}
 
441
        nms = self.analyze_one(nm, importernm)
 
442
        nms = map(None, nms, [importer]*len(nms))
 
443
        i = 0
 
444
        while i < len(nms):
 
445
            nm, importer = nms[i]
 
446
            if seen.get(nm,0):
 
447
                del nms[i]
 
448
                mod = self.modules[nm]
 
449
                if mod:
 
450
                    mod.xref(importer)
 
451
            else:
 
452
                i = i + 1
 
453
                seen[nm] = 1
 
454
                j = i
 
455
                mod = self.modules[nm]
 
456
                if mod:
 
457
                    mod.xref(importer)
 
458
                    for name, isdelayed, isconditional, level in mod.imports:
 
459
                        imptyp = isdelayed * 2 + isconditional
 
460
                        newnms = self.analyze_one(name, nm, imptyp, level)
 
461
                        newnms = map(None, newnms, [nm]*len(newnms))
 
462
                        nms[j:j] = newnms
 
463
                        j = j + len(newnms)
 
464
        return map(lambda a: a[0], nms)
 
465
 
 
466
    def analyze_one(self, nm, importernm=None, imptyp=0, level=-1):
 
467
        #print '## analyze_one', nm, importernm, imptyp, level
 
468
        # break the name being imported up so we get:
 
469
        # a.b.c -> [a, b, c] ; ..z -> ['', '', z]
 
470
        if not nm:
 
471
            nm = importernm
 
472
            importernm = None
 
473
            level = 0
 
474
        nmparts = string.split(nm, '.')
 
475
 
 
476
        if level < 0:
 
477
            # behaviour up to Python 2.4 (and default in Python 2.5)
 
478
            # first see if we could be importing a relative name
 
479
            contexts = [None]
 
480
            if importernm:
 
481
                if self.ispackage(importernm):
 
482
                    contexts.insert(0, importernm)
 
483
                else:
 
484
                    pkgnm = string.join(string.split(importernm, '.')[:-1], '.')
 
485
                    if pkgnm:
 
486
                        contexts.insert(0, pkgnm)
 
487
        elif level == 0:
 
488
            # absolute import, do not try relative
 
489
            importernm = None
 
490
            contexts = [None]
 
491
        elif level > 0:
 
492
            # relative import, do not try absolute
 
493
            if self.ispackage(importernm):
 
494
                level -= 1
 
495
            if level > 0:
 
496
                importernm = string.join(string.split(importernm, '.')[:-level], ".")
 
497
            contexts = [importernm, None]
 
498
            importernm = None
 
499
 
 
500
        _all = None
 
501
 
 
502
        assert contexts
 
503
 
 
504
        # so contexts is [pkgnm, None] or just [None]
 
505
        if nmparts[-1] == '*':
 
506
            del nmparts[-1]
 
507
            _all = []
 
508
        nms = []
 
509
        for context in contexts:
 
510
            ctx = context
 
511
            for i in range(len(nmparts)):
 
512
                nm = nmparts[i]
 
513
                if ctx:
 
514
                    fqname = ctx + '.' + nm
 
515
                else:
 
516
                    fqname = nm
 
517
                mod = self.modules.get(fqname, UNTRIED)
 
518
                if mod is UNTRIED:
 
519
                    mod = self.doimport(nm, ctx, fqname)
 
520
                if mod:
 
521
                    nms.append(mod.__name__)
 
522
                    ctx = fqname
 
523
                else:
 
524
                    break
 
525
            else:
 
526
                # no break, point i beyond end
 
527
                i = i + 1
 
528
            if i:
 
529
                break
 
530
        # now nms is the list of modules that went into sys.modules
 
531
        # just as result of the structure of the name being imported
 
532
        # however, each mod has been scanned and that list is in mod.imports
 
533
        if i<len(nmparts):
 
534
            if ctx:
 
535
                if hasattr(self.modules[ctx], nmparts[i]):
 
536
                    return nms
 
537
                if not self.ispackage(ctx):
 
538
                    return nms
 
539
            self.warnings["W: no module named %s (%s import by %s)" % (fqname, imptyps[imptyp], importernm or "__main__")] = 1
 
540
            if self.modules.has_key(fqname):
 
541
                del self.modules[fqname]
 
542
            return nms
 
543
        if _all is None:
 
544
            return nms
 
545
        bottommod = self.modules[ctx]
 
546
        if bottommod.ispackage():
 
547
            for nm in bottommod._all:
 
548
                if not hasattr(bottommod, nm):
 
549
                    mod = self.doimport(nm, ctx, ctx+'.'+nm)
 
550
                    if mod:
 
551
                        nms.append(mod.__name__)
 
552
                    else:
 
553
                        bottommod.warnings.append("W: name %s not found" % nm)
 
554
        return nms
 
555
 
 
556
    def analyze_script(self, fnm):
 
557
        try:
 
558
            stuff = open(fnm, 'r').read()+'\n'
 
559
            co = compile(string.replace(stuff, "\r\n", "\n"), fnm, 'exec')
 
560
        except SyntaxError, e:
 
561
            print "Invalid syntax in %s" % fnm
 
562
            print e.args
 
563
            raise
 
564
        mod = PyScript(fnm, co)
 
565
        self.modules['__main__'] = mod
 
566
        return self.analyze_r('__main__')
 
567
 
 
568
 
 
569
    def ispackage(self, nm):
 
570
        return self.modules[nm].ispackage()
 
571
 
 
572
    def doimport(self, nm, ctx, fqname):
 
573
        #print "doimport", nm, ctx, fqname
 
574
        # Not that nm is NEVER a dotted name at this point
 
575
        assert ("." not in nm), nm
 
576
        if fqname in self.excludes:
 
577
            return None
 
578
        if ctx:
 
579
            parent = self.modules[ctx]
 
580
            if parent.ispackage():
 
581
                mod = parent.doimport(nm)
 
582
                if mod:
 
583
                    # insert the new module in the parent package
 
584
                    # FIXME why?
 
585
                    setattr(parent, nm, mod)
 
586
            else:
 
587
                # if parent is not a package, there is nothing more to do
 
588
                return None
 
589
        else:
 
590
            # now we're dealing with an absolute import
 
591
            # try to import nm using available directors
 
592
            for director in self.metapath:
 
593
                mod = director.getmod(nm)
 
594
                if mod:
 
595
                    break
 
596
        # here we have `mod` from:
 
597
        #   mod = parent.doimport(nm)
 
598
        # or
 
599
        #   mod = director.getmod(nm)
 
600
        if mod:
 
601
            mod.__name__ = fqname
 
602
            self.modules[fqname] = mod
 
603
            # now look for hooks
 
604
            # this (and scan_code) are instead of doing "exec co in mod.__dict__"
 
605
            try:
 
606
                hookmodnm = 'hook-'+fqname
 
607
                hooks = __import__('hooks', globals(), locals(), [hookmodnm])
 
608
                hook = getattr(hooks, hookmodnm)
 
609
            except AttributeError:
 
610
                pass
 
611
            else:
 
612
                # rearranged so that hook() has a chance to mess with hiddenimports & attrs
 
613
                if hasattr(hook, 'hook'):
 
614
                    mod = hook.hook(mod)
 
615
                if hasattr(hook, 'hiddenimports'):
 
616
                    for impnm in hook.hiddenimports:
 
617
                        mod.imports.append((impnm, 0, 0, -1))
 
618
                if hasattr(hook, 'attrs'):
 
619
                    for attr, val in hook.attrs:
 
620
                        setattr(mod, attr, val)
 
621
                if hasattr(hook, 'datas'):
 
622
                    # hook.datas is a list of globs of files or directories to bundle
 
623
                    # as datafiles. For each glob, a destination directory is specified.
 
624
                    for g,dest_dir in hook.datas:
 
625
                        if dest_dir: dest_dir += "/"
 
626
                        for fn in glob.glob(g):
 
627
                            if os.path.isfile(fn):
 
628
                                mod.datas.append((dest_dir + os.path.basename(fn), fn, 'DATA'))
 
629
                            else:
 
630
                                def visit((base,dest_dir,datas), dirname, names):
 
631
                                    for fn in names:
 
632
                                        fn = os.path.join(dirname, fn)
 
633
                                        if os.path.isfile(fn):
 
634
                                            datas.append((dest_dir + fn[len(base)+1:], fn, 'DATA'))
 
635
                                os.path.walk(fn, visit, (os.path.dirname(fn),dest_dir,mod.datas))
 
636
                if fqname != mod.__name__:
 
637
                    print "W: %s is changing it's name to %s" % (fqname, mod.__name__)
 
638
                    self.modules[mod.__name__] = mod
 
639
        else:
 
640
            assert (mod == None), mod
 
641
            self.modules[fqname] = None
 
642
        # should be equivalent using only one
 
643
        # self.modules[fqname] = mod
 
644
        # here
 
645
        return mod
 
646
 
 
647
    def getwarnings(self):
 
648
        warnings = self.warnings.keys()
 
649
        for nm,mod in self.modules.items():
 
650
            if mod:
 
651
                for w in mod.warnings:
 
652
                    warnings.append(w+' - %s (%s)' % (mod.__name__, mod.__file__))
 
653
        return warnings
 
654
 
 
655
    def getxref(self):
 
656
        mods = self.modules.items() # (nm, mod)
 
657
        mods.sort()
 
658
        rslt = []
 
659
        for nm, mod in mods:
 
660
            if mod:
 
661
                importers = mod._xref.keys()
 
662
                importers.sort()
 
663
                rslt.append((nm, importers))
 
664
        return rslt
 
665
 
 
666
#====================Modules============================#
 
667
# All we're doing here is tracking, not importing
 
668
# If we were importing, these would be hooked to the real module objects
 
669
 
 
670
class Module:
 
671
    _ispkg = 0
 
672
    typ = 'UNKNOWN'
 
673
 
 
674
    def __init__(self, nm):
 
675
        self.__name__ = nm
 
676
        self.__file__ = None
 
677
        self._all = []
 
678
        self.imports = []
 
679
        self.warnings = []
 
680
        self.binaries = []
 
681
        self.datas = []
 
682
        self._xref = {}
 
683
 
 
684
    def ispackage(self):
 
685
        return self._ispkg
 
686
 
 
687
    def doimport(self, nm):
 
688
        pass
 
689
 
 
690
    def xref(self, nm):
 
691
        self._xref[nm] = 1
 
692
 
 
693
    def __str__(self):
 
694
        return "<Module %s %s imports=%s binaries=%s datas=%s>" % \
 
695
            (self.__name__, self.__file__, self.imports, self.binaries, self.datas)
 
696
 
 
697
class BuiltinModule(Module):
 
698
    typ = 'BUILTIN'
 
699
 
 
700
    def __init__(self, nm):
 
701
        Module.__init__(self, nm)
 
702
 
 
703
class ExtensionModule(Module):
 
704
    typ = 'EXTENSION'
 
705
 
 
706
    def __init__(self, nm, pth):
 
707
        Module.__init__(self, nm)
 
708
        self.__file__ = pth
 
709
 
 
710
class PyModule(Module):
 
711
    typ = 'PYMODULE'
 
712
 
 
713
    def __init__(self, nm, pth, co):
 
714
        Module.__init__(self, nm)
 
715
        self.co = co
 
716
        self.__file__ = pth
 
717
        if os.path.splitext(self.__file__)[1] == '.py':
 
718
            self.__file__ = self.__file__ + pyco()
 
719
        self.scancode()
 
720
 
 
721
    def scancode(self):
 
722
        self.imports, self.warnings, self.binaries, allnms = scan_code(self.co)
 
723
        if allnms:
 
724
            self._all = allnms
 
725
        if ctypes and self.binaries:
 
726
            self.binaries = _resolveCtypesImports(self.binaries)
 
727
 
 
728
 
 
729
class PyScript(PyModule):
 
730
    typ = 'PYSOURCE'
 
731
 
 
732
    def __init__(self, pth, co):
 
733
        Module.__init__(self, '__main__')
 
734
        self.co = co
 
735
        self.__file__ = pth
 
736
        self.scancode()
 
737
 
 
738
 
 
739
class PkgModule(PyModule):
 
740
    typ = 'PYMODULE'
 
741
 
 
742
    def __init__(self, nm, pth, co):
 
743
        PyModule.__init__(self, nm, pth, co)
 
744
        self._ispkg = 1
 
745
        pth = os.path.dirname(pth)
 
746
        self.__path__ = [ pth ]
 
747
        self._update_director(force=True)
 
748
 
 
749
    def _update_director(self, force=False):
 
750
        if force or self.subimporter.path != self.__path__:
 
751
            self.subimporter = PathImportDirector(self.__path__)
 
752
 
 
753
    def doimport(self, nm):
 
754
        self._update_director()
 
755
        mod = self.subimporter.getmod(nm)
 
756
        if mod:
 
757
            mod.__name__ = self.__name__ + '.' + mod.__name__
 
758
        return mod
 
759
 
 
760
class PkgInPYZModule(PyModule):
 
761
    def __init__(self, nm, co, pyzowner):
 
762
        PyModule.__init__(self, nm, co.co_filename, co)
 
763
        self._ispkg = 1
 
764
        self.__path__ = [ str(pyzowner) ]
 
765
        self.owner = pyzowner
 
766
 
 
767
    def doimport(self, nm):
 
768
        mod = self.owner.getmod(self.__name__ + '.' + nm)
 
769
        return mod
 
770
 
 
771
class PyInZipModule(PyModule):
 
772
    typ = 'ZIPFILE'
 
773
    def __init__(self, zipowner, nm, pth, co):
 
774
        PyModule.__init__(self, nm, co.co_filename, co)
 
775
        self.owner = zipowner
 
776
 
 
777
class PkgInZipModule(PyModule):
 
778
    typ = 'ZIPFILE'
 
779
    def __init__(self, zipowner, nm, pth, co):
 
780
        PyModule.__init__(self, nm, co.co_filename, co)
 
781
        self._ispkg = 1
 
782
        self.__path__ = [ str(zipowner) ]
 
783
        self.owner = zipowner
 
784
 
 
785
    def doimport(self, nm):
 
786
        mod = self.owner.getmod(self.__name__ + '.' + nm)
 
787
        return mod
 
788
 
 
789
 
 
790
#======================== Utility ================================#
 
791
# Scan the code object for imports, __all__ and wierd stuff
 
792
 
 
793
import dis
 
794
IMPORT_NAME = dis.opname.index('IMPORT_NAME')
 
795
IMPORT_FROM = dis.opname.index('IMPORT_FROM')
 
796
try:
 
797
    IMPORT_STAR = dis.opname.index('IMPORT_STAR')
 
798
except:
 
799
    IMPORT_STAR = 999
 
800
STORE_NAME = dis.opname.index('STORE_NAME')
 
801
STORE_FAST = dis.opname.index('STORE_FAST')
 
802
STORE_GLOBAL = dis.opname.index('STORE_GLOBAL')
 
803
LOAD_GLOBAL = dis.opname.index('LOAD_GLOBAL')
 
804
LOAD_ATTR = dis.opname.index('LOAD_ATTR')
 
805
LOAD_NAME = dis.opname.index('LOAD_NAME')
 
806
EXEC_STMT = dis.opname.index('EXEC_STMT')
 
807
try:
 
808
    SET_LINENO = dis.opname.index('SET_LINENO')
 
809
except ValueError:
 
810
    SET_LINENO = 999
 
811
BUILD_LIST = dis.opname.index('BUILD_LIST')
 
812
LOAD_CONST = dis.opname.index('LOAD_CONST')
 
813
if getattr(sys, 'version_info', (0,0,0)) > (2,5,0):
 
814
    LOAD_CONST_level = LOAD_CONST
 
815
else:
 
816
    LOAD_CONST_level = 999
 
817
JUMP_IF_FALSE = dis.opname.index('JUMP_IF_FALSE')
 
818
JUMP_IF_TRUE = dis.opname.index('JUMP_IF_TRUE')
 
819
JUMP_FORWARD = dis.opname.index('JUMP_FORWARD')
 
820
try:
 
821
    STORE_DEREF = dis.opname.index('STORE_DEREF')
 
822
except ValueError:
 
823
    STORE_DEREF = 999
 
824
COND_OPS = [JUMP_IF_TRUE, JUMP_IF_FALSE]
 
825
STORE_OPS = [STORE_NAME, STORE_FAST, STORE_GLOBAL, STORE_DEREF]
 
826
#IMPORT_STAR -> IMPORT_NAME mod ; IMPORT_STAR
 
827
#JUMP_IF_FALSE / JUMP_IF_TRUE / JUMP_FORWARD
 
828
 
 
829
def pass1(code):
 
830
    instrs = []
 
831
    i = 0
 
832
    n = len(code)
 
833
    curline = 0
 
834
    incondition = 0
 
835
    out = 0
 
836
    while i < n:
 
837
        if i >= out:
 
838
            incondition = 0
 
839
        c = code[i]
 
840
        i = i+1
 
841
        op = ord(c)
 
842
        if op >= dis.HAVE_ARGUMENT:
 
843
            oparg = ord(code[i]) + ord(code[i+1])*256
 
844
            i = i+2
 
845
        else:
 
846
            oparg = None
 
847
        if not incondition and op in COND_OPS:
 
848
            incondition = 1
 
849
            out = i + oparg
 
850
        elif incondition and op == JUMP_FORWARD:
 
851
            out = max(out, i + oparg)
 
852
        if op == SET_LINENO:
 
853
            curline = oparg
 
854
        else:
 
855
            instrs.append((op, oparg, incondition, curline))
 
856
    return instrs
 
857
 
 
858
def scan_code(co, m=None, w=None, b=None, nested=0):
 
859
    instrs = pass1(co.co_code)
 
860
    if m is None:
 
861
        m = []
 
862
    if w is None:
 
863
        w = []
 
864
    if b is None:
 
865
        b = []
 
866
    all = None
 
867
    lastname = None
 
868
    level = -1 # import-level, same behaviour as up to Python 2.4
 
869
    for i in range(len(instrs)):
 
870
        op, oparg, conditional, curline = instrs[i]
 
871
        if op == IMPORT_NAME:
 
872
            if level <= 0:
 
873
                name = lastname = co.co_names[oparg]
 
874
            else:
 
875
                name = lastname = co.co_names[oparg]
 
876
            #print 'import_name', name, `lastname`, level
 
877
            m.append((name, nested, conditional, level))
 
878
        elif op == IMPORT_FROM:
 
879
            name = co.co_names[oparg]
 
880
            #print 'import_from', name, `lastname`, level,
 
881
            if level > 0 and (not lastname or lastname[-1:] == '.'):
 
882
                name = lastname + name
 
883
            else:
 
884
                name = lastname + '.' + name
 
885
            #print name
 
886
            m.append((name, nested, conditional, level))
 
887
            assert lastname is not None
 
888
        elif op == IMPORT_STAR:
 
889
            m.append((lastname+'.*', nested, conditional, level))
 
890
        elif op == STORE_NAME:
 
891
            if co.co_names[oparg] == "__all__":
 
892
                j = i - 1
 
893
                pop, poparg, pcondtl, pline = instrs[j]
 
894
                if pop != BUILD_LIST:
 
895
                    w.append("W: __all__ is built strangely at line %s" % pline)
 
896
                else:
 
897
                    all = []
 
898
                    while j > 0:
 
899
                        j = j - 1
 
900
                        pop, poparg, pcondtl, pline = instrs[j]
 
901
                        if pop == LOAD_CONST:
 
902
                            all.append(co.co_consts[poparg])
 
903
                        else:
 
904
                            break
 
905
        elif op in STORE_OPS:
 
906
            pass
 
907
        elif op == LOAD_CONST_level:
 
908
            # starting with Python 2.5, _each_ import is preceeded with a
 
909
            # LOAD_CONST to indicate the relative level.
 
910
            if isinstance(co.co_consts[oparg], (int, long)):
 
911
                level = co.co_consts[oparg]
 
912
        elif op == LOAD_GLOBAL:
 
913
            name = co.co_names[oparg]
 
914
            cndtl = ['', 'conditional'][conditional]
 
915
            lvl = ['top-level', 'delayed'][nested]
 
916
            if name == "__import__":
 
917
                w.append("W: %s %s __import__ hack detected at line %s"  % (lvl, cndtl, curline))
 
918
            elif name == "eval":
 
919
                w.append("W: %s %s eval hack detected at line %s"  % (lvl, cndtl, curline))
 
920
        elif op == EXEC_STMT:
 
921
            cndtl = ['', 'conditional'][conditional]
 
922
            lvl = ['top-level', 'delayed'][nested]
 
923
            w.append("W: %s %s exec statement detected at line %s"  % (lvl, cndtl, curline))
 
924
        else:
 
925
            lastname = None
 
926
 
 
927
        if ctypes:
 
928
            # ctypes scanning requires a scope wider than one bytecode instruction,
 
929
            # so the code resides in a separate function for clarity.
 
930
            ctypesb, ctypesw = scan_code_for_ctypes(co, instrs, i)
 
931
            b.extend(ctypesb)
 
932
            w.extend(ctypesw)
 
933
 
 
934
    for c in co.co_consts:
 
935
        if isinstance(c, type(co)):
 
936
            # FIXME: "all" was not updated here nor returned. Was it the desired
 
937
            # behaviour?
 
938
            _, _, _, all_nested = scan_code(c, m, w, b, 1)
 
939
            if all_nested:
 
940
                all.extend(all_nested)
 
941
    return m, w, b, all
 
942
 
 
943
def scan_code_for_ctypes(co, instrs, i):
 
944
    """Detects ctypes dependencies, using reasonable heuristics that should
 
945
    cover most common ctypes usages; returns a tuple of two lists, one
 
946
    containing names of binaries detected as dependencies, the other containing
 
947
    warnings.
 
948
    """
 
949
 
 
950
    def _libFromConst(i):
 
951
        """Extracts library name from an expected LOAD_CONST instruction and
 
952
        appends it to local binaries list.
 
953
        """
 
954
        op, oparg, conditional, curline = instrs[i]
 
955
        if op == LOAD_CONST:
 
956
            soname = co.co_consts[oparg]
 
957
            b.append(soname)
 
958
 
 
959
    b = []
 
960
 
 
961
    op, oparg, conditional, curline = instrs[i]
 
962
 
 
963
    if op in (LOAD_GLOBAL, LOAD_NAME):
 
964
        name = co.co_names[oparg]
 
965
 
 
966
        if name in ("CDLL", "WinDLL"):
 
967
            # Guesses ctypes imports of this type: CDLL("library.so")
 
968
 
 
969
            # LOAD_GLOBAL 0 (CDLL) <--- we "are" here right now
 
970
            # LOAD_CONST 1 ('library.so')
 
971
 
 
972
            _libFromConst(i+1)
 
973
 
 
974
        elif name == "ctypes":
 
975
            # Guesses ctypes imports of this type: ctypes.DLL("library.so")
 
976
 
 
977
            # LOAD_GLOBAL 0 (ctypes) <--- we "are" here right now
 
978
            # LOAD_ATTR 1 (CDLL)
 
979
            # LOAD_CONST 1 ('library.so')
 
980
 
 
981
            op2, oparg2, conditional2, curline2 = instrs[i+1]
 
982
            if op2 == LOAD_ATTR:
 
983
                if co.co_names[oparg2] in ("CDLL", "WinDLL"):
 
984
                    # Fetch next, and finally get the library name
 
985
                    _libFromConst(i+2)
 
986
 
 
987
        elif name == ("cdll", "windll"):
 
988
            # Guesses ctypes imports of these types:
 
989
 
 
990
            #  * cdll.library (only valid on Windows)
 
991
 
 
992
            #     LOAD_GLOBAL 0 (cdll) <--- we "are" here right now
 
993
            #     LOAD_ATTR 1 (library)
 
994
 
 
995
            #  * cdll.LoadLibrary("library.so")
 
996
 
 
997
            #     LOAD_GLOBAL              0 (cdll) <--- we "are" here right now
 
998
            #     LOAD_ATTR                1 (LoadLibrary)
 
999
            #     LOAD_CONST               1 ('library.so')
 
1000
 
 
1001
            op2, oparg2, conditional2, curline2 = instrs[i+1]
 
1002
            if op2 == LOAD_ATTR:
 
1003
                if co.co_names[oparg2] != "LoadLibrary":
 
1004
                    # First type
 
1005
                    soname = co.co_names[oparg2] + ".dll"
 
1006
                    b.append(soname)
 
1007
                else:
 
1008
                    # Second type, needs to fetch one more instruction
 
1009
                    _libFromConst(i+2)
 
1010
 
 
1011
    # If any of the libraries has been requested with anything different from
 
1012
    # the bare filename, drop that entry and warn the user - pyinstaller would
 
1013
    # need to patch the compiled pyc file to make it work correctly!
 
1014
 
 
1015
    w = []
 
1016
    for bin in list(b):
 
1017
        if bin != os.path.basename(bin):
 
1018
            b.remove(bin)
 
1019
            w.append("W: ignoring %s - ctypes imports only supported using bare filenames" % (bin,))
 
1020
 
 
1021
    return b, w
 
1022
 
 
1023
 
 
1024
def _resolveCtypesImports(cbinaries):
 
1025
    """Completes ctypes BINARY entries for modules with their full path.
 
1026
    """
 
1027
    if sys.platform.startswith("linux"):
 
1028
        envvar = "LD_LIBRARY_PATH"
 
1029
    elif sys.platform.startswith("darwin"):
 
1030
        envvar = "DYLD_LIBRARY_PATH"
 
1031
    else:
 
1032
        envvar = "PATH"
 
1033
 
 
1034
    def _savePaths():
 
1035
        old = os.environ.get(envvar, None)
 
1036
        os.environ[envvar] = os.pathsep.join(sys.pathex)
 
1037
        if old is not None:
 
1038
            os.environ[envvar] = os.pathsep.join([os.environ[envvar], old])
 
1039
        return old
 
1040
 
 
1041
    def _restorePaths(old):
 
1042
        del os.environ[envvar]
 
1043
        if old is not None:
 
1044
            os.environ[envvar] = old
 
1045
 
 
1046
    ret = []
 
1047
 
 
1048
    # Try to locate the shared library on disk. This is done by
 
1049
    # executing ctypes.utile.find_library prepending ImportTracker's
 
1050
    # local paths to library search paths, then replaces original values.
 
1051
    old = _savePaths()
 
1052
    for cbin in cbinaries:
 
1053
        cpath = find_library(os.path.splitext(cbin)[0])
 
1054
        if sys.platform == "linux2":
 
1055
            # CAVEAT: find_library() is not the correct function. Ctype's
 
1056
            # documentation says that it is meant to resolve only the filename
 
1057
            # (as a *compiler* does) not the full path. Anyway, it works well
 
1058
            # enough on Windows and Mac. On Linux, we need to implement
 
1059
            # more code to find out the full path.
 
1060
            if cpath is None:
 
1061
                cpath = cbin
 
1062
            # "man ld.so" says that we should first search LD_LIBRARY_PATH
 
1063
            # and then the ldcache
 
1064
            for d in os.environ["LD_LIBRARY_PATH"].split(":"):
 
1065
                if os.path.isfile(d + "/" + cpath):
 
1066
                    cpath = d + "/" + cpath
 
1067
                    break
 
1068
            else:
 
1069
                for L in os.popen("ldconfig -p").read().splitlines():
 
1070
                    if cpath in L:
 
1071
                        cpath = L.split("=>", 1)[1].strip()
 
1072
                        assert os.path.isfile(cpath)
 
1073
                        break
 
1074
                else:
 
1075
                    cpath = None
 
1076
        if cpath is None:
 
1077
            print "W: library %s required via ctypes not found" % (cbin,)
 
1078
        else:
 
1079
            ret.append((cbin, cpath, "BINARY"))
 
1080
    _restorePaths(old)
 
1081
    return ret
 
1082
 
 
1083