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._dirstate_helpers_c')
230
add_pyrex_extension('bzrlib._knit_load_data_c')
231
add_pyrex_extension('bzrlib._readdir_pyx')
232
if sys.platform == 'win32':
233
# pyrex uses the macro WIN32 to detect the platform, even though it should
234
# be using something like _WIN32 or MS_WINDOWS, oh well, we can give it the
236
add_pyrex_extension('bzrlib._walkdirs_win32',
237
define_macros=[('WIN32', None)])
238
ext_modules.append(Extension('bzrlib._patiencediff_c', ['bzrlib/_patiencediff_c.c']))
241
if unavailable_files:
242
print 'C extension(s) not found:'
243
print ' %s' % ('\n '.join(unavailable_files),)
244
print 'The python versions will be used instead.'
248
def get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
250
packages.append('tbzrcommands')
252
# ModuleFinder can't handle runtime changes to __path__, but
253
# win32com uses them. Hook this in so win32com.shell is found.
256
for p in win32com.__path__[1:]:
257
modulefinder.AddPackagePath("win32com", p)
258
for extra in ["win32com.shell"]:
260
m = sys.modules[extra]
261
for p in m.__path__[1:]:
262
modulefinder.AddPackagePath(extra, p)
264
# TBZR points to the TBZR directory
265
tbzr_root = os.environ["TBZR"]
267
# Ensure tbzrlib itself is on sys.path
268
sys.path.append(tbzr_root)
270
# Ensure our COM "entry-point" is on sys.path
271
sys.path.append(os.path.join(tbzr_root, "shellext", "python"))
273
packages.append("tbzrlib")
274
excludes.extend("""pywin pywin.dialogs pywin.dialogs.list
275
win32ui crawler.Crawler""".split())
279
create_exe = False, # we only want a .dll
281
com_targets.append(tbzr)
283
# tbzrcache executables - a "console" version for debugging and a
284
# GUI version that is generally used.
286
script = os.path.join(tbzr_root, "Scripts", "tbzrcache.py"),
287
icon_resources = [(0,'bzr.ico')],
289
console_targets.append(tbzrcache)
291
# Make a windows version which is the same except for the base name.
292
tbzrcachew = tbzrcache.copy()
293
tbzrcachew["dest_base"]="tbzrcachew"
294
gui_targets.append(tbzrcachew)
296
# ditto for the tbzrcommand tool
298
script = os.path.join(tbzr_root, "Scripts", "tbzrcommand.py"),
299
icon_resources = [(0,'bzr.ico')],
301
console_targets.append(tbzrcommand)
302
tbzrcommandw = tbzrcommand.copy()
303
tbzrcommandw["dest_base"]="tbzrcommandw"
304
gui_targets.append(tbzrcommandw)
308
script = os.path.join(tbzr_root, "Scripts", "tbzrtest.py"),
310
console_targets.append(tbzrtest)
312
# A utility to see python output from the shell extension - this will
313
# die when we get a c++ extension
314
# any .py file from pywin32's win32 lib will do (other than
315
# win32traceutil itself that is)
317
win32_lib_dir = os.path.dirname(winerror.__file__)
318
tracer = dict(script = os.path.join(win32_lib_dir, "win32traceutil.py"),
319
dest_base="tbzr_tracer")
320
console_targets.append(tracer)
323
def get_qbzr_py2exe_info(includes, excludes, packages):
324
# PyQt4 itself still escapes the plugin detection code for some reason...
325
packages.append('PyQt4')
326
excludes.append('PyQt4.elementtree.ElementTree')
327
includes.append('sip') # extension module required for Qt.
328
packages.append('pygments') # colorizer for qbzr
329
# but we can avoid many Qt4 Dlls.
331
"""QtAssistantClient4.dll QtCLucene4.dll QtDesigner4.dll
332
QtHelp4.dll QtNetwork4.dll QtOpenGL4.dll QtScript4.dll
333
QtSql4.dll QtTest4.dll QtWebKit4.dll QtXml4.dll
334
qscintilla2.dll""".split())
335
# the qt binaries might not be on PATH...
336
qt_dir = os.path.join(sys.prefix, "PyQt4", "bin")
337
path = os.environ.get("PATH","")
338
if qt_dir.lower() not in [p.lower() for p in path.split(os.pathsep)]:
339
os.environ["PATH"] = path + os.pathsep + qt_dir
342
if 'bdist_wininst' in sys.argv:
345
for root, dirs, files in os.walk('doc'):
348
if (os.path.splitext(f)[1] in ('.html','.css','.png','.pdf')
349
or f == 'quick-start-summary.svg'):
350
r.append(os.path.join(root, f))
354
target = os.path.join('Doc\\Bazaar', relative)
356
target = 'Doc\\Bazaar'
357
docs.append((target, r))
360
# python's distutils-based win32 installer
361
ARGS = {'scripts': ['bzr', 'tools/win32/bzr-win32-bdist-postinstall.py'],
362
'ext_modules': ext_modules,
364
'data_files': find_docs(),
365
# for building pyrex extensions
366
'cmdclass': {'build_ext': build_ext_if_possible},
369
ARGS.update(META_INFO)
371
ARGS.update(PKG_DATA)
375
elif 'py2exe' in sys.argv:
380
# pick real bzr version
384
for i in bzrlib.version_info[:4]:
389
version_number.append(str(i))
390
version_str = '.'.join(version_number)
392
# An override to install_data used only by py2exe builds, which arranges
393
# to byte-compile any .py files in data_files (eg, our plugins)
394
# Necessary as we can't rely on the user having the relevant permissions
395
# to the "Program Files" directory to generate them on the fly.
396
class install_data_with_bytecompile(install_data):
398
from distutils.util import byte_compile
400
install_data.run(self)
402
py2exe = self.distribution.get_command_obj('py2exe', False)
403
optimize = py2exe.optimize
404
compile_names = [f for f in self.outfiles if f.endswith('.py')]
405
byte_compile(compile_names,
407
force=self.force, prefix=self.install_dir,
408
dry_run=self.dry_run)
413
self.outfiles.extend([f + suffix for f in compile_names])
414
# end of class install_data_with_bytecompile
416
target = py2exe.build_exe.Target(script = "bzr",
418
icon_resources = [(0,'bzr.ico')],
419
name = META_INFO['name'],
420
version = version_str,
421
description = META_INFO['description'],
422
author = META_INFO['author'],
423
copyright = "(c) Canonical Ltd, 2005-2007",
424
company_name = "Canonical Ltd.",
425
comments = META_INFO['description'],
428
packages = BZRLIB['packages']
429
packages.remove('bzrlib')
430
packages = [i for i in packages if not i.startswith('bzrlib.plugins')]
432
for i in glob.glob('bzrlib\\*.py'):
433
module = i[:-3].replace('\\', '.')
434
if module.endswith('__init__'):
435
module = module[:-len('__init__')]
436
includes.append(module)
438
additional_packages = set()
439
if sys.version.startswith('2.4'):
440
# adding elementtree package
441
additional_packages.add('elementtree')
442
elif sys.version.startswith('2.5'):
443
additional_packages.add('xml.etree')
446
warnings.warn('Unknown Python version.\n'
447
'Please check setup.py script for compatibility.')
449
# Although we currently can't enforce it, we consider it an error for
450
# py2exe to report any files are "missing". Such modules we know aren't
451
# used should be listed here.
452
excludes = """Tkinter psyco ElementPath r_hmac
453
ImaginaryModule cElementTree elementtree.ElementTree
454
Crypto.PublicKey._fastmath
455
medusa medusa.filesys medusa.ftp_server
456
tools tools.doc_generate
457
resource validate""".split()
460
# email package from std python library use lazy import,
461
# so we need to explicitly add all package
462
additional_packages.add('email')
463
# And it uses funky mappings to conver to 'Oldname' to 'newname'. As
464
# a result, packages like 'email.Parser' show as missing. Tell py2exe
467
for oldname in getattr(email, '_LOWERNAMES', []):
468
excludes.append("email." + oldname)
469
for oldname in getattr(email, '_MIMENAMES', []):
470
excludes.append("email.MIME" + oldname)
472
# text files for help topis
473
text_topics = glob.glob('bzrlib/help_topics/en/*.txt')
474
topics_files = [('lib/help_topics/en', text_topics)]
478
# XXX - should we consider having the concept of an 'official' build,
479
# which hard-codes the list of plugins, gets more upset if modules are
481
plugins = None # will be a set after plugin sniffing...
482
for root, dirs, files in os.walk('bzrlib/plugins'):
483
if root == 'bzrlib/plugins':
487
if os.path.splitext(i)[1] not in [".py", ".pyd", ".dll"]:
489
if i == '__init__.py' and root == 'bzrlib/plugins':
491
x.append(os.path.join(root, i))
493
target_dir = root[len('bzrlib/'):] # install to 'plugins/...'
494
plugins_files.append((target_dir, x))
495
# find modules for built-in plugins
496
import tools.package_mf
497
mf = tools.package_mf.CustomModuleFinder()
498
mf.run_package('bzrlib/plugins')
499
packs, mods = mf.get_result()
500
additional_packages.update(packs)
501
includes.extend(mods)
503
console_targets = [target,
504
'tools/win32/bzr_postinstall.py',
509
if 'qbzr' in plugins:
510
get_qbzr_py2exe_info(includes, excludes, packages)
512
if "TBZR" in os.environ:
513
# TORTOISE_OVERLAYS_MSI_WIN32 must be set to the location of the
514
# TortoiseOverlays MSI installer file. It is in the TSVN svn repo and
515
# can be downloaded from (username=guest, blank password):
516
# http://tortoisesvn.tigris.org/svn/tortoisesvn/TortoiseOverlays/version-1.0.4/bin/TortoiseOverlays-1.0.4.11886-win32.msi
517
if not os.path.isfile(os.environ.get('TORTOISE_OVERLAYS_MSI_WIN32',
519
raise RuntimeError("Please set TORTOISE_OVERLAYS_MSI_WIN32 to the"
520
" location of the Win32 TortoiseOverlays .msi"
522
get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
525
# print this warning to stderr as output is redirected, so it is seen
526
# at build time. Also to stdout so it appears in the log
527
for f in (sys.stderr, sys.stdout):
529
"Skipping TBZR binaries - please set TBZR to a directory to enable"
531
# MSWSOCK.dll is a system-specific library, which py2exe accidentally pulls
533
dll_excludes.append("MSWSOCK.dll")
534
options_list = {"py2exe": {"packages": packages + list(additional_packages),
535
"includes": includes,
536
"excludes": excludes,
537
"dll_excludes": dll_excludes,
538
"dist_dir": "win32_bzr.exe",
543
setup(options=options_list,
544
console=console_targets,
546
com_server=com_targets,
547
zipfile='lib/library.zip',
548
data_files=topics_files + plugins_files,
549
cmdclass={'install_data': install_data_with_bytecompile},
553
# ad-hoc for easy_install
555
if not 'bdist_egg' in sys.argv:
556
# generate and install bzr.1 only with plain install, not easy_install one
557
DATA_FILES = [('man/man1', ['bzr.1'])]
560
ARGS = {'scripts': ['bzr'],
561
'data_files': DATA_FILES,
562
'cmdclass': command_classes,
563
'ext_modules': ext_modules,
566
ARGS.update(META_INFO)
568
ARGS.update(PKG_DATA)