~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to setup.py

  • Committer: Patch Queue Manager
  • Date: 2014-04-09 13:36:25 UTC
  • mfrom: (6592.1.2 1303879-py27-issues)
  • Revision ID: pqm@pqm.ubuntu.com-20140409133625-s24spv3kha2w2860
(vila) Fix python-2.7.6 test failures. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/env python
 
2
 
 
3
"""Installation script for bzr.
 
4
Run it with
 
5
 './setup.py install', or
 
6
 './setup.py --help' for more options
 
7
"""
 
8
 
 
9
import os
 
10
import os.path
 
11
import sys
 
12
import copy
 
13
import glob
 
14
 
 
15
if sys.version_info < (2, 6):
 
16
    sys.stderr.write("[ERROR] Not a supported Python version. Need 2.6+\n")
 
17
    sys.exit(1)
 
18
 
 
19
# NOTE: The directory containing setup.py, whether run by 'python setup.py' or
 
20
# './setup.py' or the equivalent with another path, should always be at the
 
21
# start of the path, so this should find the right one...
 
22
import bzrlib
 
23
 
 
24
def get_long_description():
 
25
    dirname = os.path.dirname(__file__)
 
26
    readme = os.path.join(dirname, 'README')
 
27
    f = open(readme, 'rb')
 
28
    try:
 
29
        return f.read()
 
30
    finally:
 
31
        f.close()
 
32
 
 
33
 
 
34
##
 
35
# META INFORMATION FOR SETUP
 
36
# see http://docs.python.org/dist/meta-data.html
 
37
META_INFO = {
 
38
    'name':         'bzr',
 
39
    'version':      bzrlib.__version__,
 
40
    'author':       'Canonical Ltd',
 
41
    'author_email': 'bazaar@lists.canonical.com',
 
42
    'url':          'http://bazaar.canonical.com/',
 
43
    'description':  'Friendly distributed version control system',
 
44
    'license':      'GNU GPL v2',
 
45
    'download_url': 'https://launchpad.net/bzr/+download',
 
46
    'long_description': get_long_description(),
 
47
    'classifiers': [
 
48
        'Development Status :: 6 - Mature',
 
49
        'Environment :: Console',
 
50
        'Intended Audience :: Developers',
 
51
        'Intended Audience :: System Administrators',
 
52
        'License :: OSI Approved :: GNU General Public License (GPL)',
 
53
        'Operating System :: Microsoft :: Windows',
 
54
        'Operating System :: OS Independent',
 
55
        'Operating System :: POSIX',
 
56
        'Programming Language :: Python',
 
57
        'Programming Language :: C',
 
58
        'Topic :: Software Development :: Version Control',
 
59
        ],
 
60
    }
 
61
 
 
62
# The list of packages is automatically generated later. Add other things
 
63
# that are part of BZRLIB here.
 
64
BZRLIB = {}
 
65
 
 
66
PKG_DATA = {# install files from selftest suite
 
67
            'package_data': {'bzrlib': ['doc/api/*.txt',
 
68
                                        'tests/test_patches_data/*',
 
69
                                        'help_topics/en/*.txt',
 
70
                                        'tests/ssl_certs/ca.crt',
 
71
                                        'tests/ssl_certs/server_without_pass.key',
 
72
                                        'tests/ssl_certs/server_with_pass.key',
 
73
                                        'tests/ssl_certs/server.crt',
 
74
                                       ]},
 
75
           }
 
76
I18N_FILES = []
 
77
for filepath in glob.glob("bzrlib/locale/*/LC_MESSAGES/*.mo"):
 
78
    langfile = filepath[len("bzrlib/locale/"):]
 
79
    targetpath = os.path.dirname(os.path.join("share/locale", langfile))
 
80
    I18N_FILES.append((targetpath, [filepath]))
 
81
 
 
82
def get_bzrlib_packages():
 
83
    """Recurse through the bzrlib directory, and extract the package names"""
 
84
 
 
85
    packages = []
 
86
    base_path = os.path.dirname(os.path.abspath(bzrlib.__file__))
 
87
    for root, dirs, files in os.walk(base_path):
 
88
        if '__init__.py' in files:
 
89
            assert root.startswith(base_path)
 
90
            # Get just the path below bzrlib
 
91
            package_path = root[len(base_path):]
 
92
            # Remove leading and trailing slashes
 
93
            package_path = package_path.strip('\\/')
 
94
            if not package_path:
 
95
                package_name = 'bzrlib'
 
96
            else:
 
97
                package_name = ('bzrlib.' +
 
98
                            package_path.replace('/', '.').replace('\\', '.'))
 
99
            packages.append(package_name)
 
100
    return sorted(packages)
 
