1
1
#! /usr/bin/env python
3
# This is an installation script for bzr. Run it with
4
# './setup.py install', or
5
# './setup.py --help' for more options
3
"""Installation script for bzr.
5
'./setup.py install', or
6
'./setup.py --help' for more options
13
if sys.version_info < (2, 4):
14
sys.stderr.write("[ERROR] Not a supported Python version. Need 2.4+\n")
17
# NOTE: The directory containing setup.py, whether run by 'python setup.py' or
18
# './setup.py' or the equivalent with another path, should always be at the
19
# start of the path, so this should find the right one...
22
def get_long_description():
23
dirname = os.path.dirname(__file__)
24
readme = os.path.join(dirname, 'README')
25
f = open(readme, 'rb')
33
# META INFORMATION FOR SETUP
34
# see http://docs.python.org/dist/meta-data.html
37
'version': bzrlib.__version__,
38
'author': 'Canonical Ltd',
39
'author_email': 'bazaar@lists.canonical.com',
40
'url': 'http://www.bazaar-vcs.org/',
41
'description': 'Friendly distributed version control system',
42
'license': 'GNU GPL v2',
43
'download_url': 'http://bazaar-vcs.org/Download',
44
'long_description': get_long_description(),
46
'Development Status :: 6 - Mature',
47
'Environment :: Console',
48
'Intended Audience :: Developers',
49
'Intended Audience :: System Administrators',
50
'License :: OSI Approved :: GNU General Public License (GPL)',
51
'Operating System :: Microsoft :: Windows',
52
'Operating System :: OS Independent',
53
'Operating System :: POSIX',
54
'Programming Language :: Python',
55
'Programming Language :: C',
56
'Topic :: Software Development :: Version Control',
60
# The list of packages is automatically generated later. Add other things
61
# that are part of BZRLIB here.
64
PKG_DATA = {# install files from selftest suite
65
'package_data': {'bzrlib': ['doc/api/*.txt',
66
'tests/test_patches_data/*',
67
'help_topics/en/*.txt',
72
def get_bzrlib_packages():
73
"""Recurse through the bzrlib directory, and extract the package names"""
76
base_path = os.path.dirname(os.path.abspath(bzrlib.__file__))
77
for root, dirs, files in os.walk(base_path):
78
if '__init__.py' in files:
79
assert root.startswith(base_path)
80
# Get just the path below bzrlib
81
package_path = root[len(base_path):]
82
# Remove leading and trailing slashes
83
package_path = package_path.strip('\\/')
85
package_name = 'bzrlib'
87
package_name = ('bzrlib.' +
88
package_path.replace('/', '.').replace('\\', '.'))
89
packages.append(package_name)
90
return sorted(packages)
93
BZRLIB['packages'] = get_bzrlib_packages()
7
96
from distutils.core import setup
12
author_email='mbp@sourcefrog.net',
13
url='http://www.bazaar-ng.org/',
14
description='Friendly distributed version control system',
97
from distutils.command.install_scripts import install_scripts
98
from distutils.command.install_data import install_data
99
from distutils.command.build import build
101
###############################
102
# Overridden distutils actions
103
###############################
105
class my_install_scripts(install_scripts):
106
""" Customized install_scripts distutils action.
107
Create bzr.bat for win32.
110
install_scripts.run(self) # standard action
112
if sys.platform == "win32":
114
scripts_dir = os.path.join(sys.prefix, 'Scripts')
115
script_path = self._quoted_path(os.path.join(scripts_dir,
117
python_exe = self._quoted_path(sys.executable)
118
args = self._win_batch_args()
119
batch_str = "@%s %s %s" % (python_exe, script_path, args)
120
batch_path = os.path.join(self.install_dir, "bzr.bat")
121
f = file(batch_path, "w")
124
print "Created:", batch_path
126
print "ERROR: Unable to create %s: %s" % (batch_path, e)
128
def _quoted_path(self, path):
130
return '"' + path + '"'
134
def _win_batch_args(self):
135
from bzrlib.win32utils import winver
136
if winver == 'Windows NT':
139
return '%1 %2 %3 %4 %5 %6 %7 %8 %9'
140
#/class my_install_scripts
143
class bzr_build(build):
144
"""Customized build distutils action.
151
generate_docs.main(argv=["bzr", "man"])
154
########################
156
########################
158
command_classes = {'install_scripts': my_install_scripts,
160
from distutils import log
161
from distutils.errors import CCompilerError, DistutilsPlatformError
162
from distutils.extension import Extension
165
from Pyrex.Distutils import build_ext
168
# try to build the extension from the prior generated source.
170
print ("The python package 'Pyrex' is not available."
171
" If the .c files are available,")
172
print ("they will be built,"
173
" but modifying the .pyx files will not rebuild them.")
175
from distutils.command.build_ext import build_ext
180
class build_ext_if_possible(build_ext):
185
except DistutilsPlatformError, e:
187
log.warn('Extensions cannot be built, '
188
'will use the Python versions instead')
190
def build_extension(self, ext):
192
build_ext.build_extension(self, ext)
193
except CCompilerError:
194
log.warn('Building of "%s" extension failed, '
195
'will use the Python version instead' % (ext.name,))
198
# Override the build_ext if we have Pyrex available
199
command_classes['build_ext'] = build_ext_if_possible
200
unavailable_files = []
203
def add_pyrex_extension(module_name, **kwargs):
204
"""Add a pyrex module to build.
206
This will use Pyrex to auto-generate the .c file if it is available.
207
Otherwise it will fall back on the .c file. If the .c file is not
208
available, it will warn, and not add anything.
210
You can pass any extra options to Extension through kwargs. One example is
213
:param module_name: The python path to the module. This will be used to
214
determine the .pyx and .c files to use.
216
path = module_name.replace('.', '/')
217
pyrex_name = path + '.pyx'
220
ext_modules.append(Extension(module_name, [pyrex_name], **kwargs))
222
if not os.path.isfile(c_name):
223
unavailable_files.append(c_name)
225
ext_modules.append(Extension(module_name, [c_name], **kwargs))
228
add_pyrex_extension('bzrlib._btree_serializer_c')
229
add_pyrex_extension('bzrlib._knit_load_data_c')
230
if sys.platform == 'win32':
231
add_pyrex_extension('bzrlib._dirstate_helpers_c',
234
# pyrex uses the macro WIN32 to detect the platform, even though it should
235
# be using something like _WIN32 or MS_WINDOWS, oh well, we can give it the
237
add_pyrex_extension('bzrlib._walkdirs_win32',
238
define_macros=[('WIN32', None)])
240
add_pyrex_extension('bzrlib._dirstate_helpers_c')
241
add_pyrex_extension('bzrlib._readdir_pyx')
242
ext_modules.append(Extension('bzrlib._patiencediff_c', ['bzrlib/_patiencediff_c.c']))
245
if unavailable_files:
246
print 'C extension(s) not found:'
247
print ' %s' % ('\n '.join(unavailable_files),)
248
print 'The python versions will be used instead.'
252
def get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
254
packages.append('tbzrcommands')
256
# ModuleFinder can't handle runtime changes to __path__, but
257
# win32com uses them. Hook this in so win32com.shell is found.
260
import cPickle as pickle
261
for p in win32com.__path__[1:]:
262
modulefinder.AddPackagePath("win32com", p)
263
for extra in ["win32com.shell"]:
265
m = sys.modules[extra]
266
for p in m.__path__[1:]:
267
modulefinder.AddPackagePath(extra, p)
269
# TBZR points to the TBZR directory
270
tbzr_root = os.environ["TBZR"]
272
# Ensure tbzrlib itself is on sys.path
273
sys.path.append(tbzr_root)
275
# Ensure our COM "entry-point" is on sys.path
276
sys.path.append(os.path.join(tbzr_root, "shellext", "python"))
278
packages.append("tbzrlib")
280
# collect up our icons.
282
ico_root = os.path.join(tbzr_root, 'tbzrlib', 'resources')
283
icos = [] # list of (path_root, relative_ico_path)
284
# First always bzr's icon and its in the root of the bzr tree.
285
icos.append(('', 'bzr.ico'))
286
for root, dirs, files in os.walk(ico_root):
287
icos.extend([(ico_root, os.path.join(root, f)[len(ico_root)+1:])
288
for f in files if f.endswith('.ico')])
289
# allocate an icon ID for each file and the full path to the ico
290
icon_resources = [(rid, os.path.join(ico_dir, ico_name))
291
for rid, (ico_dir, ico_name) in enumerate(icos)]
292
# create a string resource with the mapping. Might as well save the
293
# runtime some effort and write a pickle.
294
# Runtime expects unicode objects with forward-slash seps.
295
fse = sys.getfilesystemencoding()
296
map_items = [(f.replace('\\', '/').decode(fse), rid)
297
for rid, (_, f) in enumerate(icos)]
298
ico_map = dict(map_items)
299
# Create a new resource type of 'ICON_MAP', and use ID=1
300
other_resources = [ ("ICON_MAP", 1, pickle.dumps(ico_map))]
302
excludes.extend("""pywin pywin.dialogs pywin.dialogs.list
303
win32ui crawler.Crawler""".split())
307
create_exe = False, # we only want a .dll
309
com_targets.append(tbzr)
311
# tbzrcache executables - a "console" version for debugging and a
312
# GUI version that is generally used.
314
script = os.path.join(tbzr_root, "Scripts", "tbzrcache.py"),
315
icon_resources = icon_resources,
316
other_resources = other_resources,
318
console_targets.append(tbzrcache)
320
# Make a windows version which is the same except for the base name.
321
tbzrcachew = tbzrcache.copy()
322
tbzrcachew["dest_base"]="tbzrcachew"
323
gui_targets.append(tbzrcachew)
325
# ditto for the tbzrcommand tool
327
script = os.path.join(tbzr_root, "Scripts", "tbzrcommand.py"),
328
icon_resources = [(0,'bzr.ico')],
330
console_targets.append(tbzrcommand)
331
tbzrcommandw = tbzrcommand.copy()
332
tbzrcommandw["dest_base"]="tbzrcommandw"
333
gui_targets.append(tbzrcommandw)
337
script = os.path.join(tbzr_root, "Scripts", "tbzrtest.py"),
339
console_targets.append(tbzrtest)
341
# A utility to see python output from the shell extension - this will
342
# die when we get a c++ extension
343
# any .py file from pywin32's win32 lib will do (other than
344
# win32traceutil itself that is)
346
win32_lib_dir = os.path.dirname(winerror.__file__)
347
tracer = dict(script = os.path.join(win32_lib_dir, "win32traceutil.py"),
348
dest_base="tbzr_tracer")
349
console_targets.append(tracer)
352
def get_qbzr_py2exe_info(includes, excludes, packages):
353
# PyQt4 itself still escapes the plugin detection code for some reason...
354
packages.append('PyQt4')
355
excludes.append('PyQt4.elementtree.ElementTree')
356
includes.append('sip') # extension module required for Qt.
357
packages.append('pygments') # colorizer for qbzr
358
packages.append('docutils') # html formatting
359
# but we can avoid many Qt4 Dlls.
361
"""QtAssistantClient4.dll QtCLucene4.dll QtDesigner4.dll
362
QtHelp4.dll QtNetwork4.dll QtOpenGL4.dll QtScript4.dll
363
QtSql4.dll QtTest4.dll QtWebKit4.dll QtXml4.dll
364
qscintilla2.dll""".split())
365
# the qt binaries might not be on PATH...
366
qt_dir = os.path.join(sys.prefix, "PyQt4", "bin")
367
path = os.environ.get("PATH","")
368
if qt_dir.lower() not in [p.lower() for p in path.split(os.pathsep)]:
369
os.environ["PATH"] = path + os.pathsep + qt_dir
372
if 'bdist_wininst' in sys.argv:
375
for root, dirs, files in os.walk('doc'):
378
if (os.path.splitext(f)[1] in ('.html','.css','.png','.pdf')
379
or f == 'quick-start-summary.svg'):
380
r.append(os.path.join(root, f))
384
target = os.path.join('Doc\\Bazaar', relative)
386
target = 'Doc\\Bazaar'
387
docs.append((target, r))
390
# python's distutils-based win32 installer
391
ARGS = {'scripts': ['bzr', 'tools/win32/bzr-win32-bdist-postinstall.py'],
392
'ext_modules': ext_modules,
394
'data_files': find_docs(),
395
# for building pyrex extensions
396
'cmdclass': {'build_ext': build_ext_if_possible},
399
ARGS.update(META_INFO)
401
ARGS.update(PKG_DATA)
405
elif 'py2exe' in sys.argv:
410
# pick real bzr version
414
for i in bzrlib.version_info[:4]:
419
version_number.append(str(i))
420
version_str = '.'.join(version_number)
422
# An override to install_data used only by py2exe builds, which arranges
423
# to byte-compile any .py files in data_files (eg, our plugins)
424
# Necessary as we can't rely on the user having the relevant permissions
425
# to the "Program Files" directory to generate them on the fly.
426
class install_data_with_bytecompile(install_data):
428
from distutils.util import byte_compile
430
install_data.run(self)
432
py2exe = self.distribution.get_command_obj('py2exe', False)
433
optimize = py2exe.optimize
434
compile_names = [f for f in self.outfiles if f.endswith('.py')]
435
byte_compile(compile_names,
437
force=self.force, prefix=self.install_dir,
438
dry_run=self.dry_run)
443
self.outfiles.extend([f + suffix for f in compile_names])
444
# end of class install_data_with_bytecompile
446
target = py2exe.build_exe.Target(script = "bzr",
448
icon_resources = [(0,'bzr.ico')],
449
name = META_INFO['name'],
450
version = version_str,
451
description = META_INFO['description'],
452
author = META_INFO['author'],
453
copyright = "(c) Canonical Ltd, 2005-2007",
454
company_name = "Canonical Ltd.",
455
comments = META_INFO['description'],
458
packages = BZRLIB['packages']
459
packages.remove('bzrlib')
460
packages = [i for i in packages if not i.startswith('bzrlib.plugins')]
462
for i in glob.glob('bzrlib\\*.py'):
463
module = i[:-3].replace('\\', '.')
464
if module.endswith('__init__'):
465
module = module[:-len('__init__')]
466
includes.append(module)
468
additional_packages = set()
469
if sys.version.startswith('2.4'):
470
# adding elementtree package
471
additional_packages.add('elementtree')
472
elif sys.version.startswith('2.5'):
473
additional_packages.add('xml.etree')
476
warnings.warn('Unknown Python version.\n'
477
'Please check setup.py script for compatibility.')
479
# Although we currently can't enforce it, we consider it an error for
480
# py2exe to report any files are "missing". Such modules we know aren't
481
# used should be listed here.
482
excludes = """Tkinter psyco ElementPath r_hmac
483
ImaginaryModule cElementTree elementtree.ElementTree
484
Crypto.PublicKey._fastmath
485
medusa medusa.filesys medusa.ftp_server
486
tools tools.doc_generate
487
resource validate""".split()
490
# email package from std python library use lazy import,
491
# so we need to explicitly add all package
492
additional_packages.add('email')
493
# And it uses funky mappings to conver to 'Oldname' to 'newname'. As
494
# a result, packages like 'email.Parser' show as missing. Tell py2exe
497
for oldname in getattr(email, '_LOWERNAMES', []):
498
excludes.append("email." + oldname)
499
for oldname in getattr(email, '_MIMENAMES', []):
500
excludes.append("email.MIME" + oldname)
502
# text files for help topis
503
text_topics = glob.glob('bzrlib/help_topics/en/*.txt')
504
topics_files = [('lib/help_topics/en', text_topics)]
508
# XXX - should we consider having the concept of an 'official' build,
509
# which hard-codes the list of plugins, gets more upset if modules are
511
plugins = None # will be a set after plugin sniffing...
512
for root, dirs, files in os.walk('bzrlib/plugins'):
513
if root == 'bzrlib/plugins':
517
if os.path.splitext(i)[1] not in [".py", ".pyd", ".dll", ".mo"]:
519
if i == '__init__.py' and root == 'bzrlib/plugins':
521
x.append(os.path.join(root, i))
523
target_dir = root[len('bzrlib/'):] # install to 'plugins/...'
524
plugins_files.append((target_dir, x))
525
# find modules for built-in plugins
526
import tools.package_mf
527
mf = tools.package_mf.CustomModuleFinder()
528
mf.run_package('bzrlib/plugins')
529
packs, mods = mf.get_result()
530
additional_packages.update(packs)
531
includes.extend(mods)
533
console_targets = [target,
534
'tools/win32/bzr_postinstall.py',
539
if 'qbzr' in plugins:
540
get_qbzr_py2exe_info(includes, excludes, packages)
542
if "TBZR" in os.environ:
543
# TORTOISE_OVERLAYS_MSI_WIN32 must be set to the location of the
544
# TortoiseOverlays MSI installer file. It is in the TSVN svn repo and
545
# can be downloaded from (username=guest, blank password):
546
# http://tortoisesvn.tigris.org/svn/tortoisesvn/TortoiseOverlays/version-1.0.4/bin/TortoiseOverlays-1.0.4.11886-win32.msi
547
if not os.path.isfile(os.environ.get('TORTOISE_OVERLAYS_MSI_WIN32',
549
raise RuntimeError("Please set TORTOISE_OVERLAYS_MSI_WIN32 to the"
550
" location of the Win32 TortoiseOverlays .msi"
552
get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
555
# print this warning to stderr as output is redirected, so it is seen
556
# at build time. Also to stdout so it appears in the log
557
for f in (sys.stderr, sys.stdout):
559
"Skipping TBZR binaries - please set TBZR to a directory to enable"
561
# MSWSOCK.dll is a system-specific library, which py2exe accidentally pulls
563
dll_excludes.append("MSWSOCK.dll")
564
options_list = {"py2exe": {"packages": packages + list(additional_packages),
565
"includes": includes,
566
"excludes": excludes,
567
"dll_excludes": dll_excludes,
568
"dist_dir": "win32_bzr.exe",
573
setup(options=options_list,
574
console=console_targets,
576
com_server=com_targets,
577
zipfile='lib/library.zip',
578
data_files=topics_files + plugins_files,
579
cmdclass={'install_data': install_data_with_bytecompile},
583
# ad-hoc for easy_install
585
if not 'bdist_egg' in sys.argv:
586
# generate and install bzr.1 only with plain install, not easy_install one
587
DATA_FILES = [('man/man1', ['bzr.1'])]
590
ARGS = {'scripts': ['bzr'],
591
'data_files': DATA_FILES,
592
'cmdclass': command_classes,
593
'ext_modules': ext_modules,
596
ARGS.update(META_INFO)
598
ARGS.update(PKG_DATA)