/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to setup.py

  • Committer: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2020-05-06 03:06:18 UTC
  • mfrom: (7500.1.2 trunk-merge-3.1)
  • Revision ID: breezy.the.bot@gmail.com-20200506030618-131sjbc876q7on66
Merge the 3.1 branch.

Merged from https://code.launchpad.net/~jelmer/brz/trunk-merge-3.1/+merge/383481

Show diffs side-by-side

added added

removed removed

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