101
 
 
102
 
 
103
BZRLIB['packages'] = get_bzrlib_packages()
 
104
 
 
105
 
 
106
from distutils import log
 
107
from distutils.core import setup
 
108
from distutils.version import LooseVersion
 
109
from distutils.command.install_scripts import install_scripts
 
110
from distutils.command.install_data import install_data
 
111
from distutils.command.build import build
 
112
 
 
113
###############################
 
114
# Overridden distutils actions
 
115
###############################
 
116
 
 
117
class my_install_scripts(install_scripts):
 
118
    """ Customized install_scripts distutils action.
 
119
    Create bzr.bat for win32.
 
120
    """
 
121
    def run(self):
 
122
        install_scripts.run(self)   # standard action
 
123
 
 
124
        if sys.platform == "win32":
 
125
            try:
 
126
                scripts_dir = os.path.join(sys.prefix, 'Scripts')
 
127
                script_path = self._quoted_path(os.path.join(scripts_dir,
 
128
                                                             "bzr"))
 
129
                python_exe = self._quoted_path(sys.executable)
 
130
                args = self._win_batch_args()
 
131
                batch_str = "@%s %s %s" % (python_exe, script_path, args)
 
132
                batch_path = os.path.join(self.install_dir, "bzr.bat")
 
133
                f = file(batch_path, "w")
 
134
                f.write(batch_str)
 
135
                f.close()
 
136
                print("Created: %s" % batch_path)
 
137
            except Exception:
 
138
                e = sys.exc_info()[1]
 
139
                print("ERROR: Unable to create %s: %s" % (batch_path, e))
 
140
 
 
141
    def _quoted_path(self, path):
 
142
        if ' ' in path:
 
143
            return '"' + path + '"'
 
144
        else:
 
145
            return path
 
146
 
 
147
    def _win_batch_args(self):
 
148
        from bzrlib.win32utils import winver
 
149
        if winver == 'Windows NT':
 
150
            return '%*'
 
151
        else:
 
152
            return '%1 %2 %3 %4 %5 %6 %7 %8 %9'
 
153
#/class my_install_scripts
 
154
 
 
155
 
 
156
class bzr_build(build):
 
157
    """Customized build distutils action.
 
158
    Generate bzr.1.
 
159
    """
 
160
 
 
161
    sub_commands = build.sub_commands + [
 
162
            ('build_mo', lambda _: True),
 
163
            ]
 
164
 
 
165
    def run(self):
 
166
        build.run(self)
 
167
 
 
168
        from tools import generate_docs
 
169
        generate_docs.main(argv=["bzr", "man"])
 
170
 
 
171
 
 
172
########################
 
173
## Setup
 
174
########################
 
175
 
 
176
from bzrlib.bzr_distutils import build_mo
 
177
 
 
178
command_classes = {'install_scripts': my_install_scripts,
 
179
                   'build': bzr_build,
 
180
                   'build_mo': build_mo,
 
181
                   }
 
182
from distutils import log
 
183
from distutils.errors import CCompilerError, DistutilsPlatformError
 
184
from distutils.extension import Extension
 
185
ext_modules = []
 
186
try:
 
187
    try:
 
188
        from Cython.Distutils import build_ext
 
189
        from Cython.Compiler.Version import version as pyrex_version
 
190
    except ImportError:
 
191
        print("No Cython, trying Pyrex...")
 
192
        from Pyrex.Distutils import build_ext
 
193
        from Pyrex.Compiler.Version import version as pyrex_version
 
194
except ImportError:
 
195
    have_pyrex = False
 
196
    # try to build the extension from the prior generated source.
 
197
    print("")
 
198
    print("The python package 'Pyrex' is not available."
 
199
          " If the .c files are available,")
 
200
    print("they will be built,"
 
201
          " but modifying the .pyx files will not rebuild them.")
 
202
    print("")
 
203
    from distutils.command.build_ext import build_ext
 
204
else:
 
205
    have_pyrex = True
 
206
    pyrex_version_info = LooseVersion(pyrex_version)
 
207
 
 
208
 
 
209
class build_ext_if_possible(build_ext):
 
210
 
 
211
    user_options = build_ext.user_options + [
 
212
        ('allow-python-fallback', None,
 
213
         "When an extension cannot be built, allow falling"
 
214
         " back to the pure-python implementation.")
 
215
        ]
 
216
 
 
217
    def initialize_options(self):
 
218
        build_ext.initialize_options(self)
 
219
        self.allow_python_fallback = False
 
220
 
 
221
    def run(self):
 
222
        try:
 
223
            build_ext.run(self)
 
224
        except DistutilsPlatformError:
 
225
            e = sys.exc_info()[1]
 
