3
# This is an installation script for bzr. Run it with
4
# './setup.py install', or
5
# './setup.py --help' for more options
7
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',
20
'bzrlib.util.elementtree',
21
'bzrlib.util.effbot.org',
1
#! /usr/bin/env python3
3
"""Installation script for brz.
5
'./setup.py install', or
6
'./setup.py --help' for more options
15
if sys.version_info < (3, 5):
16
sys.stderr.write("[ERROR] Not a supported Python version. Need 3.5+\n")
22
except ImportError as e:
23
sys.stderr.write("[ERROR] Please install setuptools (%s)\n" % e)
27
# NOTE: The directory containing setup.py, whether run by 'python setup.py' or
28
# './setup.py' or the equivalent with another path, should always be at the
29
# start of the path, so this should find the right one...
32
def get_long_description():
33
dirname = os.path.dirname(__file__)
34
readme = os.path.join(dirname, 'README.rst')
35
with open(readme, 'r') as f:
40
# META INFORMATION FOR SETUP
41
# see http://docs.python.org/dist/meta-data.html
44
'version': breezy.__version__,
45
'maintainer': 'Breezy Developers',
46
'maintainer_email': 'team@breezy-vcs.org',
47
'url': 'https://www.breezy-vcs.org/',
48
'description': 'Friendly distributed version control system',
49
'license': 'GNU GPL v2',
50
'download_url': 'https://launchpad.net/brz/+download',
51
'long_description': get_long_description(),
53
'Development Status :: 6 - Mature',
54
'Environment :: Console',
55
'Intended Audience :: Developers',
56
'Intended Audience :: System Administrators',
57
'License :: OSI Approved :: GNU General Public License (GPL)',
58
'Operating System :: Microsoft :: Windows',
59
'Operating System :: OS Independent',
60
'Operating System :: POSIX',
61
'Programming Language :: Python',
62
'Programming Language :: C',
63
'Topic :: Software Development :: Version Control',
68
# Technically, Breezy works without these two dependencies too. But there's
69
# no way to enable them by default and let users opt out.
70
'dulwich>=0.19.12;python_version>="3.5"',
71
'dulwich<0.20,>=0.19.12;python_version<"3.0"',
76
'launchpad': ['launchpadlib>=1.6.3'],
77
'workspace': ['pyinotify'],
81
'testtools<=2.4.0;python_version<"3.0"',
86
# The list of packages is automatically generated later. Add other things
87
# that are part of BREEZY here.
91
# install files from selftest suite
92
'package_data': {'breezy': ['doc/api/*.txt',
93
'tests/test_patches_data/*',
94
'help_topics/en/*.txt',
95
'tests/ssl_certs/ca.crt',
96
'tests/ssl_certs/server_without_pass.key',
97
'tests/ssl_certs/server_with_pass.key',
98
'tests/ssl_certs/server.crt',
102
for filepath in glob.glob("breezy/locale/*/LC_MESSAGES/*.mo"):
103
langfile = filepath[len("breezy/locale/"):]
104
targetpath = os.path.dirname(os.path.join("share/locale", langfile))
105
I18N_FILES.append((targetpath, [filepath]))
107
def get_breezy_packages():
108
"""Recurse through the breezy directory, and extract the package names"""
111
base_path = os.path.dirname(os.path.abspath(breezy.__file__))
112
for root, dirs, files in os.walk(base_path):
113
if '__init__.py' in files:
114
assert root.startswith(base_path)
115
# Get just the path below breezy
116
package_path = root[len(base_path):]
117
# Remove leading and trailing slashes
118
package_path = package_path.strip('\\/')
120
package_name = 'breezy'
124
package_path.replace('/', '.').replace('\\', '.'))
125
packages.append(package_name)
126
return sorted(packages)
129
BREEZY['packages'] = get_breezy_packages()
132
from setuptools import setup
133
from distutils.version import LooseVersion
134
from distutils.command.install_scripts import install_scripts
135
from distutils.command.install_data import install_data
136
from distutils.command.build import build
138
###############################
139
# Overridden distutils actions
140
###############################
142
class my_install_scripts(install_scripts):
143
""" Customized install_scripts distutils action.
144
Create brz.bat for win32.
147
install_scripts.run(self) # standard action
149
if sys.platform == "win32":
151
scripts_dir = os.path.join(sys.prefix, 'Scripts')
152
script_path = self._quoted_path(os.path.join(scripts_dir,
154
python_exe = self._quoted_path(sys.executable)
155
batch_str = "@%s %s %%*" % (python_exe, script_path)
156
batch_path = os.path.join(self.install_dir, "brz.bat")
157
with open(batch_path, "w") as f:
159
print(("Created: %s" % batch_path))
161
e = sys.exc_info()[1]
162
print(("ERROR: Unable to create %s: %s" % (batch_path, e)))
164
def _quoted_path(self, path):
166
return '"' + path + '"'
169
#/class my_install_scripts
172
class bzr_build(build):
173
"""Customized build distutils action.
177
sub_commands = build.sub_commands + [
178
('build_mo', lambda _: True),
184
from tools import generate_docs
185
generate_docs.main(argv=["brz", "man"])
188
########################
190
########################
192
from breezy.bzr_distutils import build_mo
194
command_classes = {'install_scripts': my_install_scripts,
196
'build_mo': build_mo,
198
from distutils import log
199
from distutils.errors import CCompilerError, DistutilsPlatformError
200
from distutils.extension import Extension
203
from Cython.Distutils import build_ext
204
from Cython.Compiler.Version import version as cython_version
207
# try to build the extension from the prior generated source.
209
print("The python package 'Cython' is not available."
210
" If the .c files are available,")
211
print("they will be built,"
212
" but modifying the .pyx files will not rebuild them.")
214
from distutils.command.build_ext import build_ext
216
minimum_cython_version = '0.29'
217
cython_version_info = LooseVersion(cython_version)
218
if cython_version_info < LooseVersion(minimum_cython_version):
219
print("Version of Cython is too old. "
220
"Current is %s, need at least %s."
221
% (cython_version, minimum_cython_version))
222
print("If the .c files are available, they will be built,"
223
" but modifying the .pyx files will not rebuild them.")
229
class build_ext_if_possible(build_ext):
231
user_options = build_ext.user_options + [
232
('allow-python-fallback', None,
233
"When an extension cannot be built, allow falling"
234
" back to the pure-python implementation.")
237
def initialize_options(self):
238
build_ext.initialize_options(self)
239
self.allow_python_fallback = False
244
except DistutilsPlatformError:
245
e = sys.exc_info()[1]
246
if not self.allow_python_fallback:
247
log.warn('\n Cannot build extensions.\n'
248
' Use "build_ext --allow-python-fallback" to use'
249
' slower python implementations instead.\n')
252
log.warn('\n Extensions cannot be built.\n'
253
' Using the slower Python implementations instead.\n')
255
def build_extension(self, ext):
257
build_ext.build_extension(self, ext)
258
except CCompilerError:
259
if not self.allow_python_fallback:
260
log.warn('\n Cannot build extension "%s".\n'
261
' Use "build_ext --allow-python-fallback" to use'
262
' slower python implementations instead.\n'
265
log.warn('\n Building of "%s" extension failed.\n'
266
' Using the slower Python implementation instead.'
270
# Override the build_ext if we have Cython available
271
command_classes['build_ext'] = build_ext_if_possible
272
unavailable_files = []
275
def add_cython_extension(module_name, libraries=None, extra_source=[]):
276
"""Add a cython module to build.
278
This will use Cython to auto-generate the .c file if it is available.
279
Otherwise it will fall back on the .c file. If the .c file is not
280
available, it will warn, and not add anything.
282
You can pass any extra options to Extension through kwargs. One example is
285
:param module_name: The python path to the module. This will be used to
286
determine the .pyx and .c files to use.
288
path = module_name.replace('.', '/')
289
cython_name = path + '.pyx'
292
if sys.platform == 'win32':
293
# cython uses the macro WIN32 to detect the platform, even though it
294
# should be using something like _WIN32 or MS_WINDOWS, oh well, we can
295
# give it the right value.
296
define_macros.append(('WIN32', None))
298
source = [cython_name]
300
if not os.path.isfile(c_name):
301
unavailable_files.append(c_name)
305
source.extend(extra_source)
306
include_dirs = ['breezy']
309
module_name, source, define_macros=define_macros,
310
libraries=libraries, include_dirs=include_dirs))
313
add_cython_extension('breezy._simple_set_pyx')
314
ext_modules.append(Extension('breezy._static_tuple_c',
315
['breezy/_static_tuple_c.c']))
316
add_cython_extension('breezy._annotator_pyx')
317
add_cython_extension('breezy._bencode_pyx')
318
add_cython_extension('breezy._chunks_to_lines_pyx')
319
add_cython_extension('breezy.bzr._groupcompress_pyx',
320
extra_source=['breezy/bzr/diff-delta.c'])
321
add_cython_extension('breezy.bzr._knit_load_data_pyx')
322
add_cython_extension('breezy._known_graph_pyx')
323
add_cython_extension('breezy._rio_pyx')
324
if sys.platform == 'win32':
325
add_cython_extension('breezy.bzr._dirstate_helpers_pyx',
326
libraries=['Ws2_32'])
327
add_cython_extension('breezy._walkdirs_win32')
329
add_cython_extension('breezy.bzr._dirstate_helpers_pyx')
330
add_cython_extension('breezy._readdir_pyx')
331
add_cython_extension('breezy.bzr._chk_map_pyx')
332
add_cython_extension('breezy.bzr._btree_serializer_pyx')
335
if unavailable_files:
336
print('C extension(s) not found:')
337
print((' %s' % ('\n '.join(unavailable_files),)))
338
print('The python versions will be used instead.')
342
def get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
343
gui_targets, data_files):
344
packages.append('tbzrcommands')
346
# ModuleFinder can't handle runtime changes to __path__, but
347
# win32com uses them. Hook this in so win32com.shell is found.
350
import cPickle as pickle
351
for p in win32com.__path__[1:]:
352
modulefinder.AddPackagePath("win32com", p)
353
for extra in ["win32com.shell"]:
355
m = sys.modules[extra]
356
for p in m.__path__[1:]:
357
modulefinder.AddPackagePath(extra, p)
359
# TBZR points to the TBZR directory
360
tbzr_root = os.environ["TBZR"]
362
# Ensure tbreezy itself is on sys.path
363
sys.path.append(tbzr_root)
365
packages.append("tbreezy")
367
# collect up our icons.
369
ico_root = os.path.join(tbzr_root, 'tbreezy', 'resources')
370
icos = [] # list of (path_root, relative_ico_path)
371
# First always brz's icon and its in the root of the brz tree.
372
icos.append(('', 'brz.ico'))
373
for root, dirs, files in os.walk(ico_root):
374
icos.extend([(ico_root, os.path.join(root, f)[len(ico_root) + 1:])
375
for f in files if f.endswith('.ico')])
376
# allocate an icon ID for each file and the full path to the ico
377
icon_resources = [(rid, os.path.join(ico_dir, ico_name))
378
for rid, (ico_dir, ico_name) in enumerate(icos)]
379
# create a string resource with the mapping. Might as well save the
380
# runtime some effort and write a pickle.
381
# Runtime expects unicode objects with forward-slash seps.
382
fse = sys.getfilesystemencoding()
383
map_items = [(f.replace('\\', '/').decode(fse), rid)
384
for rid, (_, f) in enumerate(icos)]
385
ico_map = dict(map_items)
386
# Create a new resource type of 'ICON_MAP', and use ID=1
387
other_resources = [("ICON_MAP", 1, pickle.dumps(ico_map))]
389
excludes.extend("""pywin pywin.dialogs pywin.dialogs.list
390
win32ui crawler.Crawler""".split())
392
# tbzrcache executables - a "console" version for debugging and a
393
# GUI version that is generally used.
395
script=os.path.join(tbzr_root, "scripts", "tbzrcache.py"),
396
icon_resources=icon_resources,
397
other_resources=other_resources,
399
console_targets.append(tbzrcache)
401
# Make a windows version which is the same except for the base name.
402
tbzrcachew = tbzrcache.copy()
403
tbzrcachew["dest_base"] = "tbzrcachew"
404
gui_targets.append(tbzrcachew)
406
# ditto for the tbzrcommand tool
408
script=os.path.join(tbzr_root, "scripts", "tbzrcommand.py"),
409
icon_resources=icon_resources,
410
other_resources=other_resources,
412
console_targets.append(tbzrcommand)
413
tbzrcommandw = tbzrcommand.copy()
414
tbzrcommandw["dest_base"] = "tbzrcommandw"
415
gui_targets.append(tbzrcommandw)
417
# A utility to see python output from both C++ and Python based shell
419
tracer = dict(script=os.path.join(tbzr_root, "scripts", "tbzrtrace.py"))
420
console_targets.append(tracer)
422
# The C++ implemented shell extensions.
423
dist_dir = os.path.join(tbzr_root, "shellext", "build")
424
data_files.append(('', [os.path.join(dist_dir, 'tbzrshellext_x86.dll')]))
425
data_files.append(('', [os.path.join(dist_dir, 'tbzrshellext_x64.dll')]))
428
def get_qbzr_py2exe_info(includes, excludes, packages, data_files):
429
# PyQt4 itself still escapes the plugin detection code for some reason...
430
includes.append('PyQt4.QtCore')
431
includes.append('PyQt4.QtGui')
432
includes.append('PyQt4.QtTest')
433
includes.append('sip') # extension module required for Qt.
434
packages.append('pygments') # colorizer for qbzr
435
packages.append('docutils') # html formatting
436
includes.append('win32event') # for qsubprocess stuff
437
# the qt binaries might not be on PATH...
438
# They seem to install to a place like C:\Python25\PyQt4\*
439
# Which is not the same as C:\Python25\Lib\site-packages\PyQt4
440
pyqt_dir = os.path.join(sys.prefix, "PyQt4")
441
pyqt_bin_dir = os.path.join(pyqt_dir, "bin")
442
if os.path.isdir(pyqt_bin_dir):
443
path = os.environ.get("PATH", "")
444
if pyqt_bin_dir.lower() not in [p.lower() for p in path.split(os.pathsep)]:
445
os.environ["PATH"] = path + os.pathsep + pyqt_bin_dir
446
# also add all imageformat plugins to distribution
447
# We will look in 2 places, dirname(PyQt4.__file__) and pyqt_dir
448
base_dirs_to_check = []
449
if os.path.isdir(pyqt_dir):
450
base_dirs_to_check.append(pyqt_dir)
456
pyqt4_base_dir = os.path.dirname(PyQt4.__file__)
457
if pyqt4_base_dir != pyqt_dir:
458
base_dirs_to_check.append(pyqt4_base_dir)
459
if not base_dirs_to_check:
460
log.warn("Can't find PyQt4 installation -> not including imageformat"
464
for base_dir in base_dirs_to_check:
465
plug_dir = os.path.join(base_dir, 'plugins', 'imageformats')
466
if os.path.isdir(plug_dir):
467
for fname in os.listdir(plug_dir):
468
# Include plugin dlls, but not debugging dlls
469
fullpath = os.path.join(plug_dir, fname)
470
if fname.endswith('.dll') and not fname.endswith('d4.dll'):
471
files.append(fullpath)
473
data_files.append(('imageformats', files))
475
log.warn('PyQt4 was found, but we could not find any imageformat'
476
' plugins. Are you sure your configuration is correct?')
479
def get_svn_py2exe_info(includes, excludes, packages):
480
packages.append('subvertpy')
481
packages.append('sqlite3')
484
def get_git_py2exe_info(includes, excludes, packages):
485
packages.append('dulwich')
488
def get_fastimport_py2exe_info(includes, excludes, packages):
489
# This is the python-fastimport package, not to be confused with the
490
# brz-fastimport plugin.
491
packages.append('fastimport')
494
if 'bdist_wininst' in sys.argv:
497
for root, dirs, files in os.walk('doc'):
500
if (os.path.splitext(f)[1] in ('.html', '.css', '.png', '.pdf')
501
or f == 'quick-start-summary.svg'):
502
r.append(os.path.join(root, f))
506
target = os.path.join('Doc\\Breezy', relative)
508
target = 'Doc\\Breezy'
509
docs.append((target, r))
512
# python's distutils-based win32 installer
513
ARGS = {'scripts': ['brz', 'tools/win32/brz-win32-bdist-postinstall.py'],
514
'ext_modules': ext_modules,
516
'data_files': find_docs(),
517
# for building cython extensions
518
'cmdclass': command_classes,
521
ARGS.update(META_INFO)
523
PKG_DATA['package_data']['breezy'].append('locale/*/LC_MESSAGES/*.mo')
524
ARGS.update(PKG_DATA)
528
elif 'py2exe' in sys.argv:
532
# pick real brz version
536
for i in breezy.version_info[:4]:
541
version_number.append(str(i))
542
version_str = '.'.join(version_number)
544
# An override to install_data used only by py2exe builds, which arranges
545
# to byte-compile any .py files in data_files (eg, our plugins)
546
# Necessary as we can't rely on the user having the relevant permissions
547
# to the "Program Files" directory to generate them on the fly.
548
class install_data_with_bytecompile(install_data):
550
from distutils.util import byte_compile
552
install_data.run(self)
554
py2exe = self.distribution.get_command_obj('py2exe', False)
555
# GZ 2010-04-19: Setup has py2exe.optimize as 2, but give plugins
556
# time before living with docstring stripping
558
compile_names = [f for f in self.outfiles if f.endswith('.py')]
559
# Round mtime to nearest even second so that installing on a FAT
560
# filesystem bytecode internal and script timestamps will match
561
for f in compile_names:
562
mtime = os.stat(f).st_mtime
563
remainder = mtime % 2
566
os.utime(f, (mtime, mtime))
567
byte_compile(compile_names,
569
force=self.force, prefix=self.install_dir,
570
dry_run=self.dry_run)
571
self.outfiles.extend([f + 'o' for f in compile_names])
572
# end of class install_data_with_bytecompile
574
target = py2exe.build_exe.Target(
577
icon_resources=[(0, 'brz.ico')],
578
name=META_INFO['name'],
580
description=META_INFO['description'],
581
author=META_INFO['author'],
582
copyright="(c) Canonical Ltd, 2005-2010",
583
company_name="Canonical Ltd.",
584
comments=META_INFO['description'],
586
gui_target = copy.copy(target)
587
gui_target.dest_base = "bzrw"
589
packages = BREEZY['packages']
590
packages.remove('breezy')
591
packages = [i for i in packages if not i.startswith('breezy.plugins')]
593
for i in glob.glob('breezy\\*.py'):
594
module = i[:-3].replace('\\', '.')
595
if module.endswith('__init__'):
596
module = module[:-len('__init__')]
597
includes.append(module)
599
additional_packages = set()
601
# Although we currently can't enforce it, we consider it an error for
602
# py2exe to report any files are "missing". Such modules we know aren't
603
# used should be listed here.
604
excludes = """Tkinter psyco ElementPath r_hmac
605
ImaginaryModule cElementTree elementtree.ElementTree
606
Crypto.PublicKey._fastmath
608
resource validate""".split()
611
# email package from std python library use lazy import,
612
# so we need to explicitly add all package
613
additional_packages.add('email')
614
# And it uses funky mappings to conver to 'Oldname' to 'newname'. As
615
# a result, packages like 'email.Parser' show as missing. Tell py2exe
618
for oldname in getattr(email, '_LOWERNAMES', []):
619
excludes.append("email." + oldname)
620
for oldname in getattr(email, '_MIMENAMES', []):
621
excludes.append("email.MIME" + oldname)
623
# text files for help topis
624
text_topics = glob.glob('breezy/help_topics/en/*.txt')
625
topics_files = [('lib/help_topics/en', text_topics)]
629
# XXX - should we consider having the concept of an 'official' build,
630
# which hard-codes the list of plugins, gets more upset if modules are
632
plugins = None # will be a set after plugin sniffing...
633
for root, dirs, files in os.walk('breezy/plugins'):
634
if root == 'breezy/plugins':
636
# We ship plugins as normal files on the file-system - however,
637
# the build process can cause *some* of these plugin files to end
638
# up in library.zip. Thus, we saw (eg) "plugins/svn/test" in
639
# library.zip, and then saw import errors related to that as the
640
# rest of the svn plugin wasn't. So we tell py2exe to leave the
641
# plugins out of the .zip file
642
excludes.extend(["breezy.plugins." + d for d in dirs])
645
# Throw away files we don't want packaged. Note that plugins may
646
# have data files with all sorts of extensions so we need to
647
# be conservative here about what we ditch.
648
ext = os.path.splitext(i)[1]
649
if ext.endswith('~') or ext in [".pyc", ".swp"]:
651
if i == '__init__.py' and root == 'breezy/plugins':
653
x.append(os.path.join(root, i))
655
target_dir = root[len('breezy/'):] # install to 'plugins/...'
656
plugins_files.append((target_dir, x))
657
# find modules for built-in plugins
658
import tools.package_mf
659
mf = tools.package_mf.CustomModuleFinder()
660
mf.run_package('breezy/plugins')
661
packs, mods = mf.get_result()
662
additional_packages.update(packs)
663
includes.extend(mods)
665
console_targets = [target,
666
'tools/win32/bzr_postinstall.py',
668
gui_targets = [gui_target]
669
data_files = topics_files + plugins_files + I18N_FILES
671
if 'qbzr' in plugins:
672
get_qbzr_py2exe_info(includes, excludes, packages, data_files)
675
get_svn_py2exe_info(includes, excludes, packages)
678
get_git_py2exe_info(includes, excludes, packages)
680
if 'fastimport' in plugins:
681
get_fastimport_py2exe_info(includes, excludes, packages)
683
if "TBZR" in os.environ:
684
# TORTOISE_OVERLAYS_MSI_WIN32 must be set to the location of the
685
# TortoiseOverlays MSI installer file. It is in the TSVN svn repo and
686
# can be downloaded from (username=guest, blank password):
687
# http://tortoisesvn.tigris.org/svn/tortoisesvn/TortoiseOverlays
688
# look for: version-1.0.4/bin/TortoiseOverlays-1.0.4.11886-win32.msi
689
# Ditto for TORTOISE_OVERLAYS_MSI_X64, pointing at *-x64.msi.
690
for needed in ('TORTOISE_OVERLAYS_MSI_WIN32',
691
'TORTOISE_OVERLAYS_MSI_X64'):
692
url = ('http://guest:@tortoisesvn.tigris.org/svn/tortoisesvn'
694
if not os.path.isfile(os.environ.get(needed, '<nofile>')):
696
"\nPlease set %s to the location of the relevant"
697
"\nTortoiseOverlays .msi installer file."
698
" The installers can be found at"
700
"\ncheck in the version-X.Y.Z/bin/ subdir" % (needed, url))
701
get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
702
gui_targets, data_files)
704
# print this warning to stderr as output is redirected, so it is seen
705
# at build time. Also to stdout so it appears in the log
706
for f in (sys.stderr, sys.stdout):
707
f.write("Skipping TBZR binaries - "
708
"please set TBZR to a directory to enable\n")
710
# MSWSOCK.dll is a system-specific library, which py2exe accidentally pulls
712
dll_excludes.extend(["MSWSOCK.dll",
717
options_list = {"py2exe": {"packages": packages + list(additional_packages),
718
"includes": includes,
719
"excludes": excludes,
720
"dll_excludes": dll_excludes,
721
"dist_dir": "win32_bzr.exe",
723
"custom_boot_script":
724
"tools/win32/py2exe_boot_common.py",
728
# We want the libaray.zip to have optimize = 2, but the exe to have
729
# optimize = 1, so that .py files that get compilied at run time
730
# (e.g. user installed plugins) dont have their doc strings removed.
731
class py2exe_no_oo_exe(py2exe.build_exe.py2exe):
732
def build_executable(self, *args, **kwargs):
734
py2exe.build_exe.py2exe.build_executable(self, *args, **kwargs)
737
if __name__ == '__main__':
738
command_classes['install_data'] = install_data_with_bytecompile
739
command_classes['py2exe'] = py2exe_no_oo_exe
740
setup(options=options_list,
741
console=console_targets,
743
zipfile='lib/library.zip',
744
data_files=data_files,
745
cmdclass=command_classes,
749
# ad-hoc for easy_install
751
if 'bdist_egg' not in sys.argv:
752
# generate and install brz.1 only with plain install, not the
754
DATA_FILES = [('man/man1', ['brz.1', 'breezy/git/git-remote-bzr.1'])]
756
DATA_FILES = DATA_FILES + I18N_FILES
758
ARGS = {'scripts': ['brz',
759
# TODO(jelmer): Only install the git scripts if
761
'breezy/git/git-remote-bzr',
762
'breezy/git/bzr-receive-pack',
763
'breezy/git/bzr-upload-pack'],
764
'data_files': DATA_FILES,
765
'cmdclass': command_classes,
766
'ext_modules': ext_modules,
769
ARGS.update(META_INFO)
771
ARGS.update(PKG_DATA)
773
if __name__ == '__main__':