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 | 1a305335 | officers | """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 | # |