226
            if not self.allow_python_fallback:
 
227
                log.warn('\n  Cannot build extensions.\n'
 
228
                         '  Use "build_ext --allow-python-fallback" to use'
 
229
                         ' slower python implementations instead.\n')
 
230
                raise
 
231
            log.warn(str(e))
 
232
            log.warn('\n  Extensions cannot be built.\n'
 
233
                     '  Using the slower Python implementations instead.\n')
 
234
 
 
235
    def build_extension(self, ext):
 
236
        try:
 
237
            build_ext.build_extension(self, ext)
 
238
        except CCompilerError:
 
239
            if not self.allow_python_fallback:
 
240
                log.warn('\n  Cannot build extension "%s".\n'
 
241
                         '  Use "build_ext --allow-python-fallback" to use'
 
242
                         ' slower python implementations instead.\n'
 
243
                         % (ext.name,))
 
244
                raise
 
245
            log.warn('\n  Building of "%s" extension failed.\n'
 
246
                     '  Using the slower Python implementation instead.'
 
247
                     % (ext.name,))
 
248
 
 
249
 
 
250
# Override the build_ext if we have Pyrex available
 
251
command_classes['build_ext'] = build_ext_if_possible
 
252
unavailable_files = []
 
253
 
 
254
 
 
255
def add_pyrex_extension(module_name, libraries=None, extra_source=[]):
 
256
    """Add a pyrex module to build.
 
257
 
 
258
    This will use Pyrex to auto-generate the .c file if it is available.
 
259
    Otherwise it will fall back on the .c file. If the .c file is not
 
260
    available, it will warn, and not add anything.
 
261
 
 
262
    You can pass any extra options to Extension through kwargs. One example is
 
263
    'libraries = []'.
 
264
 
 
265
    :param module_name: The python path to the module. This will be used to
 
266
        determine the .pyx and .c files to use.
 
267
    """
 
268
    path = module_name.replace('.', '/')
 
269
    pyrex_name = path + '.pyx'
 
270
    c_name = path + '.c'
 
271
    define_macros = []
 
272
    if sys.platform == 'win32':
 
273
        # pyrex uses the macro WIN32 to detect the platform, even though it
 
274
        # should be using something like _WIN32 or MS_WINDOWS, oh well, we can
 
275
        # give it the right value.
 
276
        define_macros.append(('WIN32', None))
 
277
    if have_pyrex:
 
278
        source = [pyrex_name]
 
279
    else:
 
280
        if not os.path.isfile(c_name):
 
281
            unavailable_files.append(c_name)
 
282
            return
 
283
        else:
 
284
            source = [c_name]
 
285
    source.extend(extra_source)
 
286
    ext_modules.append(Extension(module_name, source,
 
287
        define_macros=define_macros, libraries=libraries))
 
288
 
 
289
 
 
290
add_pyrex_extension('bzrlib._annotator_pyx')
 
291
add_pyrex_extension('bzrlib._bencode_pyx')
 
292
add_pyrex_extension('bzrlib._chunks_to_lines_pyx')
 
293
add_pyrex_extension('bzrlib._groupcompress_pyx',
 
294
                    extra_source=['bzrlib/diff-delta.c'])
 
295
add_pyrex_extension('bzrlib._knit_load_data_pyx')
 
296
add_pyrex_extension('bzrlib._known_graph_pyx')
 
297
add_pyrex_extension('bzrlib._rio_pyx')
 
298
if sys.platform == 'win32':
 
299
    add_pyrex_extension('bzrlib._dirstate_helpers_pyx',
 
300
                        libraries=['Ws2_32'])
 
301
    add_pyrex_extension('bzrlib._walkdirs_win32')
 
302
else:
 
303
    if have_pyrex and pyrex_version_info == LooseVersion("0.9.4.1"):
 
304
        # Pyrex 0.9.4.1 fails to compile this extension correctly
 
305
        # The code it generates re-uses a "local" pointer and
 
306
        # calls "PY_DECREF" after having set it to NULL. (It mixes PY_XDECREF
 
307
        # which is NULL safe with PY_DECREF which is not.)
 
308
        # <https://bugs.launchpad.net/bzr/+bug/449372>
 
309
        # <https://bugs.launchpad.net/bzr/+bug/276868>
 
310
        print('Cannot build extension "bzrlib._dirstate_helpers_pyx" using')
 
311
        print('your version of pyrex "%s". Please upgrade your pyrex'
 
312
              % (pyrex_version,))
 
313
        print('install. For now, the non-compiled (python) version will')
 
314
        print('be used instead.')
 
315
    else:
 
316
        add_pyrex_extension('bzrlib._dirstate_helpers_pyx')
 
