~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to setup.py

  • Committer: Jelmer Vernooij
  • Date: 2011-08-19 22:34:02 UTC
  • mto: This revision was merged to the branch mainline in revision 6089.
  • Revision ID: jelmer@samba.org-20110819223402-wjywqb0fa1xxx522
Use get_transport_from_{url,path} in more places.

Show diffs side-by-side

added added

removed removed

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