148
174
########################
176
from bzrlib.bzr_distutils import build_mo
150
178
command_classes = {'install_scripts': my_install_scripts,
180
'build_mo': build_mo,
182
from distutils import log
183
from distutils.errors import CCompilerError, DistutilsPlatformError
184
from distutils.extension import Extension
154
from Pyrex.Distutils import build_ext
188
from Cython.Distutils import build_ext
189
from Cython.Compiler.Version import version as pyrex_version
191
print("No Cython, trying Pyrex...")
192
from Pyrex.Distutils import build_ext
193
from Pyrex.Compiler.Version import version as pyrex_version
155
194
except ImportError:
156
196
# try to build the extension from the prior generated source.
157
print ("Pyrex not available, while bzr will build, "
158
"you cannot modify the C extensions.")
198
print("The python package 'Pyrex' is not available."
199
" If the .c files are available,")
200
print("they will be built,"
201
" but modifying the .pyx files will not rebuild them.")
159
203
from distutils.command.build_ext import build_ext
160
from distutils.extension import Extension
162
# Extension("bzrlib.modulename", ["bzrlib/foo.c"], libraries = []))
164
from distutils.extension import Extension
166
# Extension("bzrlib.modulename", ["bzrlib/foo.pyx"], libraries = []))
167
command_classes['build_ext'] = build_ext
206
pyrex_version_info = LooseVersion(pyrex_version)
209
class build_ext_if_possible(build_ext):
211
user_options = build_ext.user_options + [
212
('allow-python-fallback', None,
213
"When an extension cannot be built, allow falling"
214
" back to the pure-python implementation.")
217
def initialize_options(self):
218
build_ext.initialize_options(self)
219
self.allow_python_fallback = False
224
except DistutilsPlatformError:
225
e = sys.exc_info()[1]
226
if not self.allow_python_fallback:
227
log.warn('\n Cannot build extensions.\n'
228
' Use "build_ext --allow-python-fallback" to use'
229
' slower python implementations instead.\n')
232
log.warn('\n Extensions cannot be built.\n'
233
' Using the slower Python implementations instead.\n')
235
def build_extension(self, ext):
237
build_ext.build_extension(self, ext)
238
except CCompilerError:
239
if not self.allow_python_fallback:
240
log.warn('\n Cannot build extension "%s".\n'
241
' Use "build_ext --allow-python-fallback" to use'
242
' slower python implementations instead.\n'
245
log.warn('\n Building of "%s" extension failed.\n'
246
' Using the slower Python implementation instead.'
250
# Override the build_ext if we have Pyrex available
251
command_classes['build_ext'] = build_ext_if_possible
252
unavailable_files = []
255
def add_pyrex_extension(module_name, libraries=None, extra_source=[]):
256
"""Add a pyrex module to build.
258
This will use Pyrex to auto-generate the .c file if it is available.
259
Otherwise it will fall back on the .c file. If the .c file is not
260
available, it will warn, and not add anything.
262
You can pass any extra options to Extension through kwargs. One example is
265
:param module_name: The python path to the module. This will be used to
266
determine the .pyx and .c files to use.
268
path = module_name.replace('.', '/')
269
pyrex_name = path + '.pyx'
272
if sys.platform == 'win32':
273
# pyrex uses the macro WIN32 to detect the platform, even though it
274
# should be using something like _WIN32 or MS_WINDOWS, oh well, we can
275
# give it the right value.
276
define_macros.append(('WIN32', None))
278
source = [pyrex_name]
280
if not os.path.isfile(c_name):
281
unavailable_files.append(c_name)
285
source.extend(extra_source)
286
ext_modules.append(Extension(module_name, source,
287
define_macros=define_macros, libraries=libraries))
290
add_pyrex_extension('bzrlib._annotator_pyx')
291
add_pyrex_extension('bzrlib._bencode_pyx')
292
add_pyrex_extension('bzrlib._chunks_to_lines_pyx')
293
add_pyrex_extension('bzrlib._groupcompress_pyx',
294
extra_source=['bzrlib/diff-delta.c'])
295
add_pyrex_extension('bzrlib._knit_load_data_pyx')
296
add_pyrex_extension('bzrlib._known_graph_pyx')
297
add_pyrex_extension('bzrlib._rio_pyx')
298
if sys.platform == 'win32':
299
add_pyrex_extension('bzrlib._dirstate_helpers_pyx',
300
libraries=['Ws2_32'])
301
add_pyrex_extension('bzrlib._walkdirs_win32')
303
if have_pyrex and pyrex_version_info == LooseVersion("0.9.4.1"):
304
# Pyrex 0.9.4.1 fails to compile this extension correctly
305
# The code it generates re-uses a "local" pointer and
306
# calls "PY_DECREF" after having set it to NULL. (It mixes PY_XDECREF
307
# which is NULL safe with PY_DECREF which is not.)
308
# <https://bugs.launchpad.net/bzr/+bug/449372>
309
# <https://bugs.launchpad.net/bzr/+bug/276868>
310
print('Cannot build extension "bzrlib._dirstate_helpers_pyx" using')
311
print('your version of pyrex "%s". Please upgrade your pyrex'
313
print('install. For now, the non-compiled (python) version will')
314
print('be used instead.')
316
add_pyrex_extension('bzrlib._dirstate_helpers_pyx')
317
add_pyrex_extension('bzrlib._readdir_pyx')
318
add_pyrex_extension('bzrlib._chk_map_pyx')
319
ext_modules.append(Extension('bzrlib._patiencediff_c',
320
['bzrlib/_patiencediff_c.c']))
321
if have_pyrex and pyrex_version_info < LooseVersion("0.9.6.3"):
323
print('Your Pyrex/Cython version %s is too old to build the simple_set' % (
325
print('and static_tuple extensions.')
326
print('Please upgrade to at least Pyrex 0.9.6.3')
328
# TODO: Should this be a fatal error?
330
# We only need 0.9.6.3 to build _simple_set_pyx, but static_tuple depends
332
add_pyrex_extension('bzrlib._simple_set_pyx')
333
ext_modules.append(Extension('bzrlib._static_tuple_c',
334
['bzrlib/_static_tuple_c.c']))
335
add_pyrex_extension('bzrlib._btree_serializer_pyx')
338
if unavailable_files:
339
print('C extension(s) not found:')
340
print(' %s' % ('\n '.join(unavailable_files),))
341
print('The python versions will be used instead.')
345
def get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
346
gui_targets, data_files):
347
packages.append('tbzrcommands')
349
# ModuleFinder can't handle runtime changes to __path__, but
350
# win32com uses them. Hook this in so win32com.shell is found.
353
import cPickle as pickle
354
for p in win32com.__path__[1:]:
355
modulefinder.AddPackagePath("win32com", p)
356
for extra in ["win32com.shell"]:
358
m = sys.modules[extra]
359
for p in m.__path__[1:]:
360
modulefinder.AddPackagePath(extra, p)
362
# TBZR points to the TBZR directory
363
tbzr_root = os.environ["TBZR"]
365
# Ensure tbzrlib itself is on sys.path
366
sys.path.append(tbzr_root)
368
packages.append("tbzrlib")
370
# collect up our icons.
372
ico_root = os.path.join(tbzr_root, 'tbzrlib', 'resources')
373
icos = [] # list of (path_root, relative_ico_path)
374
# First always bzr's icon and its in the root of the bzr tree.
375
icos.append(('', 'bzr.ico'))
376
for root, dirs, files in os.walk(ico_root):
377
icos.extend([(ico_root, os.path.join(root, f)[len(ico_root)+1:])
378
for f in files if f.endswith('.ico')])
379
# allocate an icon ID for each file and the full path to the ico
380
icon_resources = [(rid, os.path.join(ico_dir, ico_name))
381
for rid, (ico_dir, ico_name) in enumerate(icos)]
382
# create a string resource with the mapping. Might as well save the
383
# runtime some effort and write a pickle.
384
# Runtime expects unicode objects with forward-slash seps.
385
fse = sys.getfilesystemencoding()
386
map_items = [(f.replace('\\', '/').decode(fse), rid)
387
for rid, (_, f) in enumerate(icos)]
388
ico_map = dict(map_items)
389
# Create a new resource type of 'ICON_MAP', and use ID=1
390
other_resources = [ ("ICON_MAP", 1, pickle.dumps(ico_map))]
392
excludes.extend("""pywin pywin.dialogs pywin.dialogs.list
393
win32ui crawler.Crawler""".split())
395
# tbzrcache executables - a "console" version for debugging and a
396
# GUI version that is generally used.
398
script = os.path.join(tbzr_root, "scripts", "tbzrcache.py"),
399
icon_resources = icon_resources,
400
other_resources = other_resources,
402
console_targets.append(tbzrcache)
404
# Make a windows version which is the same except for the base name.
405
tbzrcachew = tbzrcache.copy()
406
tbzrcachew["dest_base"]="tbzrcachew"
407
gui_targets.append(tbzrcachew)
409
# ditto for the tbzrcommand tool
411
script = os.path.join(tbzr_root, "scripts", "tbzrcommand.py"),
412
icon_resources = icon_resources,
413
other_resources = other_resources,
415
console_targets.append(tbzrcommand)
416
tbzrcommandw = tbzrcommand.copy()
417
tbzrcommandw["dest_base"]="tbzrcommandw"
418
gui_targets.append(tbzrcommandw)
420
# A utility to see python output from both C++ and Python based shell
422
tracer = dict(script=os.path.join(tbzr_root, "scripts", "tbzrtrace.py"))
423
console_targets.append(tracer)
425
# The C++ implemented shell extensions.
426
dist_dir = os.path.join(tbzr_root, "shellext", "build")
427
data_files.append(('', [os.path.join(dist_dir, 'tbzrshellext_x86.dll')]))
428
data_files.append(('', [os.path.join(dist_dir, 'tbzrshellext_x64.dll')]))
431
def get_qbzr_py2exe_info(includes, excludes, packages, data_files):
432
# PyQt4 itself still escapes the plugin detection code for some reason...
433
includes.append('PyQt4.QtCore')
434
includes.append('PyQt4.QtGui')
435
includes.append('PyQt4.QtTest')
436
includes.append('sip') # extension module required for Qt.
437
packages.append('pygments') # colorizer for qbzr
438
packages.append('docutils') # html formatting
439
includes.append('win32event') # for qsubprocess stuff
440
# the qt binaries might not be on PATH...
441
# They seem to install to a place like C:\Python25\PyQt4\*
442
# Which is not the same as C:\Python25\Lib\site-packages\PyQt4
443
pyqt_dir = os.path.join(sys.prefix, "PyQt4")
444
pyqt_bin_dir = os.path.join(pyqt_dir, "bin")
445
if os.path.isdir(pyqt_bin_dir):
446
path = os.environ.get("PATH", "")
447
if pyqt_bin_dir.lower() not in [p.lower() for p in path.split(os.pathsep)]:
448
os.environ["PATH"] = path + os.pathsep + pyqt_bin_dir
449
# also add all imageformat plugins to distribution
450
# We will look in 2 places, dirname(PyQt4.__file__) and pyqt_dir
451
base_dirs_to_check = []
452
if os.path.isdir(pyqt_dir):
453
base_dirs_to_check.append(pyqt_dir)
459
pyqt4_base_dir = os.path.dirname(PyQt4.__file__)
460
if pyqt4_base_dir != pyqt_dir:
461
base_dirs_to_check.append(pyqt4_base_dir)
462
if not base_dirs_to_check:
463
log.warn("Can't find PyQt4 installation -> not including imageformat"
467
for base_dir in base_dirs_to_check:
468
plug_dir = os.path.join(base_dir, 'plugins', 'imageformats')
469
if os.path.isdir(plug_dir):
470
for fname in os.listdir(plug_dir):
471
# Include plugin dlls, but not debugging dlls
472
fullpath = os.path.join(plug_dir, fname)
473
if fname.endswith('.dll') and not fname.endswith('d4.dll'):
474
files.append(fullpath)
476
data_files.append(('imageformats', files))
478
log.warn('PyQt4 was found, but we could not find any imageformat'
479
' plugins. Are you sure your configuration is correct?')
482
def get_svn_py2exe_info(includes, excludes, packages):
483
packages.append('subvertpy')
484
packages.append('sqlite3')
487
def get_git_py2exe_info(includes, excludes, packages):
488
packages.append('dulwich')
491
def get_fastimport_py2exe_info(includes, excludes, packages):
492
# This is the python-fastimport package, not to be confused with the
493
# bzr-fastimport plugin.
494
packages.append('fastimport')
169
497
if 'bdist_wininst' in sys.argv:
172
docs = glob.glob('doc/*.htm') + ['doc/default.css']
173
dev_docs = glob.glob('doc/developers/*.htm')
500
for root, dirs, files in os.walk('doc'):
503
if (os.path.splitext(f)[1] in ('.html','.css','.png','.pdf')
504
or f == 'quick-start-summary.svg'):
505
r.append(os.path.join(root, f))
509
target = os.path.join('Doc\\Bazaar', relative)
511
target = 'Doc\\Bazaar'
512
docs.append((target, r))
174
515
# python's distutils-based win32 installer
175
516
ARGS = {'scripts': ['bzr', 'tools/win32/bzr-win32-bdist-postinstall.py'],
517
'ext_modules': ext_modules,
177
'data_files': [('Doc/Bazaar', docs),
178
('Doc/Bazaar/developers', dev_docs),
519
'data_files': find_docs(),
520
# for building pyrex extensions
521
'cmdclass': command_classes,
182
524
ARGS.update(META_INFO)
183
525
ARGS.update(BZRLIB)
526
PKG_DATA['package_data']['bzrlib'].append('locale/*/LC_MESSAGES/*.mo')
184
527
ARGS.update(PKG_DATA)
188
531
elif 'py2exe' in sys.argv:
208
581
version = version_str,
209
582
description = META_INFO['description'],
210
583
author = META_INFO['author'],
211
copyright = "(c) Canonical Ltd, 2005-2007",
584
copyright = "(c) Canonical Ltd, 2005-2010",
212
585
company_name = "Canonical Ltd.",
213
586
comments = META_INFO['description'],
216
additional_packages = []
588
gui_target = copy.copy(target)
589
gui_target.dest_base = "bzrw"
591
packages = BZRLIB['packages']
592
packages.remove('bzrlib')
593
packages = [i for i in packages if not i.startswith('bzrlib.plugins')]
595
for i in glob.glob('bzrlib\\*.py'):
596
module = i[:-3].replace('\\', '.')
597
if module.endswith('__init__'):
598
module = module[:-len('__init__')]
599
includes.append(module)
601
additional_packages = set()
217
602
if sys.version.startswith('2.4'):
218
603
# adding elementtree package
219
additional_packages.append('elementtree')
220
elif sys.version.startswith('2.5'):
221
additional_packages.append('xml.etree')
604
additional_packages.add('elementtree')
605
elif sys.version.startswith('2.6') or sys.version.startswith('2.5'):
606
additional_packages.add('xml.etree')
224
609
warnings.warn('Unknown Python version.\n'
225
610
'Please check setup.py script for compatibility.')
227
options_list = {"py2exe": {"packages": BZRLIB['packages'] +
229
"excludes": ["Tkinter", "medusa", "tools"],
612
# Although we currently can't enforce it, we consider it an error for
613
# py2exe to report any files are "missing". Such modules we know aren't
614
# used should be listed here.
615
excludes = """Tkinter psyco ElementPath r_hmac
616
ImaginaryModule cElementTree elementtree.ElementTree
617
Crypto.PublicKey._fastmath
618
medusa medusa.filesys medusa.ftp_server
620
resource validate""".split()
623
# email package from std python library use lazy import,
624
# so we need to explicitly add all package
625
additional_packages.add('email')
626
# And it uses funky mappings to conver to 'Oldname' to 'newname'. As
627
# a result, packages like 'email.Parser' show as missing. Tell py2exe
630
for oldname in getattr(email, '_LOWERNAMES', []):
631
excludes.append("email." + oldname)
632
for oldname in getattr(email, '_MIMENAMES', []):
633
excludes.append("email.MIME" + oldname)
635
# text files for help topis
636
text_topics = glob.glob('bzrlib/help_topics/en/*.txt')
637
topics_files = [('lib/help_topics/en', text_topics)]
641
# XXX - should we consider having the concept of an 'official' build,
642
# which hard-codes the list of plugins, gets more upset if modules are
644
plugins = None # will be a set after plugin sniffing...
645
for root, dirs, files in os.walk('bzrlib/plugins'):
646
if root == 'bzrlib/plugins':
648
# We ship plugins as normal files on the file-system - however,
649
# the build process can cause *some* of these plugin files to end
650
# up in library.zip. Thus, we saw (eg) "plugins/svn/test" in
651
# library.zip, and then saw import errors related to that as the
652
# rest of the svn plugin wasn't. So we tell py2exe to leave the
653
# plugins out of the .zip file
654
excludes.extend(["bzrlib.plugins." + d for d in dirs])
657
# Throw away files we don't want packaged. Note that plugins may
658
# have data files with all sorts of extensions so we need to
659
# be conservative here about what we ditch.
660
ext = os.path.splitext(i)[1]
661
if ext.endswith('~') or ext in [".pyc", ".swp"]:
663
if i == '__init__.py' and root == 'bzrlib/plugins':
665
x.append(os.path.join(root, i))
667
target_dir = root[len('bzrlib/'):] # install to 'plugins/...'
668
plugins_files.append((target_dir, x))
669
# find modules for built-in plugins
670
import tools.package_mf
671
mf = tools.package_mf.CustomModuleFinder()
672
mf.run_package('bzrlib/plugins')
673
packs, mods = mf.get_result()
674
additional_packages.update(packs)
675
includes.extend(mods)
677
console_targets = [target,
678
'tools/win32/bzr_postinstall.py',
680
gui_targets = [gui_target]
681
data_files = topics_files + plugins_files + I18N_FILES
683
if 'qbzr' in plugins:
684
get_qbzr_py2exe_info(includes, excludes, packages, data_files)
687
get_svn_py2exe_info(includes, excludes, packages)
690
get_git_py2exe_info(includes, excludes, packages)
692
if 'fastimport' in plugins:
693
get_fastimport_py2exe_info(includes, excludes, packages)
695
if "TBZR" in os.environ:
696
# TORTOISE_OVERLAYS_MSI_WIN32 must be set to the location of the
697
# TortoiseOverlays MSI installer file. It is in the TSVN svn repo and
698
# can be downloaded from (username=guest, blank password):
699
# http://tortoisesvn.tigris.org/svn/tortoisesvn/TortoiseOverlays
700
# look for: version-1.0.4/bin/TortoiseOverlays-1.0.4.11886-win32.msi
701
# Ditto for TORTOISE_OVERLAYS_MSI_X64, pointing at *-x64.msi.
702
for needed in ('TORTOISE_OVERLAYS_MSI_WIN32',
703
'TORTOISE_OVERLAYS_MSI_X64'):
704
url = ('http://guest:@tortoisesvn.tigris.org/svn/tortoisesvn'
706
if not os.path.isfile(os.environ.get(needed, '<nofile>')):
708
"\nPlease set %s to the location of the relevant"
709
"\nTortoiseOverlays .msi installer file."
710
" The installers can be found at"
712
"\ncheck in the version-X.Y.Z/bin/ subdir" % (needed, url))
713
get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
714
gui_targets, data_files)
716
# print this warning to stderr as output is redirected, so it is seen
717
# at build time. Also to stdout so it appears in the log
718
for f in (sys.stderr, sys.stdout):
719
f.write("Skipping TBZR binaries - "
720
"please set TBZR to a directory to enable\n")
722
# MSWSOCK.dll is a system-specific library, which py2exe accidentally pulls
724
dll_excludes.extend(["MSWSOCK.dll",
729
options_list = {"py2exe": {"packages": packages + list(additional_packages),
730
"includes": includes,
731
"excludes": excludes,
732
"dll_excludes": dll_excludes,
230
733
"dist_dir": "win32_bzr.exe",
735
"custom_boot_script":
736
"tools/win32/py2exe_boot_common.py",
233
setup(options=options_list,
235
'tools/win32/bzr_postinstall.py',
237
zipfile='lib/library.zip')
740
# We want the libaray.zip to have optimize = 2, but the exe to have
741
# optimize = 1, so that .py files that get compilied at run time
742
# (e.g. user installed plugins) dont have their doc strings removed.
743
class py2exe_no_oo_exe(py2exe.build_exe.py2exe):
744
def build_executable(self, *args, **kwargs):
746
py2exe.build_exe.py2exe.build_executable(self, *args, **kwargs)
749
if __name__ == '__main__':
750
command_classes['install_data'] = install_data_with_bytecompile
751
command_classes['py2exe'] = py2exe_no_oo_exe
752
setup(options=options_list,
753
console=console_targets,
755
zipfile='lib/library.zip',
756
data_files=data_files,
757
cmdclass=command_classes,
761
# ad-hoc for easy_install
763
if not 'bdist_egg' in sys.argv:
764
# generate and install bzr.1 only with plain install, not the
766
DATA_FILES = [('man/man1', ['bzr.1'])]
768
DATA_FILES = DATA_FILES + I18N_FILES
241
770
ARGS = {'scripts': ['bzr'],
242
'data_files': [('man/man1', ['bzr.1'])],
771
'data_files': DATA_FILES,
243
772
'cmdclass': command_classes,
244
773
'ext_modules': ext_modules,
247
776
ARGS.update(META_INFO)
248
777
ARGS.update(BZRLIB)
249
778
ARGS.update(PKG_DATA)
780
if __name__ == '__main__':