317
    add_pyrex_extension('bzrlib._readdir_pyx')
 
318
add_pyrex_extension('bzrlib._chk_map_pyx')
 
319
ext_modules.append(Extension('bzrlib._patiencediff_c',
 
320
                             ['bzrlib/_patiencediff_c.c']))
 
321
if have_pyrex and pyrex_version_info < LooseVersion("0.9.6.3"):
 
322
    print("")
 
323
    print('Your Pyrex/Cython version %s is too old to build the simple_set' % (
 
324
        pyrex_version))
 
325
    print('and static_tuple extensions.')
 
326
    print('Please upgrade to at least Pyrex 0.9.6.3')
 
327
    print("")
 
328
    # TODO: Should this be a fatal error?
 
329
else:
 
330
    # We only need 0.9.6.3 to build _simple_set_pyx, but static_tuple depends
 
331
    # on simple_set
 
332
    add_pyrex_extension('bzrlib._simple_set_pyx')
 
333
    ext_modules.append(Extension('bzrlib._static_tuple_c',
 
334
                                 ['bzrlib/_static_tuple_c.c']))
 
335
add_pyrex_extension('bzrlib._btree_serializer_pyx')
 
336
 
 
337
 
 
338
if unavailable_files:
 
339
    print('C extension(s) not found:')
 
340
    print('   %s' % ('\n  '.join(unavailable_files),))
 
341
    print('The python versions will be used instead.')
 
342
    print("")
 
343
 
 
344
 
 
345
def get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
 
346
                         gui_targets, data_files):
 
347
    packages.append('tbzrcommands')
 
348
 
 
349
    # ModuleFinder can't handle runtime changes to __path__, but
 
350
    # win32com uses them.  Hook this in so win32com.shell is found.
 
351
    import modulefinder
 
352
    import win32com
 
353
    import cPickle as pickle
 
354
    for p in win32com.__path__[1:]:
 
355
        modulefinder.AddPackagePath("win32com", p)
 
356
    for extra in ["win32com.shell"]:
 
357
        __import__(extra)
 
358
        m = sys.modules[extra]
 
359
        for p in m.__path__[1:]:
 
360
            modulefinder.AddPackagePath(extra, p)
 
361
 
 
362
    # TBZR points to the TBZR directory
 
363
    tbzr_root = os.environ["TBZR"]
 
364
 
 
365
    # Ensure tbzrlib itself is on sys.path
 
366
    sys.path.append(tbzr_root)
 
367
 
 
368
    packages.append("tbzrlib")
 
369
 
 
370
    # collect up our icons.
 
371
    cwd = os.getcwd()
 
372
    ico_root = os.path.join(tbzr_root, 'tbzrlib', 'resources')
 
373
    icos = [] # list of (path_root, relative_ico_path)
 
374
    # First always bzr's icon and its in the root of the bzr tree.
 
375
    icos.append(('', 'bzr.ico'))
 
376
    for root, dirs, files in os.walk(ico_root):
 
377
        icos.extend([(ico_root, os.path.join(root, f)[len(ico_root)+1:])
 
378
                     for f in files if f.endswith('.ico')])
 
379
    # allocate an icon ID for each file and the full path to the ico
 
380
    icon_resources = [(rid, os.path.join(ico_dir, ico_name))
 
381
                      for rid, (ico_dir, ico_name) in enumerate(icos)]
 
382
    # create a string resource with the mapping.  Might as well save the
 
383
    # runtime some effort and write a pickle.
 
384
    # Runtime expects unicode objects with forward-slash seps.
 
385
    fse = sys.getfilesystemencoding()
 
386
    map_items = [(f.replace('\\', '/').decode(fse), rid)
 
387
                 for rid, (_, f) in enumerate(icos)]
 
388
    ico_map = dict(map_items)
 
389
    # Create a new resource type of 'ICON_MAP', and use ID=1
 
390
    other_resources = [ ("ICON_MAP", 1, pickle.dumps(ico_map))]
 
391
 
 
392
    excludes.extend("""pywin pywin.dialogs pywin.dialogs.list
 
393
                       win32ui crawler.Crawler""".split())
 
394
 
 
395
    # tbzrcache executables - a "console" version for debugging and a
 
396
    # GUI version that is generally used.
 
397
    tbzrcache = dict(
 
398
        script = os.path.join(tbzr_root, "scripts", "tbzrcache.py"),
 
399
        icon_resources = icon_resources,
 
400
        other_resources = other_resources,
 
401
    )
 
402
    console_targets.append(tbzrcache)
 
403
 
 
404
    # Make a windows version which is the same except for the base name.
 
405
    tbzrcachew = tbzrcache.copy()
 
