root / env / lib / python2.7 / site-packages / distribute-0.6.19-py2.7.egg / setuptools / command / bdist_egg.py @ 1a305335
History | View | Annotate | Download (17.9 KB)
1 |
"""setuptools.command.bdist_egg
|
---|---|
2 |
|
3 |
Build .egg distributions"""
|
4 |
|
5 |
# This module should be kept compatible with Python 2.3
|
6 |
import sys, os, marshal |
7 |
from setuptools import Command |
8 |
from distutils.dir_util import remove_tree, mkpath |
9 |
try:
|
10 |
from distutils.sysconfig import get_python_version, get_python_lib |
11 |
except ImportError: |
12 |
from sysconfig import get_python_version |
13 |
from distutils.sysconfig import get_python_lib |
14 |
|
15 |
from distutils import log |
16 |
from distutils.errors import DistutilsSetupError |
17 |
from pkg_resources import get_build_platform, Distribution, ensure_directory |
18 |
from pkg_resources import EntryPoint |
19 |
from types import CodeType |
20 |
from setuptools.extension import Library |
21 |
|
22 |
def strip_module(filename): |
23 |
if '.' in filename: |
24 |
filename = os.path.splitext(filename)[0]
|
25 |
if filename.endswith('module'): |
26 |
filename = filename[:-6]
|
27 |
return filename
|
28 |
|
29 |
def write_stub(resource, pyfile): |
30 |
f = open(pyfile,'w') |
31 |
f.write('\n'.join([
|
32 |
"def __bootstrap__():",
|
33 |
" global __bootstrap__, __loader__, __file__",
|
34 |
" import sys, pkg_resources, imp",
|
35 |
" __file__ = pkg_resources.resource_filename(__name__,%r)"
|
36 |
% resource, |
37 |
" __loader__ = None; del __bootstrap__, __loader__",
|
38 |
" imp.load_dynamic(__name__,__file__)",
|
39 |
"__bootstrap__()",
|
40 |
"" # terminal \n |
41 |
])) |
42 |
f.close() |
43 |
|
44 |
# stub __init__.py for packages distributed without one
|
45 |
NS_PKG_STUB = '__import__("pkg_resources").declare_namespace(__name__)'
|
46 |
|
47 |
class bdist_egg(Command): |
48 |
|
49 |
description = "create an \"egg\" distribution"
|
50 |
|
51 |
user_options = [ |
52 |
('bdist-dir=', 'b', |
53 |
"temporary directory for creating the distribution"),
|
54 |
('plat-name=', 'p', |
55 |
"platform name to embed in generated filenames "
|
56 |
"(default: %s)" % get_build_platform()),
|
57 |
('exclude-source-files', None, |
58 |
"remove all .py files from the generated egg"),
|
59 |
('keep-temp', 'k', |
60 |
"keep the pseudo-installation tree around after " +
|
61 |
"creating the distribution archive"),
|
62 |
('dist-dir=', 'd', |
63 |
"directory to put final built distributions in"),
|
64 |
('skip-build', None, |
65 |
"skip rebuilding everything (for testing/debugging)"),
|
66 |
] |
67 |
|
68 |
boolean_options = [ |
69 |
'keep-temp', 'skip-build', 'exclude-source-files' |
70 |
] |
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
def initialize_options (self): |
89 |
self.bdist_dir = None |
90 |
self.plat_name = None |
91 |
self.keep_temp = 0 |
92 |
self.dist_dir = None |
93 |
self.skip_build = 0 |
94 |
self.egg_output = None |
95 |
self.exclude_source_files = None |
96 |
|
97 |
|
98 |
def finalize_options(self): |
99 |
ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info") |
100 |
self.egg_info = ei_cmd.egg_info
|
101 |
|
102 |
if self.bdist_dir is None: |
103 |
bdist_base = self.get_finalized_command('bdist').bdist_base |
104 |
self.bdist_dir = os.path.join(bdist_base, 'egg') |
105 |
|
106 |
if self.plat_name is None: |
107 |
self.plat_name = get_build_platform()
|
108 |
|
109 |
self.set_undefined_options('bdist',('dist_dir', 'dist_dir')) |
110 |
|
111 |
if self.egg_output is None: |
112 |
|
113 |
# Compute filename of the output egg
|
114 |
basename = Distribution( |
115 |
None, None, ei_cmd.egg_name, ei_cmd.egg_version, |
116 |
get_python_version(), |
117 |
self.distribution.has_ext_modules() and self.plat_name |
118 |
).egg_name() |
119 |
|
120 |
self.egg_output = os.path.join(self.dist_dir, basename+'.egg') |
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
def do_install_data(self): |
130 |
# Hack for packages that install data to install's --install-lib
|
131 |
self.get_finalized_command('install').install_lib = self.bdist_dir |
132 |
|
133 |
site_packages = os.path.normcase(os.path.realpath(get_python_lib())) |
134 |
old, self.distribution.data_files = self.distribution.data_files,[] |
135 |
|
136 |
for item in old: |
137 |
if isinstance(item,tuple) and len(item)==2: |
138 |
if os.path.isabs(item[0]): |
139 |
realpath = os.path.realpath(item[0])
|
140 |
normalized = os.path.normcase(realpath) |
141 |
if normalized==site_packages or normalized.startswith( |
142 |
site_packages+os.sep |
143 |
): |
144 |
item = realpath[len(site_packages)+1:], item[1] |
145 |
# XXX else: raise ???
|
146 |
self.distribution.data_files.append(item)
|
147 |
|
148 |
try:
|
149 |
log.info("installing package data to %s" % self.bdist_dir) |
150 |
self.call_command('install_data', force=0, root=None) |
151 |
finally:
|
152 |
self.distribution.data_files = old
|
153 |
|
154 |
|
155 |
def get_outputs(self): |
156 |
return [self.egg_output] |
157 |
|
158 |
|
159 |
def call_command(self,cmdname,**kw): |
160 |
"""Invoke reinitialized command `cmdname` with keyword args"""
|
161 |
for dirname in INSTALL_DIRECTORY_ATTRS: |
162 |
kw.setdefault(dirname,self.bdist_dir)
|
163 |
kw.setdefault('skip_build',self.skip_build) |
164 |
kw.setdefault('dry_run', self.dry_run) |
165 |
cmd = self.reinitialize_command(cmdname, **kw)
|
166 |
self.run_command(cmdname)
|
167 |
return cmd
|
168 |
|
169 |
|
170 |
def run(self): |
171 |
# Generate metadata first
|
172 |
self.run_command("egg_info") |
173 |
|
174 |
# We run install_lib before install_data, because some data hacks
|
175 |
# pull their data path from the install_lib command.
|
176 |
log.info("installing library code to %s" % self.bdist_dir) |
177 |
instcmd = self.get_finalized_command('install') |
178 |
old_root = instcmd.root; instcmd.root = None
|
179 |
cmd = self.call_command('install_lib', warn_dir=0) |
180 |
instcmd.root = old_root |
181 |
|
182 |
all_outputs, ext_outputs = self.get_ext_outputs()
|
183 |
self.stubs = []
|
184 |
to_compile = [] |
185 |
for (p,ext_name) in enumerate(ext_outputs): |
186 |
filename,ext = os.path.splitext(ext_name) |
187 |
pyfile = os.path.join(self.bdist_dir, strip_module(filename)+'.py') |
188 |
self.stubs.append(pyfile)
|
189 |
log.info("creating stub loader for %s" % ext_name)
|
190 |
if not self.dry_run: |
191 |
write_stub(os.path.basename(ext_name), pyfile) |
192 |
to_compile.append(pyfile) |
193 |
ext_outputs[p] = ext_name.replace(os.sep,'/')
|
194 |
|
195 |
to_compile.extend(self.make_init_files())
|
196 |
if to_compile:
|
197 |
cmd.byte_compile(to_compile) |
198 |
|
199 |
if self.distribution.data_files: |
200 |
self.do_install_data()
|
201 |
|
202 |
# Make the EGG-INFO directory
|
203 |
archive_root = self.bdist_dir
|
204 |
egg_info = os.path.join(archive_root,'EGG-INFO')
|
205 |
self.mkpath(egg_info)
|
206 |
if self.distribution.scripts: |
207 |
script_dir = os.path.join(egg_info, 'scripts')
|
208 |
log.info("installing scripts to %s" % script_dir)
|
209 |
self.call_command('install_scripts',install_dir=script_dir,no_ep=1) |
210 |
|
211 |
self.copy_metadata_to(egg_info)
|
212 |
native_libs = os.path.join(egg_info, "native_libs.txt")
|
213 |
if all_outputs:
|
214 |
log.info("writing %s" % native_libs)
|
215 |
if not self.dry_run: |
216 |
ensure_directory(native_libs) |
217 |
libs_file = open(native_libs, 'wt') |
218 |
libs_file.write('\n'.join(all_outputs))
|
219 |
libs_file.write('\n')
|
220 |
libs_file.close() |
221 |
elif os.path.isfile(native_libs):
|
222 |
log.info("removing %s" % native_libs)
|
223 |
if not self.dry_run: |
224 |
os.unlink(native_libs) |
225 |
|
226 |
write_safety_flag( |
227 |
os.path.join(archive_root,'EGG-INFO'), self.zip_safe() |
228 |
) |
229 |
|
230 |
if os.path.exists(os.path.join(self.egg_info,'depends.txt')): |
231 |
log.warn( |
232 |
"WARNING: 'depends.txt' will not be used by setuptools 0.6!\n"
|
233 |
"Use the install_requires/extras_require setup() args instead."
|
234 |
) |
235 |
|
236 |
if self.exclude_source_files: |
237 |
self.zap_pyfiles()
|
238 |
|
239 |
# Make the archive
|
240 |
make_zipfile(self.egg_output, archive_root, verbose=self.verbose, |
241 |
dry_run=self.dry_run, mode=self.gen_header()) |
242 |
if not self.keep_temp: |
243 |
remove_tree(self.bdist_dir, dry_run=self.dry_run) |
244 |
|
245 |
# Add to 'Distribution.dist_files' so that the "upload" command works
|
246 |
getattr(self.distribution,'dist_files',[]).append( |
247 |
('bdist_egg',get_python_version(),self.egg_output)) |
248 |
|
249 |
|
250 |
|
251 |
|
252 |
def zap_pyfiles(self): |
253 |
log.info("Removing .py files from temporary directory")
|
254 |
for base,dirs,files in walk_egg(self.bdist_dir): |
255 |
for name in files: |
256 |
if name.endswith('.py'): |
257 |
path = os.path.join(base,name) |
258 |
log.debug("Deleting %s", path)
|
259 |
os.unlink(path) |
260 |
|
261 |
def zip_safe(self): |
262 |
safe = getattr(self.distribution,'zip_safe',None) |
263 |
if safe is not None: |
264 |
return safe
|
265 |
log.warn("zip_safe flag not set; analyzing archive contents...")
|
266 |
return analyze_egg(self.bdist_dir, self.stubs) |
267 |
|
268 |
def make_init_files(self): |
269 |
"""Create missing package __init__ files"""
|
270 |
init_files = [] |
271 |
for base,dirs,files in walk_egg(self.bdist_dir): |
272 |
if base==self.bdist_dir: |
273 |
# don't put an __init__ in the root
|
274 |
continue
|
275 |
for name in files: |
276 |
if name.endswith('.py'): |
277 |
if '__init__.py' not in files: |
278 |
pkg = base[len(self.bdist_dir)+1:].replace(os.sep,'.') |
279 |
if self.distribution.has_contents_for(pkg): |
280 |
log.warn("Creating missing __init__.py for %s",pkg)
|
281 |
filename = os.path.join(base,'__init__.py')
|
282 |
if not self.dry_run: |
283 |
f = open(filename,'w'); f.write(NS_PKG_STUB) |
284 |
f.close() |
285 |
init_files.append(filename) |
286 |
break
|
287 |
else:
|
288 |
# not a package, don't traverse to subdirectories
|
289 |
dirs[:] = [] |
290 |
|
291 |
return init_files
|
292 |
|
293 |
def gen_header(self): |
294 |
epm = EntryPoint.parse_map(self.distribution.entry_points or '') |
295 |
ep = epm.get('setuptools.installation',{}).get('eggsecutable') |
296 |
if ep is None: |
297 |
return 'w' # not an eggsecutable, do it the usual way. |
298 |
|
299 |
if not ep.attrs or ep.extras: |
300 |
raise DistutilsSetupError(
|
301 |
"eggsecutable entry point (%r) cannot have 'extras' "
|
302 |
"or refer to a module" % (ep,)
|
303 |
) |
304 |
|
305 |
pyver = sys.version[:3]
|
306 |
pkg = ep.module_name |
307 |
full = '.'.join(ep.attrs)
|
308 |
base = ep.attrs[0]
|
309 |
basename = os.path.basename(self.egg_output)
|
310 |
|
311 |
header = ( |
312 |
"#!/bin/sh\n"
|
313 |
'if [ `basename $0` = "%(basename)s" ]\n'
|
314 |
'then exec python%(pyver)s -c "'
|
315 |
"import sys, os; sys.path.insert(0, os.path.abspath('$0')); "
|
316 |
"from %(pkg)s import %(base)s; sys.exit(%(full)s())"
|
317 |
'" "$@"\n'
|
318 |
'else\n'
|
319 |
' echo $0 is not the correct name for this egg file.\n'
|
320 |
' echo Please rename it back to %(basename)s and try again.\n'
|
321 |
' exec false\n'
|
322 |
'fi\n'
|
323 |
|
324 |
) % locals()
|
325 |
|
326 |
if not self.dry_run: |
327 |
mkpath(os.path.dirname(self.egg_output), dry_run=self.dry_run) |
328 |
f = open(self.egg_output, 'w') |
329 |
f.write(header) |
330 |
f.close() |
331 |
return 'a' |
332 |
|
333 |
|
334 |
def copy_metadata_to(self, target_dir): |
335 |
"Copy metadata (egg info) to the target_dir"
|
336 |
# normalize the path (so that a forward-slash in egg_info will
|
337 |
# match using startswith below)
|
338 |
norm_egg_info = os.path.normpath(self.egg_info)
|
339 |
prefix = os.path.join(norm_egg_info,'')
|
340 |
for path in self.ei_cmd.filelist.files: |
341 |
if path.startswith(prefix):
|
342 |
target = os.path.join(target_dir, path[len(prefix):])
|
343 |
ensure_directory(target) |
344 |
self.copy_file(path, target)
|
345 |
|
346 |
def get_ext_outputs(self): |
347 |
"""Get a list of relative paths to C extensions in the output distro"""
|
348 |
|
349 |
all_outputs = [] |
350 |
ext_outputs = [] |
351 |
|
352 |
paths = {self.bdist_dir:''} |
353 |
for base, dirs, files in os.walk(self.bdist_dir): |
354 |
for filename in files: |
355 |
if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS: |
356 |
all_outputs.append(paths[base]+filename) |
357 |
for filename in dirs: |
358 |
paths[os.path.join(base,filename)] = paths[base]+filename+'/'
|
359 |
|
360 |
if self.distribution.has_ext_modules(): |
361 |
build_cmd = self.get_finalized_command('build_ext') |
362 |
for ext in build_cmd.extensions: |
363 |
if isinstance(ext,Library): |
364 |
continue
|
365 |
fullname = build_cmd.get_ext_fullname(ext.name) |
366 |
filename = build_cmd.get_ext_filename(fullname) |
367 |
if not os.path.basename(filename).startswith('dl-'): |
368 |
if os.path.exists(os.path.join(self.bdist_dir,filename)): |
369 |
ext_outputs.append(filename) |
370 |
|
371 |
return all_outputs, ext_outputs
|
372 |
|
373 |
|
374 |
NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split()) |
375 |
|
376 |
|
377 |
|
378 |
|
379 |
def walk_egg(egg_dir): |
380 |
"""Walk an unpacked egg's contents, skipping the metadata directory"""
|
381 |
walker = os.walk(egg_dir) |
382 |
base,dirs,files = walker.next() |
383 |
if 'EGG-INFO' in dirs: |
384 |
dirs.remove('EGG-INFO')
|
385 |
yield base,dirs,files
|
386 |
for bdf in walker: |
387 |
yield bdf
|
388 |
|
389 |
def analyze_egg(egg_dir, stubs): |
390 |
# check for existing flag in EGG-INFO
|
391 |
for flag,fn in safety_flags.items(): |
392 |
if os.path.exists(os.path.join(egg_dir,'EGG-INFO',fn)): |
393 |
return flag
|
394 |
if not can_scan(): return False |
395 |
safe = True
|
396 |
for base, dirs, files in walk_egg(egg_dir): |
397 |
for name in files: |
398 |
if name.endswith('.py') or name.endswith('.pyw'): |
399 |
continue
|
400 |
elif name.endswith('.pyc') or name.endswith('.pyo'): |
401 |
# always scan, even if we already know we're not safe
|
402 |
safe = scan_module(egg_dir, base, name, stubs) and safe
|
403 |
return safe
|
404 |
|
405 |
def write_safety_flag(egg_dir, safe): |
406 |
# Write or remove zip safety flag file(s)
|
407 |
for flag,fn in safety_flags.items(): |
408 |
fn = os.path.join(egg_dir, fn) |
409 |
if os.path.exists(fn):
|
410 |
if safe is None or bool(safe)<>flag: |
411 |
os.unlink(fn) |
412 |
elif safe is not None and bool(safe)==flag: |
413 |
f=open(fn,'wt'); f.write('\n'); f.close() |
414 |
|
415 |
safety_flags = { |
416 |
True: 'zip-safe', |
417 |
False: 'not-zip-safe', |
418 |
} |
419 |
|
420 |
def scan_module(egg_dir, base, name, stubs): |
421 |
"""Check whether module possibly uses unsafe-for-zipfile stuff"""
|
422 |
|
423 |
filename = os.path.join(base,name) |
424 |
if filename[:-1] in stubs: |
425 |
return True # Extension module |
426 |
pkg = base[len(egg_dir)+1:].replace(os.sep,'.') |
427 |
module = pkg+(pkg and '.' or '')+os.path.splitext(name)[0] |
428 |
f = open(filename,'rb'); f.read(8) # skip magic & date |
429 |
code = marshal.load(f); f.close() |
430 |
safe = True
|
431 |
symbols = dict.fromkeys(iter_symbols(code))
|
432 |
for bad in ['__file__', '__path__']: |
433 |
if bad in symbols: |
434 |
log.warn("%s: module references %s", module, bad)
|
435 |
safe = False
|
436 |
if 'inspect' in symbols: |
437 |
for bad in [ |
438 |
'getsource', 'getabsfile', 'getsourcefile', 'getfile' |
439 |
'getsourcelines', 'findsource', 'getcomments', 'getframeinfo', |
440 |
'getinnerframes', 'getouterframes', 'stack', 'trace' |
441 |
]: |
442 |
if bad in symbols: |
443 |
log.warn("%s: module MAY be using inspect.%s", module, bad)
|
444 |
safe = False
|
445 |
if '__name__' in symbols and '__main__' in symbols and '.' not in module: |
446 |
if sys.version[:3]=="2.4": # -m works w/zipfiles in 2.5 |
447 |
log.warn("%s: top-level module may be 'python -m' script", module)
|
448 |
safe = False
|
449 |
return safe
|
450 |
|
451 |
def iter_symbols(code): |
452 |
"""Yield names and strings used by `code` and its nested code objects"""
|
453 |
for name in code.co_names: yield name |
454 |
for const in code.co_consts: |
455 |
if isinstance(const,basestring): |
456 |
yield const
|
457 |
elif isinstance(const,CodeType): |
458 |
for name in iter_symbols(const): |
459 |
yield name
|
460 |
|
461 |
def can_scan(): |
462 |
if not sys.platform.startswith('java') and sys.platform != 'cli': |
463 |
# CPython, PyPy, etc.
|
464 |
return True |
465 |
log.warn("Unable to analyze compiled code on this platform.")
|
466 |
log.warn("Please ask the author to include a 'zip_safe'"
|
467 |
" setting (either True or False) in the package's setup.py")
|
468 |
|
469 |
|
470 |
|
471 |
|
472 |
|
473 |
|
474 |
|
475 |
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 |
|
484 |
|
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 |
|
493 |
|
494 |
|
495 |
|
496 |
|
497 |
|
498 |
|
499 |
|
500 |
|
501 |
|
502 |
# Attribute names of options for commands that might need to be convinced to
|
503 |
# install to the egg build directory
|
504 |
|
505 |
INSTALL_DIRECTORY_ATTRS = [ |
506 |
'install_lib', 'install_dir', 'install_data', 'install_base' |
507 |
] |
508 |
|
509 |
def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=None, |
510 |
mode='w'
|
511 |
): |
512 |
"""Create a zip file from all the files under 'base_dir'. The output
|
513 |
zip file will be named 'base_dir' + ".zip". Uses either the "zipfile"
|
514 |
Python module (if available) or the InfoZIP "zip" utility (if installed
|
515 |
and found on the default search path). If neither tool is available,
|
516 |
raises DistutilsExecError. Returns the name of the output zip file.
|
517 |
"""
|
518 |
import zipfile |
519 |
mkpath(os.path.dirname(zip_filename), dry_run=dry_run) |
520 |
log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
|
521 |
|
522 |
def visit(z, dirname, names): |
523 |
for name in names: |
524 |
path = os.path.normpath(os.path.join(dirname, name)) |
525 |
if os.path.isfile(path):
|
526 |
p = path[len(base_dir)+1:] |
527 |
if not dry_run: |
528 |
z.write(path, p) |
529 |
log.debug("adding '%s'" % p)
|
530 |
|
531 |
if compress is None: |
532 |
compress = (sys.version>="2.4") # avoid 2.3 zipimport bug when 64 bits |
533 |
|
534 |
compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)]
|
535 |
if not dry_run: |
536 |
z = zipfile.ZipFile(zip_filename, mode, compression=compression) |
537 |
for dirname, dirs, files in os.walk(base_dir): |
538 |
visit(z, dirname, files) |
539 |
z.close() |
540 |
else:
|
541 |
for dirname, dirs, files in os.walk(base_dir): |
542 |
visit(None, dirname, files)
|
543 |
return zip_filename
|
544 |
#
|