114
47
Create bzr.bat for win32.
117
53
install_scripts.run(self) # standard action
119
55
if sys.platform == "win32":
121
scripts_dir = os.path.join(sys.prefix, 'Scripts')
122
script_path = self._quoted_path(os.path.join(scripts_dir,
124
python_exe = self._quoted_path(sys.executable)
125
args = self._win_batch_args()
126
batch_str = "@%s %s %s" % (python_exe, script_path, args)
127
batch_path = os.path.join(self.install_dir, "bzr.bat")
57
scripts_dir = self.install_dir
58
script_path = os.path.join(scripts_dir, "bzr")
59
batch_str = "@%s %s %%*\n" % (sys.executable, script_path)
60
batch_path = script_path + ".bat"
128
61
f = file(batch_path, "w")
129
62
f.write(batch_str)
131
print("Created: %s" % batch_path)
133
e = sys.exc_info()[1]
134
print("ERROR: Unable to create %s: %s" % (batch_path, e))
136
def _quoted_path(self, path):
138
return '"' + path + '"'
142
def _win_batch_args(self):
143
from bzrlib.win32utils import winver
144
if winver == 'Windows NT':
147
return '%1 %2 %3 %4 %5 %6 %7 %8 %9'
148
#/class my_install_scripts
64
print "Created:", batch_path
66
print "ERROR: Unable to create %s: %s" % (batch_path, e)
151
69
class bzr_build(build):
152
70
"""Customized build distutils action.
156
sub_commands = build.sub_commands + [
157
('build_mo', lambda _: True),
163
from tools import generate_docs
164
generate_docs.main(argv=["bzr", "man"])
167
79
########################
169
81
########################
171
from tools.build_mo import build_mo
173
command_classes = {'install_scripts': my_install_scripts,
175
'build_mo': build_mo,
177
from distutils import log
178
from distutils.errors import CCompilerError, DistutilsPlatformError
179
from distutils.extension import Extension
183
from Cython.Distutils import build_ext
184
from Cython.Compiler.Version import version as pyrex_version
186
print("No Cython, trying Pyrex...")
187
from Pyrex.Distutils import build_ext
188
from Pyrex.Compiler.Version import version as pyrex_version
191
# try to build the extension from the prior generated source.
193
print("The python package 'Pyrex' is not available."
194
" If the .c files are available,")
195
print("they will be built,"
196
" but modifying the .pyx files will not rebuild them.")
198
from distutils.command.build_ext import build_ext
201
pyrex_version_info = tuple(map(int, pyrex_version.split('.')))
204
class build_ext_if_possible(build_ext):
206
user_options = build_ext.user_options + [
207
('allow-python-fallback', None,
208
"When an extension cannot be built, allow falling"
209
" back to the pure-python implementation.")
212
def initialize_options(self):
213
build_ext.initialize_options(self)
214
self.allow_python_fallback = False
219
except DistutilsPlatformError:
220
e = sys.exc_info()[1]
221
if not self.allow_python_fallback:
222
log.warn('\n Cannot build extensions.\n'
223
' Use "build_ext --allow-python-fallback" to use'
224
' slower python implementations instead.\n')
227
log.warn('\n Extensions cannot be built.\n'
228
' Using the slower Python implementations instead.\n')
230
def build_extension(self, ext):
232
build_ext.build_extension(self, ext)
233
except CCompilerError:
234
if not self.allow_python_fallback:
235
log.warn('\n Cannot build extension "%s".\n'
236
' Use "build_ext --allow-python-fallback" to use'
237
' slower python implementations instead.\n'
240
log.warn('\n Building of "%s" extension failed.\n'
241
' Using the slower Python implementation instead.'
245
# Override the build_ext if we have Pyrex available
246
command_classes['build_ext'] = build_ext_if_possible
247
unavailable_files = []
250
def add_pyrex_extension(module_name, libraries=None, extra_source=[]):
251
"""Add a pyrex module to build.
253
This will use Pyrex to auto-generate the .c file if it is available.
254
Otherwise it will fall back on the .c file. If the .c file is not
255
available, it will warn, and not add anything.
257
You can pass any extra options to Extension through kwargs. One example is
260
:param module_name: The python path to the module. This will be used to
261
determine the .pyx and .c files to use.
263
path = module_name.replace('.', '/')
264
pyrex_name = path + '.pyx'
267
if sys.platform == 'win32':
268
# pyrex uses the macro WIN32 to detect the platform, even though it
269
# should be using something like _WIN32 or MS_WINDOWS, oh well, we can
270
# give it the right value.
271
define_macros.append(('WIN32', None))
273
source = [pyrex_name]
275
if not os.path.isfile(c_name):
276
unavailable_files.append(c_name)
280
source.extend(extra_source)
281
ext_modules.append(Extension(module_name, source,
282
define_macros=define_macros, libraries=libraries))
285
add_pyrex_extension('bzrlib._annotator_pyx')
286
add_pyrex_extension('bzrlib._bencode_pyx')
287
add_pyrex_extension('bzrlib._chunks_to_lines_pyx')
288
add_pyrex_extension('bzrlib._groupcompress_pyx',
289
extra_source=['bzrlib/diff-delta.c'])
290
add_pyrex_extension('bzrlib._knit_load_data_pyx')
291
add_pyrex_extension('bzrlib._known_graph_pyx')
292
add_pyrex_extension('bzrlib._rio_pyx')
293
if sys.platform == 'win32':
294
add_pyrex_extension('bzrlib._dirstate_helpers_pyx',
295
libraries=['Ws2_32'])
296
add_pyrex_extension('bzrlib._walkdirs_win32')
298
if have_pyrex and pyrex_version_info[:3] == (0,9,4):
299
# Pyrex 0.9.4.1 fails to compile this extension correctly
300
# The code it generates re-uses a "local" pointer and
301
# calls "PY_DECREF" after having set it to NULL. (It mixes PY_XDECREF
302
# which is NULL safe with PY_DECREF which is not.)
303
# <https://bugs.launchpad.net/bzr/+bug/449372>
304
# <https://bugs.launchpad.net/bzr/+bug/276868>
305
print('Cannot build extension "bzrlib._dirstate_helpers_pyx" using')
306
print('your version of pyrex "%s". Please upgrade your pyrex'
308
print('install. For now, the non-compiled (python) version will')
309
print('be used instead.')
311
add_pyrex_extension('bzrlib._dirstate_helpers_pyx')
312
add_pyrex_extension('bzrlib._readdir_pyx')
313
add_pyrex_extension('bzrlib._chk_map_pyx')
314
ext_modules.append(Extension('bzrlib._patiencediff_c',
315
['bzrlib/_patiencediff_c.c']))
316
if have_pyrex and pyrex_version_info < (0, 9, 6, 3):
318
print('Your Pyrex/Cython version %s is too old to build the simple_set' % (
320
print('and static_tuple extensions.')
321
print('Please upgrade to at least Pyrex 0.9.6.3')
323
# TODO: Should this be a fatal error?
325
# We only need 0.9.6.3 to build _simple_set_pyx, but static_tuple depends
327
add_pyrex_extension('bzrlib._simple_set_pyx')
328
ext_modules.append(Extension('bzrlib._static_tuple_c',
329
['bzrlib/_static_tuple_c.c']))
330
add_pyrex_extension('bzrlib._btree_serializer_pyx')
333
if unavailable_files:
334
print('C extension(s) not found:')
335
print(' %s' % ('\n '.join(unavailable_files),))
336
print('The python versions will be used instead.')
340
def get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
341
gui_targets, data_files):
342
packages.append('tbzrcommands')
344
# ModuleFinder can't handle runtime changes to __path__, but
345
# win32com uses them. Hook this in so win32com.shell is found.
348
import cPickle as pickle
349
for p in win32com.__path__[1:]:
350
modulefinder.AddPackagePath("win32com", p)
351
for extra in ["win32com.shell"]:
353
m = sys.modules[extra]
354
for p in m.__path__[1:]:
355
modulefinder.AddPackagePath(extra, p)
357
# TBZR points to the TBZR directory
358
tbzr_root = os.environ["TBZR"]
360
# Ensure tbzrlib itself is on sys.path
361
sys.path.append(tbzr_root)
363
packages.append("tbzrlib")
365
# collect up our icons.
367
ico_root = os.path.join(tbzr_root, 'tbzrlib', 'resources')
368
icos = [] # list of (path_root, relative_ico_path)
369
# First always bzr's icon and its in the root of the bzr tree.
370
icos.append(('', 'bzr.ico'))
371
for root, dirs, files in os.walk(ico_root):
372
icos.extend([(ico_root, os.path.join(root, f)[len(ico_root)+1:])
373
for f in files if f.endswith('.ico')])
374
# allocate an icon ID for each file and the full path to the ico
375
icon_resources = [(rid, os.path.join(ico_dir, ico_name))
376
for rid, (ico_dir, ico_name) in enumerate(icos)]
377
# create a string resource with the mapping. Might as well save the
378
# runtime some effort and write a pickle.
379
# Runtime expects unicode objects with forward-slash seps.
380
fse = sys.getfilesystemencoding()
381
map_items = [(f.replace('\\', '/').decode(fse), rid)
382
for rid, (_, f) in enumerate(icos)]
383
ico_map = dict(map_items)
384
# Create a new resource type of 'ICON_MAP', and use ID=1
385
other_resources = [ ("ICON_MAP", 1, pickle.dumps(ico_map))]
387
excludes.extend("""pywin pywin.dialogs pywin.dialogs.list
388
win32ui crawler.Crawler""".split())
390
# tbzrcache executables - a "console" version for debugging and a
391
# GUI version that is generally used.
393
script = os.path.join(tbzr_root, "scripts", "tbzrcache.py"),
394
icon_resources = icon_resources,
395
other_resources = other_resources,
397
console_targets.append(tbzrcache)
399
# Make a windows version which is the same except for the base name.
400
tbzrcachew = tbzrcache.copy()
401
tbzrcachew["dest_base"]="tbzrcachew"
402
gui_targets.append(tbzrcachew)
404
# ditto for the tbzrcommand tool
406
script = os.path.join(tbzr_root, "scripts", "tbzrcommand.py"),
407
icon_resources = icon_resources,
408
other_resources = other_resources,
410
console_targets.append(tbzrcommand)
411
tbzrcommandw = tbzrcommand.copy()
412
tbzrcommandw["dest_base"]="tbzrcommandw"
413
gui_targets.append(tbzrcommandw)
415
# A utility to see python output from both C++ and Python based shell
417
tracer = dict(script=os.path.join(tbzr_root, "scripts", "tbzrtrace.py"))
418
console_targets.append(tracer)
420
# The C++ implemented shell extensions.
421
dist_dir = os.path.join(tbzr_root, "shellext", "build")
422
data_files.append(('', [os.path.join(dist_dir, 'tbzrshellext_x86.dll')]))
423
data_files.append(('', [os.path.join(dist_dir, 'tbzrshellext_x64.dll')]))
426
def get_qbzr_py2exe_info(includes, excludes, packages, data_files):
427
# PyQt4 itself still escapes the plugin detection code for some reason...
428
includes.append('PyQt4.QtCore')
429
includes.append('PyQt4.QtGui')
430
includes.append('sip') # extension module required for Qt.
431
packages.append('pygments') # colorizer for qbzr
432
packages.append('docutils') # html formatting
433
includes.append('win32event') # for qsubprocess stuff
434
# the qt binaries might not be on PATH...
435
# They seem to install to a place like C:\Python25\PyQt4\*
436
# Which is not the same as C:\Python25\Lib\site-packages\PyQt4
437
pyqt_dir = os.path.join(sys.prefix, "PyQt4")
438
pyqt_bin_dir = os.path.join(pyqt_dir, "bin")
439
if os.path.isdir(pyqt_bin_dir):
440
path = os.environ.get("PATH", "")
441
if pyqt_bin_dir.lower() not in [p.lower() for p in path.split(os.pathsep)]:
442
os.environ["PATH"] = path + os.pathsep + pyqt_bin_dir
443
# also add all imageformat plugins to distribution
444
# We will look in 2 places, dirname(PyQt4.__file__) and pyqt_dir
445
base_dirs_to_check = []
446
if os.path.isdir(pyqt_dir):
447
base_dirs_to_check.append(pyqt_dir)
453
pyqt4_base_dir = os.path.dirname(PyQt4.__file__)
454
if pyqt4_base_dir != pyqt_dir:
455
base_dirs_to_check.append(pyqt4_base_dir)
456
if not base_dirs_to_check:
457
log.warn("Can't find PyQt4 installation -> not including imageformat"
461
for base_dir in base_dirs_to_check:
462
plug_dir = os.path.join(base_dir, 'plugins', 'imageformats')
463
if os.path.isdir(plug_dir):
464
for fname in os.listdir(plug_dir):
465
# Include plugin dlls, but not debugging dlls
466
fullpath = os.path.join(plug_dir, fname)
467
if fname.endswith('.dll') and not fname.endswith('d4.dll'):
468
files.append(fullpath)
470
data_files.append(('imageformats', files))
472
log.warn('PyQt4 was found, but we could not find any imageformat'
473
' plugins. Are you sure your configuration is correct?')
476
def get_svn_py2exe_info(includes, excludes, packages):
477
packages.append('subvertpy')
478
packages.append('sqlite3')
481
def get_fastimport_py2exe_info(includes, excludes, packages):
482
# This is the python-fastimport package, not to be confused with the
483
# bzr-fastimport plugin.
484
packages.append('fastimport')
487
if 'bdist_wininst' in sys.argv:
490
for root, dirs, files in os.walk('doc'):
493
if (os.path.splitext(f)[1] in ('.html','.css','.png','.pdf')
494
or f == 'quick-start-summary.svg'):
495
r.append(os.path.join(root, f))
499
target = os.path.join('Doc\\Bazaar', relative)
501
target = 'Doc\\Bazaar'
502
docs.append((target, r))
505
# python's distutils-based win32 installer
506
ARGS = {'scripts': ['bzr', 'tools/win32/bzr-win32-bdist-postinstall.py'],
507
'ext_modules': ext_modules,
509
'data_files': find_docs(),
510
# for building pyrex extensions
511
'cmdclass': command_classes,
514
ARGS.update(META_INFO)
516
ARGS.update(PKG_DATA)
520
elif 'py2exe' in sys.argv:
525
# pick real bzr version
529
for i in bzrlib.version_info[:4]:
534
version_number.append(str(i))
535
version_str = '.'.join(version_number)
537
# An override to install_data used only by py2exe builds, which arranges
538
# to byte-compile any .py files in data_files (eg, our plugins)
539
# Necessary as we can't rely on the user having the relevant permissions
540
# to the "Program Files" directory to generate them on the fly.
541
class install_data_with_bytecompile(install_data):
543
from distutils.util import byte_compile
545
install_data.run(self)
547
py2exe = self.distribution.get_command_obj('py2exe', False)
548
# GZ 2010-04-19: Setup has py2exe.optimize as 2, but give plugins
549
# time before living with docstring stripping
551
compile_names = [f for f in self.outfiles if f.endswith('.py')]
552
# Round mtime to nearest even second so that installing on a FAT
553
# filesystem bytecode internal and script timestamps will match
554
for f in compile_names:
555
mtime = os.stat(f).st_mtime
556
remainder = mtime % 2
559
os.utime(f, (mtime, mtime))
560
byte_compile(compile_names,
562
force=self.force, prefix=self.install_dir,
563
dry_run=self.dry_run)
564
self.outfiles.extend([f + 'o' for f in compile_names])
565
# end of class install_data_with_bytecompile
567
target = py2exe.build_exe.Target(script = "bzr",
569
icon_resources = [(0,'bzr.ico')],
570
name = META_INFO['name'],
571
version = version_str,
572
description = META_INFO['description'],
573
author = META_INFO['author'],
574
copyright = "(c) Canonical Ltd, 2005-2010",
575
company_name = "Canonical Ltd.",
576
comments = META_INFO['description'],
578
gui_target = copy.copy(target)
579
gui_target.dest_base = "bzrw"
581
packages = BZRLIB['packages']
582
packages.remove('bzrlib')
583
packages = [i for i in packages if not i.startswith('bzrlib.plugins')]
585
for i in glob.glob('bzrlib\\*.py'):
586
module = i[:-3].replace('\\', '.')
587
if module.endswith('__init__'):
588
module = module[:-len('__init__')]
589
includes.append(module)
591
additional_packages = set()
592
if sys.version.startswith('2.4'):
593
# adding elementtree package
594
additional_packages.add('elementtree')
595
elif sys.version.startswith('2.6') or sys.version.startswith('2.5'):
596
additional_packages.add('xml.etree')
599
warnings.warn('Unknown Python version.\n'
600
'Please check setup.py script for compatibility.')
602
# Although we currently can't enforce it, we consider it an error for
603
# py2exe to report any files are "missing". Such modules we know aren't
604
# used should be listed here.
605
excludes = """Tkinter psyco ElementPath r_hmac
606
ImaginaryModule cElementTree elementtree.ElementTree
607
Crypto.PublicKey._fastmath
608
medusa medusa.filesys medusa.ftp_server
610
resource validate""".split()
613
# email package from std python library use lazy import,
614
# so we need to explicitly add all package
615
additional_packages.add('email')
616
# And it uses funky mappings to conver to 'Oldname' to 'newname'. As
617
# a result, packages like 'email.Parser' show as missing. Tell py2exe
620
for oldname in getattr(email, '_LOWERNAMES', []):
621
excludes.append("email." + oldname)
622
for oldname in getattr(email, '_MIMENAMES', []):
623
excludes.append("email.MIME" + oldname)
625
# text files for help topis
626
text_topics = glob.glob('bzrlib/help_topics/en/*.txt')
627
topics_files = [('lib/help_topics/en', text_topics)]
631
# XXX - should we consider having the concept of an 'official' build,
632
# which hard-codes the list of plugins, gets more upset if modules are
634
plugins = None # will be a set after plugin sniffing...
635
for root, dirs, files in os.walk('bzrlib/plugins'):
636
if root == 'bzrlib/plugins':
638
# We ship plugins as normal files on the file-system - however,
639
# the build process can cause *some* of these plugin files to end
640
# up in library.zip. Thus, we saw (eg) "plugins/svn/test" in
641
# library.zip, and then saw import errors related to that as the
642
# rest of the svn plugin wasn't. So we tell py2exe to leave the
643
# plugins out of the .zip file
644
excludes.extend(["bzrlib.plugins." + d for d in dirs])
647
# Throw away files we don't want packaged. Note that plugins may
648
# have data files with all sorts of extensions so we need to
649
# be conservative here about what we ditch.
650
ext = os.path.splitext(i)[1]
651
if ext.endswith('~') or ext in [".pyc", ".swp"]:
653
if i == '__init__.py' and root == 'bzrlib/plugins':
655
x.append(os.path.join(root, i))
657
target_dir = root[len('bzrlib/'):] # install to 'plugins/...'
658
plugins_files.append((target_dir, x))
659
# find modules for built-in plugins
660
import tools.package_mf
661
mf = tools.package_mf.CustomModuleFinder()
662
mf.run_package('bzrlib/plugins')
663
packs, mods = mf.get_result()
664
additional_packages.update(packs)
665
includes.extend(mods)
667
console_targets = [target,
668
'tools/win32/bzr_postinstall.py',
670
gui_targets = [gui_target]
671
data_files = topics_files + plugins_files
673
if 'qbzr' in plugins:
674
get_qbzr_py2exe_info(includes, excludes, packages, data_files)
677
get_svn_py2exe_info(includes, excludes, packages)
679
if 'fastimport' in plugins:
680
get_fastimport_py2exe_info(includes, excludes, packages)
682
if "TBZR" in os.environ:
683
# TORTOISE_OVERLAYS_MSI_WIN32 must be set to the location of the
684
# TortoiseOverlays MSI installer file. It is in the TSVN svn repo and
685
# can be downloaded from (username=guest, blank password):
686
# http://tortoisesvn.tigris.org/svn/tortoisesvn/TortoiseOverlays
687
# look for: version-1.0.4/bin/TortoiseOverlays-1.0.4.11886-win32.msi
688
# Ditto for TORTOISE_OVERLAYS_MSI_X64, pointing at *-x64.msi.
689
for needed in ('TORTOISE_OVERLAYS_MSI_WIN32',
690
'TORTOISE_OVERLAYS_MSI_X64'):
691
url = ('http://guest:@tortoisesvn.tigris.org/svn/tortoisesvn'
693
if not os.path.isfile(os.environ.get(needed, '<nofile>')):
695
"\nPlease set %s to the location of the relevant"
696
"\nTortoiseOverlays .msi installer file."
697
" The installers can be found at"
699
"\ncheck in the version-X.Y.Z/bin/ subdir" % (needed, url))
700
get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
701
gui_targets, data_files)
703
# print this warning to stderr as output is redirected, so it is seen
704
# at build time. Also to stdout so it appears in the log
705
for f in (sys.stderr, sys.stdout):
706
f.write("Skipping TBZR binaries - "
707
"please set TBZR to a directory to enable\n")
709
# MSWSOCK.dll is a system-specific library, which py2exe accidentally pulls
711
dll_excludes.extend(["MSWSOCK.dll",
716
options_list = {"py2exe": {"packages": packages + list(additional_packages),
717
"includes": includes,
718
"excludes": excludes,
719
"dll_excludes": dll_excludes,
720
"dist_dir": "win32_bzr.exe",
722
"custom_boot_script":
723
"tools/win32/py2exe_boot_common.py",
727
# We want the libaray.zip to have optimize = 2, but the exe to have
728
# optimize = 1, so that .py files that get compilied at run time
729
# (e.g. user installed plugins) dont have their doc strings removed.
730
class py2exe_no_oo_exe(py2exe.build_exe.py2exe):
731
def build_executable(self, *args, **kwargs):
733
py2exe.build_exe.py2exe.build_executable(self, *args, **kwargs)
736
if __name__ == '__main__':
737
command_classes['install_data'] = install_data_with_bytecompile
738
command_classes['py2exe'] = py2exe_no_oo_exe
739
setup(options=options_list,
740
console=console_targets,
742
zipfile='lib/library.zip',
743
data_files=data_files,
744
cmdclass=command_classes,
748
# ad-hoc for easy_install
750
if not 'bdist_egg' in sys.argv:
751
# generate and install bzr.1 only with plain install, not the
753
DATA_FILES = [('man/man1', ['bzr.1'])]
756
ARGS = {'scripts': ['bzr'],
757
'data_files': DATA_FILES,
758
'cmdclass': command_classes,
759
'ext_modules': ext_modules,
762
ARGS.update(META_INFO)
764
ARGS.update(PKG_DATA)
766
if __name__ == '__main__':
86
author_email='mbp@sourcefrog.net',
87
url='http://www.bazaar-ng.org/',
88
description='Friendly distributed version control system',
96
'bzrlib.util.elementtree',
97
'bzrlib.util.effbot.org',
98
'bzrlib.util.configobj',
101
cmdclass={'install_scripts': my_install_scripts, 'build': bzr_build},
102
data_files=[('man/man1', ['bzr.1'])],