406
    tbzrcachew["dest_base"]="tbzrcachew"
 
407
    gui_targets.append(tbzrcachew)
 
408
 
 
409
    # ditto for the tbzrcommand tool
 
410
    tbzrcommand = dict(
 
411
        script = os.path.join(tbzr_root, "scripts", "tbzrcommand.py"),
 
412
        icon_resources = icon_resources,
 
413
        other_resources = other_resources,
 
414
    )
 
415
    console_targets.append(tbzrcommand)
 
416
    tbzrcommandw = tbzrcommand.copy()
 
417
    tbzrcommandw["dest_base"]="tbzrcommandw"
 
418
    gui_targets.append(tbzrcommandw)
 
419
    
 
420
    # A utility to see python output from both C++ and Python based shell
 
421
    # extensions
 
422
    tracer = dict(script=os.path.join(tbzr_root, "scripts", "tbzrtrace.py"))
 
423
    console_targets.append(tracer)
 
424
 
 
425
    # The C++ implemented shell extensions.
 
426
    dist_dir = os.path.join(tbzr_root, "shellext", "build")
 
427
    data_files.append(('', [os.path.join(dist_dir, 'tbzrshellext_x86.dll')]))
 
428
    data_files.append(('', [os.path.join(dist_dir, 'tbzrshellext_x64.dll')]))
 
429
 
 
430
 
 
431
def get_qbzr_py2exe_info(includes, excludes, packages, data_files):
 
432
    # PyQt4 itself still escapes the plugin detection code for some reason...
 
433
    includes.append('PyQt4.QtCore')
 
434
    includes.append('PyQt4.QtGui')
 
435
    includes.append('PyQt4.QtTest')
 
436
    includes.append('sip') # extension module required for Qt.
 
437
    packages.append('pygments') # colorizer for qbzr
 
438
    packages.append('docutils') # html formatting
 
439
    includes.append('win32event')  # for qsubprocess stuff
 
440
    # the qt binaries might not be on PATH...
 
441
    # They seem to install to a place like C:\Python25\PyQt4\*
 
442
    # Which is not the same as C:\Python25\Lib\site-packages\PyQt4
 
443
    pyqt_dir = os.path.join(sys.prefix, "PyQt4")
 
444
    pyqt_bin_dir = os.path.join(pyqt_dir, "bin")
 
445
    if os.path.isdir(pyqt_bin_dir):
 
446
        path = os.environ.get("PATH", "")
 
447
        if pyqt_bin_dir.lower() not in [p.lower() for p in path.split(os.pathsep)]:
 
448
            os.environ["PATH"] = path + os.pathsep + pyqt_bin_dir
 
449
    # also add all imageformat plugins to distribution
 
450
    # We will look in 2 places, dirname(PyQt4.__file__) and pyqt_dir
 
451
    base_dirs_to_check = []
 
452
    if os.path.isdir(pyqt_dir):
 
453
        base_dirs_to_check.append(pyqt_dir)
 
454
    try:
 
455
        import PyQt4
 
456
    except ImportError:
 
457
        pass
 
458
    else:
 
459
        pyqt4_base_dir = os.path.dirname(PyQt4.__file__)
 
460
        if pyqt4_base_dir != pyqt_dir:
 
461
            base_dirs_to_check.append(pyqt4_base_dir)
 
462
    if not base_dirs_to_check:
 
463
        log.warn("Can't find PyQt4 installation -> not including imageformat"
 
464
                 " plugins")
 
465
    else:
 
466
        files = []
 
467
        for base_dir in base_dirs_to_check:
 
468
            plug_dir = os.path.join(base_dir, 'plugins', 'imageformats')
 
469
            if os.path.isdir(plug_dir):
 
470
                for fname in os.listdir(plug_dir):
 
471
                    # Include plugin dlls, but not debugging dlls
 
472
                    fullpath = os.path.join(plug_dir, fname)
 
473
                    if fname.endswith('.dll') and not fname.endswith('d4.dll'):
 
474
                        files.append(fullpath)
 
475
        if files:
 
476
            data_files.append(('imageformats', files))
 
477
        else:
 
478
            log.warn('PyQt4 was found, but we could not find any imageformat'
 
479
                     ' plugins. Are you sure your configuration is correct?')
 
480
 
 
481
 
 
482
def get_svn_py2exe_info(includes, excludes, packages):
 
483
    packages.append('subvertpy')
 
484
    packages.append('sqlite3')
 
485
 
 
486
 
 
487
def get_git_py2exe_info(includes, excludes, packages):
 
488
    packages.append('dulwich')
 
489
 
 
490
 
 
491
def get_fastimport_py2exe_info(includes, excludes, packages):
 
492
    # This is the python-fastimport package, not to be confused with the
 
