112
17
Create bzr.bat for win32.
115
23
install_scripts.run(self) # standard action
117
25
if sys.platform == "win32":
119
scripts_dir = os.path.join(sys.prefix, 'Scripts')
120
script_path = self._quoted_path(os.path.join(scripts_dir,
122
python_exe = self._quoted_path(sys.executable)
123
args = self._win_batch_args()
124
batch_str = "@%s %s %s" % (python_exe, script_path, args)
125
batch_path = os.path.join(self.install_dir, "bzr.bat")
27
scripts_dir = self.install_dir
28
script_path = os.path.join(scripts_dir, "bzr")
29
batch_str = "@%s %s %%*\n" % (sys.executable, script_path)
30
batch_path = script_path + ".bat"
126
31
f = file(batch_path, "w")
127
32
f.write(batch_str)
129
print("Created: %s" % batch_path)
131
e = sys.exc_info()[1]
132
print("ERROR: Unable to create %s: %s" % (batch_path, e))
134
def _quoted_path(self, path):
136
return '"' + path + '"'
140
def _win_batch_args(self):
141
from bzrlib.win32utils import winver
142
if winver == 'Windows NT':
145
return '%1 %2 %3 %4 %5 %6 %7 %8 %9'
146
#/class my_install_scripts
149
class bzr_build(build):
150
"""Customized build distutils action.
157
from tools import generate_docs
158
generate_docs.main(argv=["bzr", "man"])
34
print "Created:", batch_path
36
print "ERROR: Unable to create %s: %s" % (batch_path, e)
161
39
########################
163
41
########################
165
command_classes = {'install_scripts': my_install_scripts,
167
from distutils import log
168
from distutils.errors import CCompilerError, DistutilsPlatformError
169
from distutils.extension import Extension
173
from Pyrex.Distutils import build_ext
174
from Pyrex.Compiler.Version import version as pyrex_version
176
print("No Pyrex, trying Cython...")
177
from Cython.Distutils import build_ext
178
from Cython.Compiler.Version import version as pyrex_version
181
# try to build the extension from the prior generated source.
183
print("The python package 'Pyrex' is not available."
184
" If the .c files are available,")
185
print("they will be built,"
186
" but modifying the .pyx files will not rebuild them.")
188
from distutils.command.build_ext import build_ext
191
pyrex_version_info = tuple(map(int, pyrex_version.split('.')))
194
class build_ext_if_possible(build_ext):
196
user_options = build_ext.user_options + [
197
('allow-python-fallback', None,
198
"When an extension cannot be built, allow falling"
199
" back to the pure-python implementation.")
202
def initialize_options(self):
203
build_ext.initialize_options(self)
204
self.allow_python_fallback = False
209
except DistutilsPlatformError:
210
e = sys.exc_info()[1]
211
if not self.allow_python_fallback:
212
log.warn('\n Cannot build extensions.\n'
213
' Use "build_ext --allow-python-fallback" to use'
214
' slower python implementations instead.\n')
217
log.warn('\n Extensions cannot be built.\n'
218
' Using the slower Python implementations instead.\n')
220
def build_extension(self, ext):
222
build_ext.build_extension(self, ext)
223
except CCompilerError:
224
if not self.allow_python_fallback:
225
log.warn('\n Cannot build extension "%s".\n'
226
' Use "build_ext --allow-python-fallback" to use'
227
' slower python implementations instead.\n'
230
log.warn('\n Building of "%s" extension failed.\n'
231
' Using the slower Python implementation instead.'
235
# Override the build_ext if we have Pyrex available
236
command_classes['build_ext'] = build_ext_if_possible
237
unavailable_files = []
240
def add_pyrex_extension(module_name, libraries=None, extra_source=[]):
241
"""Add a pyrex module to build.
243
This will use Pyrex to auto-generate the .c file if it is available.
244
Otherwise it will fall back on the .c file. If the .c file is not
245
available, it will warn, and not add anything.
247
You can pass any extra options to Extension through kwargs. One example is
250
:param module_name: The python path to the module. This will be used to
251
determine the .pyx and .c files to use.
253
path = module_name.replace('.', '/')
254
pyrex_name = path + '.pyx'
257
if sys.platform == 'win32':
258
# pyrex uses the macro WIN32 to detect the platform, even though it
259
# should be using something like _WIN32 or MS_WINDOWS, oh well, we can
260
# give it the right value.
261
define_macros.append(('WIN32', None))
263
source = [pyrex_name]
265
if not os.path.isfile(c_name):
266
unavailable_files.append(c_name)
270
source.extend(extra_source)
271
ext_modules.append(Extension(module_name, source,
272
define_macros=define_macros, libraries=libraries))
275
add_pyrex_extension('bzrlib._annotator_pyx')
276
add_pyrex_extension('bzrlib._bencode_pyx')
277
add_pyrex_extension('bzrlib._chunks_to_lines_pyx')
278
add_pyrex_extension('bzrlib._groupcompress_pyx',
279
extra_source=['bzrlib/diff-delta.c'])
280
add_pyrex_extension('bzrlib._knit_load_data_pyx')
281
add_pyrex_extension('bzrlib._known_graph_pyx')
282
add_pyrex_extension('bzrlib._rio_pyx')
283
if sys.platform == 'win32':
284
add_pyrex_extension('bzrlib._dirstate_helpers_pyx',
285
libraries=['Ws2_32'])
286
add_pyrex_extension('bzrlib._walkdirs_win32')
288
if have_pyrex and pyrex_version_info[:3] == (0,9,4):
289
# Pyrex 0.9.4.1 fails to compile this extension correctly
290
# The code it generates re-uses a "local" pointer and
291
# calls "PY_DECREF" after having set it to NULL. (It mixes PY_XDECREF
292
# which is NULL safe with PY_DECREF which is not.)
293
# <https://bugs.edge.launchpad.net/bzr/+bug/449372>
294
# <https://bugs.edge.launchpad.net/bzr/+bug/276868>
295
print('Cannot build extension "bzrlib._dirstate_helpers_pyx" using')
296
print('your version of pyrex "%s". Please upgrade your pyrex' % (
298
print('install. For now, the non-compiled (python) version will')
299
print('be used instead.')
301
add_pyrex_extension('bzrlib._dirstate_helpers_pyx')
302
add_pyrex_extension('bzrlib._readdir_pyx')
303
add_pyrex_extension('bzrlib._chk_map_pyx')
304
ext_modules.append(Extension('bzrlib._patiencediff_c',
305
['bzrlib/_patiencediff_c.c']))
306
if have_pyrex and pyrex_version_info < (0, 9, 6, 3):
308
print('Your Pyrex/Cython version %s is too old to build the simple_set' % (
310
print('and static_tuple extensions.')
311
print('Please upgrade to at least Pyrex 0.9.6.3')
313
# TODO: Should this be a fatal error?
315
# We only need 0.9.6.3 to build _simple_set_pyx, but static_tuple depends
317
add_pyrex_extension('bzrlib._simple_set_pyx')
318
ext_modules.append(Extension('bzrlib._static_tuple_c',
319
['bzrlib/_static_tuple_c.c']))
320
add_pyrex_extension('bzrlib._btree_serializer_pyx')
323
if unavailable_files:
324
print('C extension(s) not found:')
325
print(' %s' % ('\n '.join(unavailable_files),))
326
print('The python versions will be used instead.')
330
def get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
331
gui_targets, data_files):
332
packages.append('tbzrcommands')
334
# ModuleFinder can't handle runtime changes to __path__, but
335
# win32com uses them. Hook this in so win32com.shell is found.
338
import cPickle as pickle
339
for p in win32com.__path__[1:]:
340
modulefinder.AddPackagePath("win32com", p)
341
for extra in ["win32com.shell"]:
343
m = sys.modules[extra]
344
for p in m.__path__[1:]:
345
modulefinder.AddPackagePath(extra, p)
347
# TBZR points to the TBZR directory
348
tbzr_root = os.environ["TBZR"]
350
# Ensure tbzrlib itself is on sys.path
351
sys.path.append(tbzr_root)
353
packages.append("tbzrlib")
355
# collect up our icons.
357
ico_root = os.path.join(tbzr_root, 'tbzrlib', 'resources')
358
icos = [] # list of (path_root, relative_ico_path)
359
# First always bzr's icon and its in the root of the bzr tree.
360
icos.append(('', 'bzr.ico'))
361
for root, dirs, files in os.walk(ico_root):
362
icos.extend([(ico_root, os.path.join(root, f)[len(ico_root)+1:])
363
for f in files if f.endswith('.ico')])
364
# allocate an icon ID for each file and the full path to the ico
365
icon_resources = [(rid, os.path.join(ico_dir, ico_name))
366
for rid, (ico_dir, ico_name) in enumerate(icos)]
367
# create a string resource with the mapping. Might as well save the
368
# runtime some effort and write a pickle.
369
# Runtime expects unicode objects with forward-slash seps.
370
fse = sys.getfilesystemencoding()
371
map_items = [(f.replace('\\', '/').decode(fse), rid)
372
for rid, (_, f) in enumerate(icos)]
373
ico_map = dict(map_items)
374
# Create a new resource type of 'ICON_MAP', and use ID=1
375
other_resources = [ ("ICON_MAP", 1, pickle.dumps(ico_map))]
377
excludes.extend("""pywin pywin.dialogs pywin.dialogs.list
378
win32ui crawler.Crawler""".split())
380
# tbzrcache executables - a "console" version for debugging and a
381
# GUI version that is generally used.
383
script = os.path.join(tbzr_root, "scripts", "tbzrcache.py"),
384
icon_resources = icon_resources,
385
other_resources = other_resources,
387
console_targets.append(tbzrcache)
389
# Make a windows version which is the same except for the base name.
390
tbzrcachew = tbzrcache.copy()
391
tbzrcachew["dest_base"]="tbzrcachew"
392
gui_targets.append(tbzrcachew)
394
# ditto for the tbzrcommand tool
396
script = os.path.join(tbzr_root, "scripts", "tbzrcommand.py"),
397
icon_resources = [(0,'bzr.ico')],
399
console_targets.append(tbzrcommand)
400
tbzrcommandw = tbzrcommand.copy()
401
tbzrcommandw["dest_base"]="tbzrcommandw"
402
gui_targets.append(tbzrcommandw)
404
# A utility to see python output from both C++ and Python based shell
406
tracer = dict(script=os.path.join(tbzr_root, "scripts", "tbzrtrace.py"))
407
console_targets.append(tracer)
409
# The C++ implemented shell extensions.
410
dist_dir = os.path.join(tbzr_root, "shellext", "build")
411
data_files.append(('', [os.path.join(dist_dir, 'tbzrshellext_x86.dll')]))
412
data_files.append(('', [os.path.join(dist_dir, 'tbzrshellext_x64.dll')]))
415
def get_qbzr_py2exe_info(includes, excludes, packages, data_files):
416
# PyQt4 itself still escapes the plugin detection code for some reason...
417
includes.append('PyQt4.QtCore')
418
includes.append('PyQt4.QtGui')
419
includes.append('sip') # extension module required for Qt.
420
packages.append('pygments') # colorizer for qbzr
421
packages.append('docutils') # html formatting
422
includes.append('win32event') # for qsubprocess stuff
423
# the qt binaries might not be on PATH...
424
# They seem to install to a place like C:\Python25\PyQt4\*
425
# Which is not the same as C:\Python25\Lib\site-packages\PyQt4
426
pyqt_dir = os.path.join(sys.prefix, "PyQt4")
427
pyqt_bin_dir = os.path.join(pyqt_dir, "bin")
428
if os.path.isdir(pyqt_bin_dir):
429
path = os.environ.get("PATH", "")
430
if pyqt_bin_dir.lower() not in [p.lower() for p in path.split(os.pathsep)]:
431
os.environ["PATH"] = path + os.pathsep + pyqt_bin_dir
432
# also add all imageformat plugins to distribution
433
# We will look in 2 places, dirname(PyQt4.__file__) and pyqt_dir
434
base_dirs_to_check = []
435
if os.path.isdir(pyqt_dir):
436
base_dirs_to_check.append(pyqt_dir)
442
pyqt4_base_dir = os.path.dirname(PyQt4.__file__)
443
if pyqt4_base_dir != pyqt_dir:
444
base_dirs_to_check.append(pyqt4_base_dir)
445
if not base_dirs_to_check:
446
log.warn("Can't find PyQt4 installation -> not including imageformat"
450
for base_dir in base_dirs_to_check:
451
plug_dir = os.path.join(base_dir, 'plugins', 'imageformats')
452
if os.path.isdir(plug_dir):
453
for fname in os.listdir(plug_dir):
454
# Include plugin dlls, but not debugging dlls
455
fullpath = os.path.join(plug_dir, fname)
456
if fname.endswith('.dll') and not fname.endswith('d4.dll'):
457
files.append(fullpath)
459
data_files.append(('imageformats', files))
461
log.warn('PyQt4 was found, but we could not find any imageformat'
462
' plugins. Are you sure your configuration is correct?')
465
def get_svn_py2exe_info(includes, excludes, packages):
466
packages.append('subvertpy')
467
packages.append('sqlite3')
470
if 'bdist_wininst' in sys.argv:
473
for root, dirs, files in os.walk('doc'):
476
if (os.path.splitext(f)[1] in ('.html','.css','.png','.pdf')
477
or f == 'quick-start-summary.svg'):
478
r.append(os.path.join(root, f))
482
target = os.path.join('Doc\\Bazaar', relative)
484
target = 'Doc\\Bazaar'
485
docs.append((target, r))
488
# python's distutils-based win32 installer
489
ARGS = {'scripts': ['bzr', 'tools/win32/bzr-win32-bdist-postinstall.py'],
490
'ext_modules': ext_modules,
492
'data_files': find_docs(),
493
# for building pyrex extensions
494
'cmdclass': {'build_ext': build_ext_if_possible},
497
ARGS.update(META_INFO)
499
ARGS.update(PKG_DATA)
503
elif 'py2exe' in sys.argv:
508
# pick real bzr version
512
for i in bzrlib.version_info[:4]:
517
version_number.append(str(i))
518
version_str = '.'.join(version_number)
520
# An override to install_data used only by py2exe builds, which arranges
521
# to byte-compile any .py files in data_files (eg, our plugins)
522
# Necessary as we can't rely on the user having the relevant permissions
523
# to the "Program Files" directory to generate them on the fly.
524
class install_data_with_bytecompile(install_data):
526
from distutils.util import byte_compile
528
install_data.run(self)
530
py2exe = self.distribution.get_command_obj('py2exe', False)
531
# GZ 2010-04-19: Setup has py2exe.optimize as 2, but give plugins
532
# time before living with docstring stripping
534
compile_names = [f for f in self.outfiles if f.endswith('.py')]
535
# Round mtime to nearest even second so that installing on a FAT
536
# filesystem bytecode internal and script timestamps will match
537
for f in compile_names:
538
mtime = os.stat(f).st_mtime
539
remainder = mtime % 2
542
os.utime(f, (mtime, mtime))
543
byte_compile(compile_names,
545
force=self.force, prefix=self.install_dir,
546
dry_run=self.dry_run)
547
self.outfiles.extend([f + 'o' for f in compile_names])
548
# end of class install_data_with_bytecompile
550
target = py2exe.build_exe.Target(script = "bzr",
552
icon_resources = [(0,'bzr.ico')],
553
name = META_INFO['name'],
554
version = version_str,
555
description = META_INFO['description'],
556
author = META_INFO['author'],
557
copyright = "(c) Canonical Ltd, 2005-2010",
558
company_name = "Canonical Ltd.",
559
comments = META_INFO['description'],
561
gui_target = copy.copy(target)
562
gui_target.dest_base = "bzrw"
564
packages = BZRLIB['packages']
565
packages.remove('bzrlib')
566
packages = [i for i in packages if not i.startswith('bzrlib.plugins')]
568
for i in glob.glob('bzrlib\\*.py'):
569
module = i[:-3].replace('\\', '.')
570
if module.endswith('__init__'):
571
module = module[:-len('__init__')]
572
includes.append(module)
574
additional_packages = set()
575
if sys.version.startswith('2.4'):
576
# adding elementtree package
577
additional_packages.add('elementtree')
578
elif sys.version.startswith('2.6') or sys.version.startswith('2.5'):
579
additional_packages.add('xml.etree')
582
warnings.warn('Unknown Python version.\n'
583
'Please check setup.py script for compatibility.')
585
# Although we currently can't enforce it, we consider it an error for
586
# py2exe to report any files are "missing". Such modules we know aren't
587
# used should be listed here.
588
excludes = """Tkinter psyco ElementPath r_hmac
589
ImaginaryModule cElementTree elementtree.ElementTree
590
Crypto.PublicKey._fastmath
591
medusa medusa.filesys medusa.ftp_server
593
resource validate""".split()
596
# email package from std python library use lazy import,
597
# so we need to explicitly add all package
598
additional_packages.add('email')
599
# And it uses funky mappings to conver to 'Oldname' to 'newname'. As
600
# a result, packages like 'email.Parser' show as missing. Tell py2exe
603
for oldname in getattr(email, '_LOWERNAMES', []):
604
excludes.append("email." + oldname)
605
for oldname in getattr(email, '_MIMENAMES', []):
606
excludes.append("email.MIME" + oldname)
608
# text files for help topis
609
text_topics = glob.glob('bzrlib/help_topics/en/*.txt')
610
topics_files = [('lib/help_topics/en', text_topics)]
614
# XXX - should we consider having the concept of an 'official' build,
615
# which hard-codes the list of plugins, gets more upset if modules are
617
plugins = None # will be a set after plugin sniffing...
618
for root, dirs, files in os.walk('bzrlib/plugins'):
619
if root == 'bzrlib/plugins':
621
# We ship plugins as normal files on the file-system - however,
622
# the build process can cause *some* of these plugin files to end
623
# up in library.zip. Thus, we saw (eg) "plugins/svn/test" in
624
# library.zip, and then saw import errors related to that as the
625
# rest of the svn plugin wasn't. So we tell py2exe to leave the
626
# plugins out of the .zip file
627
excludes.extend(["bzrlib.plugins." + d for d in dirs])
630
# Throw away files we don't want packaged. Note that plugins may
631
# have data files with all sorts of extensions so we need to
632
# be conservative here about what we ditch.
633
ext = os.path.splitext(i)[1]
634
if ext.endswith('~') or ext in [".pyc", ".swp"]:
636
if i == '__init__.py' and root == 'bzrlib/plugins':
638
x.append(os.path.join(root, i))
640
target_dir = root[len('bzrlib/'):] # install to 'plugins/...'
641
plugins_files.append((target_dir, x))
642
# find modules for built-in plugins
643
import tools.package_mf
644
mf = tools.package_mf.CustomModuleFinder()
645
mf.run_package('bzrlib/plugins')
646
packs, mods = mf.get_result()
647
additional_packages.update(packs)
648
includes.extend(mods)
650
console_targets = [target,
651
'tools/win32/bzr_postinstall.py',
653
gui_targets = [gui_target]
654
data_files = topics_files + plugins_files
656
if 'qbzr' in plugins:
657
get_qbzr_py2exe_info(includes, excludes, packages, data_files)
660
get_svn_py2exe_info(includes, excludes, packages)
662
if "TBZR" in os.environ:
663
# TORTOISE_OVERLAYS_MSI_WIN32 must be set to the location of the
664
# TortoiseOverlays MSI installer file. It is in the TSVN svn repo and
665
# can be downloaded from (username=guest, blank password):
666
# http://tortoisesvn.tigris.org/svn/tortoisesvn/TortoiseOverlays
667
# look for: version-1.0.4/bin/TortoiseOverlays-1.0.4.11886-win32.msi
668
# Ditto for TORTOISE_OVERLAYS_MSI_X64, pointing at *-x64.msi.
669
for needed in ('TORTOISE_OVERLAYS_MSI_WIN32',
670
'TORTOISE_OVERLAYS_MSI_X64'):
671
url = ('http://guest:@tortoisesvn.tigris.org/svn/tortoisesvn'
673
if not os.path.isfile(os.environ.get(needed, '<nofile>')):
675
"\nPlease set %s to the location of the relevant"
676
"\nTortoiseOverlays .msi installer file."
677
" The installers can be found at"
679
"\ncheck in the version-X.Y.Z/bin/ subdir" % (needed, url))
680
get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
681
gui_targets, data_files)
683
# print this warning to stderr as output is redirected, so it is seen
684
# at build time. Also to stdout so it appears in the log
685
for f in (sys.stderr, sys.stdout):
686
f.write("Skipping TBZR binaries - "
687
"please set TBZR to a directory to enable\n")
689
# MSWSOCK.dll is a system-specific library, which py2exe accidentally pulls
691
dll_excludes.extend(["MSWSOCK.dll", "MSVCP60.dll", "powrprof.dll"])
692
options_list = {"py2exe": {"packages": packages + list(additional_packages),
693
"includes": includes,
694
"excludes": excludes,
695
"dll_excludes": dll_excludes,
696
"dist_dir": "win32_bzr.exe",
698
"custom_boot_script":
699
"tools/win32/py2exe_boot_common.py",
703
# We want the libaray.zip to have optimize = 2, but the exe to have
704
# optimize = 1, so that .py files that get compilied at run time
705
# (e.g. user installed plugins) dont have their doc strings removed.
706
class py2exe_no_oo_exe(py2exe.build_exe.py2exe):
707
def build_executable(self, *args, **kwargs):
709
py2exe.build_exe.py2exe.build_executable(self, *args, **kwargs)
712
if __name__ == '__main__':
713
setup(options=options_list,
714
console=console_targets,
716
zipfile='lib/library.zip',
717
data_files=data_files,
718
cmdclass={'install_data': install_data_with_bytecompile,
719
'py2exe': py2exe_no_oo_exe},
723
# ad-hoc for easy_install
725
if not 'bdist_egg' in sys.argv:
726
# generate and install bzr.1 only with plain install, not the
728
DATA_FILES = [('man/man1', ['bzr.1'])]
730
if sys.platform != 'win32':
731
# see https://wiki.kubuntu.org/Apport/DeveloperHowTo
733
# checking the paths and hardcoding the check for root is a bit gross,
734
# but I don't see a cleaner way to find out the locations in a way
735
# that's going to align with the hardcoded paths in apport.
736
if os.geteuid() == 0:
738
('/usr/share/apport/package-hooks',
739
['apport/source_bzr.py']),
740
('/etc/apport/crashdb.conf.d/',
741
['apport/bzr-crashdb.conf']),]
744
ARGS = {'scripts': ['bzr'],
745
'data_files': DATA_FILES,
746
'cmdclass': command_classes,
747
'ext_modules': ext_modules,
750
ARGS.update(META_INFO)
752
ARGS.update(PKG_DATA)
754
if __name__ == '__main__':
46
author_email='mbp@sourcefrog.net',
47
url='http://www.bazaar-ng.org/',
48
description='Friendly distributed version control system',
56
'bzrlib.util.elementtree',
57
'bzrlib.util.effbot.org',
58
'bzrlib.util.configobj',
61
cmdclass={'install_scripts': my_install_scripts},