493
    # bzr-fastimport plugin.
 
494
    packages.append('fastimport')
 
495
 
 
496
 
 
497
if 'bdist_wininst' in sys.argv:
 
498
    def find_docs():
 
499
        docs = []
 
500
        for root, dirs, files in os.walk('doc'):
 
501
            r = []
 
502
            for f in files:
 
503
                if (os.path.splitext(f)[1] in ('.html','.css','.png','.pdf')
 
504
                    or f == 'quick-start-summary.svg'):
 
505
                    r.append(os.path.join(root, f))
 
506
            if r:
 
507
                relative = root[4:]
 
508
                if relative:
 
509
                    target = os.path.join('Doc\\Bazaar', relative)
 
510
                else:
 
511
                    target = 'Doc\\Bazaar'
 
512
                docs.append((target, r))
 
513
        return docs
 
514
 
 
515
    # python's distutils-based win32 installer
 
516
    ARGS = {'scripts': ['bzr', 'tools/win32/bzr-win32-bdist-postinstall.py'],
 
517
            'ext_modules': ext_modules,
 
518
            # help pages
 
519
            'data_files': find_docs(),
 
520
            # for building pyrex extensions
 
521
            'cmdclass': command_classes,
 
522
           }
 
523
 
 
524
    ARGS.update(META_INFO)
 
525
    ARGS.update(BZRLIB)
 
526
    PKG_DATA['package_data']['bzrlib'].append('locale/*/LC_MESSAGES/*.mo')
 
527
    ARGS.update(PKG_DATA)
 
528
 
 
529
    setup(**ARGS)
 
530
 
 
531
elif 'py2exe' in sys.argv:
 
532
    # py2exe setup
 
533
    import py2exe
 
534
 
 
535
    # pick real bzr version
 
536
    import bzrlib
 
537
 
 
538
    version_number = []
 
539
    for i in bzrlib.version_info[:4]:
 
540
        try:
 
541
            i = int(i)
 
542
        except ValueError:
 
543
            i = 0
 
544
        version_number.append(str(i))
 
545
    version_str = '.'.join(version_number)
 
546
 
 
547
    # An override to install_data used only by py2exe builds, which arranges
 
548
    # to byte-compile any .py files in data_files (eg, our plugins)
 
549
    # Necessary as we can't rely on the user having the relevant permissions
 
550
    # to the "Program Files" directory to generate them on the fly.
 
551
    class install_data_with_bytecompile(install_data):
 
552
        def run(self):
 
553
            from distutils.util import byte_compile
 
554
 
 
555
            install_data.run(self)
 
556
 
 
557
            py2exe = self.distribution.get_command_obj('py2exe', False)
 
558
            # GZ 2010-04-19: Setup has py2exe.optimize as 2, but give plugins
 
559
            #                time before living with docstring stripping
 
560
            optimize = 1
 
561
            compile_names = [f for f in self.outfiles if f.endswith('.py')]
 
562
            # Round mtime to nearest even second so that installing on a FAT
 
563
            # filesystem bytecode internal and script timestamps will match
 
564
            for f in compile_names:
 
565
                mtime = os.stat(f).st_mtime
 
566
                remainder = mtime % 2
 
567
                if remainder:
 
568
                    mtime -= remainder
 
569
                    os.utime(f, (mtime, mtime))
 
570
            byte_compile(compile_names,
 
571
                         optimize=optimize,
 
572
                         force=self.force, prefix=self.install_dir,
 
573
                         dry_run=self.dry_run)
 
574
            self.outfiles.extend([f + 'o' for f in compile_names])
 
575
    # end of class install_data_with_bytecompile
 
576
 
 
577
    target = py2exe.build_exe.Target(script = "bzr",
 
578
                                     dest_base = "bzr",
 
579
                                     icon_resources = [(0,'bzr.ico')],
 
580
                                     name = META_INFO['name'],
 
581
                                     version = version_str,
 
582
                                     description = META_INFO['description'],
 
583
                                     author = META_INFO['author'],
 
584
                                     copyright = "(c) Canonical Ltd, 2005-2010",
 
585
                                     company_name = "Canonical Ltd.",
 
586
                                     comments = META_INFO['description'],
 
587
                                    )
 
588
    gui_target = copy.copy(target)
 
589
    gui_target.dest_base = "bzrw"
 
590
 
 
591
    packages = BZRLIB['packages']
 
592
    packages.remove('bzrlib')
 
593
    packages = [i for i in packages if not i.startswith('bzrlib.plugins')]
 
594
    includes = []
 
595
    for i in glob.glob('bzrlib\\*.py'):
 
596
        module = i[:-3].replace('\\', '.')
 
597
        if module.endswith('__init__'):
 
598
            module = module[:-len('__init__')]
 
599
        includes.append(module)
 
600
 
 
601
    additional_packages = set()
 
602
    if sys.version.startswith('2.4'):
 
603
        # adding elementtree package
 
604
        additional_packages.add('elementtree')
 
605
    elif sys.version.startswith('2.6') or sys.version.startswith('2.5'):
 
606
        additional_packages.add('xml.etree')
 
607
    else:
 
608
        import warnings
 
609
        warnings.warn('Unknown Python version.\n'
 
610
                      'Please check setup.py script for compatibility.')
 
611
 
 
612
    # Although we currently can't enforce it, we consider it an error for
 
613
    # py2exe to report any files are "missing".  Such modules we know aren't
 
614
    # used should be listed here.
 
615
    excludes = """Tkinter psyco ElementPath r_hmac
 
616
                  ImaginaryModule cElementTree elementtree.ElementTree
 
617
                  Crypto.PublicKey._fastmath
 
618
                  medusa medusa.filesys medusa.ftp_server
 
619
                  tools
 
620
                  resource validate""".split()
 
621
    dll_excludes = []
 
622
 
 
623
    # email package from std python library use lazy import,
 
624
    # so we need to explicitly add all package
 
625
    additional_packages.add('email')
 
626
    # And it uses funky mappings to conver to 'Oldname' to 'newname'.  As
 
627
    # a result, packages like 'email.Parser' show as missing.  Tell py2exe
 
628
    # to exclude them.
 
629
    import email
 
630
    for oldname in getattr(email, '_LOWERNAMES', []):
 
631
        excludes.append("email." + oldname)
 
632
    for oldname in getattr(email, '_MIMENAMES', []):
 
633
        excludes.append("email.MIME" + oldname)
 
634
 
 
635
    # text files for help topis
 
636
    text_topics = glob.glob('bzrlib/help_topics/en/*.txt')
 
637
    topics_files = [('lib/help_topics/en', text_topics)]
 
638
 
 
639
    # built-in plugins
 
640
    plugins_files = []
 
641
    # XXX - should we consider having the concept of an 'official' build,
 
642
    # which hard-codes the list of plugins, gets more upset if modules are
 
643
    # missing, etc?
 
644
    plugins = None # will be a set after plugin sniffing...
 
645
    for root, dirs, files in os.walk('bzrlib/plugins'):
 
646
        if root == 'bzrlib/plugins':
 
647
            plugins = set(dirs)
 
648
            # We ship plugins as normal files on the file-system - however,
 
649
            # the build process can cause *some* of these plugin files to end
 
650
            # up in library.zip. Thus, we saw (eg) "plugins/svn/test" in
 
651
            # library.zip, and then saw import errors related to that as the
 
652
            # rest of the svn plugin wasn't. So we tell py2exe to leave the
 
653
            # plugins out of the .zip file
 
654
            excludes.extend(["bzrlib.plugins." + d for d in dirs])
 
655
        x = []
 
656
        for i in files:
 
657
            # Throw away files we don't want packaged. Note that plugins may
 
658
            # have data files with all sorts of extensions so we need to
 
659
            # be conservative here about what we ditch.
 
660
            ext = os.path.splitext(i)[1]
 
661
            if ext.endswith('~') or ext in [".pyc", ".swp"]:
 
662
                continue
 
663
            if i == '__init__.py' and root == 'bzrlib/plugins':
 
664
                continue
 
665
            x.append(os.path.join(root, i))
 
666
        if x:
 
667
            target_dir = root[len('bzrlib/'):]  # install to 'plugins/...'
 
668
            plugins_files.append((target_dir, x))
 
669
    # find modules for built-in plugins
 
670
    import tools.package_mf
 
671
    mf = tools.package_mf.CustomModuleFinder()
 
672
    mf.run_package('bzrlib/plugins')
 
673
    packs, mods = mf.get_result()
 
674
    additional_packages.update(packs)
 
675
    includes.extend(mods)
 
676
 
 
677
    console_targets = [target,
 
678
                       'tools/win32/bzr_postinstall.py',
 
679
                       ]
 
680
    gui_targets = [gui_target]
 
681
    data_files = topics_files + plugins_files + I18N_FILES
 
682
 
 
683
    if 'qbzr' in plugins:
 
684
        get_qbzr_py2exe_info(includes, excludes, packages, data_files)
 
685
 
 
686
    if 'svn' in plugins:
 
687
        get_svn_py2exe_info(includes, excludes, packages)
 
688
 
 
689
    if 'git' in plugins:
 
690
        get_git_py2exe_info(includes, excludes, packages)
 
691
 
 
692
    if 'fastimport' in plugins:
 
693
        get_fastimport_py2exe_info(includes, excludes, packages)
 
694
 
 
695
    if "TBZR" in os.environ:
 
696
        # TORTOISE_OVERLAYS_MSI_WIN32 must be set to the location of the
 
697
        # TortoiseOverlays MSI installer file. It is in the TSVN svn repo and
 
698
        # can be downloaded from (username=guest, blank password):
 
699
        # http://tortoisesvn.tigris.org/svn/tortoisesvn/TortoiseOverlays
 
700
        # look for: version-1.0.4/bin/TortoiseOverlays-1.0.4.11886-win32.msi
 
701
        # Ditto for TORTOISE_OVERLAYS_MSI_X64, pointing at *-x64.msi.
 
702
        for needed in ('TORTOISE_OVERLAYS_MSI_WIN32',
 
703
                       'TORTOISE_OVERLAYS_MSI_X64'):
 
704
            url = ('http://guest:@tortoisesvn.tigris.org/svn/tortoisesvn'
 
705
                   '/TortoiseOverlays')
 
706
            if not os.path.isfile(os.environ.get(needed, '<nofile>')):
 
707
                raise RuntimeError(
 
708
                    "\nPlease set %s to the location of the relevant"
 
709
                    "\nTortoiseOverlays .msi installer file."
 
710
                    " The installers can be found at"
 
711
                    "\n  %s"
 
712
                    "\ncheck in the version-X.Y.Z/bin/ subdir" % (needed, url))
 
713
        get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
 
714
                             gui_targets, data_files)
 
715
    else:
 
716
        # print this warning to stderr as output is redirected, so it is seen
 
717
        # at build time.  Also to stdout so it appears in the log
 
718
        for f in (sys.stderr, sys.stdout):
 
719
            f.write("Skipping TBZR binaries - "
 
720
                "please set TBZR to a directory to enable\n")
 
721
 
 
722
    # MSWSOCK.dll is a system-specific library, which py2exe accidentally pulls
 
723
    # in on Vista.
 
724
    dll_excludes.extend(["MSWSOCK.dll",
 
725
                         "MSVCP60.dll",
 
726
                         "MSVCP90.dll",
 
727
                         "powrprof.dll",
 
728
                         "SHFOLDER.dll"])
 
729
    options_list = {"py2exe": {"packages": packages + list(additional_packages),
 
730
                               "includes": includes,
 
731
                               "excludes": excludes,
 
732
                               "dll_excludes": dll_excludes,
 
733
                               "dist_dir": "win32_bzr.exe",
 
734
                               "optimize": 2,
 
735
                               "custom_boot_script":
 
736
                                        "tools/win32/py2exe_boot_common.py",
 
737
                              },
 
738
                   }
 
739
 
 
740
    # We want the libaray.zip to have optimize = 2, but the exe to have
 
741
    # optimize = 1, so that .py files that get compilied at run time
 
742
    # (e.g. user installed plugins) dont have their doc strings removed.
 
743
    class py2exe_no_oo_exe(py2exe.build_exe.py2exe):
 
744
        def build_executable(self, *args, **kwargs):
 
745
            self.optimize = 1
 
746
            py2exe.build_exe.py2exe.build_executable(self, *args, **kwargs)
 
747
            self.optimize = 2
 
748
 
 
749
    if __name__ == '__main__':
 
750
        command_classes['install_data'] = install_data_with_bytecompile
 
751
        command_classes['py2exe'] = py2exe_no_oo_exe
 
752
        setup(options=options_list,
 
753
              console=console_targets,
 
754
              windows=gui_targets,
 
755
              zipfile='lib/library.zip',
 
756
              data_files=data_files,
 
757
              cmdclass=command_classes,
 
758
              )
 
759
 
 
760
else:
 
761
    # ad-hoc for easy_install
 
762
    DATA_FILES = []
 
763
    if not 'bdist_egg' in sys.argv:
 
764
        # generate and install bzr.1 only with plain install, not the
 
765
        # easy_install one
 
766
        DATA_FILES = [('man/man1', ['bzr.1'])]
 
767
 
 
768
    DATA_FILES = DATA_FILES + I18N_FILES
 
769
    # std setup
 
770
    ARGS = {'scripts': ['bzr'],
 
771
            'data_files': DATA_FILES,
 
772
            'cmdclass': command_classes,
 
773
            'ext_modules': ext_modules,
 
774
           }
 
775
 
 
776
    ARGS.update(META_INFO)
 
777
    ARGS.update(BZRLIB)
 
778
    ARGS.update(PKG_DATA)
 
779
 
 
780
    if __name__ == '__main__':
 
781
        setup(**ARGS)