~0x44/nova/bug838466

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/python/dist.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
Distutils convenience functionality.
 
3
 
 
4
Don't use this outside of Twisted.
 
5
 
 
6
Maintainer: Christopher Armstrong
 
7
"""
 
8
 
 
9
import sys, os
 
10
from distutils.command import build_scripts, install_data, build_ext, build_py
 
11
from distutils.errors import CompileError
 
12
from distutils import core
 
13
from distutils.core import Extension
 
14
 
 
15
twisted_subprojects = ["conch", "lore", "mail", "names",
 
16
                       "news", "pair", "runner", "web", "web2",
 
17
                       "words", "vfs"]
 
18
 
 
19
 
 
20
class ConditionalExtension(Extension):
 
21
    """
 
22
    An extension module that will only be compiled if certain conditions are
 
23
    met.
 
24
 
 
25
    @param condition: A callable of one argument which returns True or False to
 
26
        indicate whether the extension should be built. The argument is an
 
27
        instance of L{build_ext_twisted}, which has useful methods for checking
 
28
        things about the platform.
 
29
    """
 
30
    def __init__(self, *args, **kwargs):
 
31
        self.condition = kwargs.pop("condition", lambda builder: True)
 
32
        Extension.__init__(self, *args, **kwargs)
 
33
 
 
34
 
 
35
 
 
36
def setup(**kw):
 
37
    """
 
38
    An alternative to distutils' setup() which is specially designed
 
39
    for Twisted subprojects.
 
40
 
 
41
    Pass twisted_subproject=projname if you want package and data
 
42
    files to automatically be found for you.
 
43
 
 
44
    @param conditionalExtensions: Extensions to optionally build.
 
45
    @type conditionalExtensions: C{list} of L{ConditionalExtension}
 
46
    """
 
47
    return core.setup(**get_setup_args(**kw))
 
48
 
 
49
def get_setup_args(**kw):
 
50
    if 'twisted_subproject' in kw:
 
51
        if 'twisted' not in os.listdir('.'):
 
52
            raise RuntimeError("Sorry, you need to run setup.py from the "
 
53
                               "toplevel source directory.")
 
54
        projname = kw['twisted_subproject']
 
55
        projdir = os.path.join('twisted', projname)
 
56
 
 
57
        kw['packages'] = getPackages(projdir, parent='twisted')
 
58
        kw['version'] = getVersion(projname)
 
59
 
 
60
        plugin = "twisted/plugins/twisted_" + projname + ".py"
 
61
        if os.path.exists(plugin):
 
62
            kw.setdefault('py_modules', []).append(
 
63
                plugin.replace("/", ".")[:-3])
 
64
 
 
65
        kw['data_files'] = getDataFiles(projdir, parent='twisted')
 
66
 
 
67
        del kw['twisted_subproject']
 
68
    else:
 
69
        if 'plugins' in kw:
 
70
            py_modules = []
 
71
            for plg in kw['plugins']:
 
72
                py_modules.append("twisted.plugins." + plg)
 
73
            kw.setdefault('py_modules', []).extend(py_modules)
 
74
            del kw['plugins']
 
75
 
 
76
    if 'cmdclass' not in kw:
 
77
        kw['cmdclass'] = {
 
78
            'install_data': install_data_twisted,
 
79
            'build_scripts': build_scripts_twisted}
 
80
        if sys.version_info[:3] < (2, 3, 0):
 
81
            kw['cmdclass']['build_py'] = build_py_twisted
 
82
 
 
83
    if "conditionalExtensions" in kw:
 
84
        extensions = kw["conditionalExtensions"]
 
85
        del kw["conditionalExtensions"]
 
86
 
 
87
        if 'ext_modules' not in kw:
 
88
            # This is a workaround for distutils behavior; ext_modules isn't
 
89
            # actually used by our custom builder.  distutils deep-down checks
 
90
            # to see if there are any ext_modules defined before invoking
 
91
            # the build_ext command.  We need to trigger build_ext regardless
 
92
            # because it is the thing that does the conditional checks to see
 
93
            # if it should build any extensions.  The reason we have to delay
 
94
            # the conditional checks until then is that the compiler objects
 
95
            # are not yet set up when this code is executed.
 
96
            kw["ext_modules"] = extensions
 
97
 
 
98
        class my_build_ext(build_ext_twisted):
 
99
            conditionalExtensions = extensions
 
100
        kw.setdefault('cmdclass', {})['build_ext'] = my_build_ext
 
101
    return kw
 
102
 
 
103
def getVersion(proj, base="twisted"):
 
104
    """
 
105
    Extract the version number for a given project.
 
106
 
 
107
    @param proj: the name of the project. Examples are "core",
 
108
    "conch", "words", "mail".
 
109
 
 
110
    @rtype: str
 
111
    @returns: The version number of the project, as a string like
 
112
    "2.0.0".
 
113
    """
 
114
    if proj == 'core':
 
115
        vfile = os.path.join(base, '_version.py')
 
116
    else:
 
117
        vfile = os.path.join(base, proj, '_version.py')
 
118
    ns = {'__name__': 'Nothing to see here'}
 
119
    execfile(vfile, ns)
 
120
    return ns['version'].base()
 
121
 
 
122
 
 
123
# Names that are exluded from globbing results:
 
124
EXCLUDE_NAMES = ["{arch}", "CVS", ".cvsignore", "_darcs",
 
125
                 "RCS", "SCCS", ".svn"]
 
126
EXCLUDE_PATTERNS = ["*.py[cdo]", "*.s[ol]", ".#*", "*~", "*.py"]
 
127
 
 
128
import fnmatch
 
129
 
 
130
def _filterNames(names):
 
131
    """Given a list of file names, return those names that should be copied.
 
132
    """
 
133
    names = [n for n in names
 
134
             if n not in EXCLUDE_NAMES]
 
135
    # This is needed when building a distro from a working
 
136
    # copy (likely a checkout) rather than a pristine export:
 
137
    for pattern in EXCLUDE_PATTERNS:
 
138
        names = [n for n in names
 
139
                 if (not fnmatch.fnmatch(n, pattern))
 
140
                 and (not n.endswith('.py'))]
 
141
    return names
 
142
 
 
143
def relativeTo(base, relativee):
 
144
    """
 
145
    Gets 'relativee' relative to 'basepath'.
 
146
 
 
147
    i.e.,
 
148
 
 
149
    >>> relativeTo('/home/', '/home/radix/')
 
150
    'radix'
 
151
    >>> relativeTo('.', '/home/radix/Projects/Twisted') # curdir is /home/radix
 
152
    'Projects/Twisted'
 
153
 
 
154
    The 'relativee' must be a child of 'basepath'.
 
155
    """
 
156
    basepath = os.path.abspath(base)
 
157
    relativee = os.path.abspath(relativee)
 
158
    if relativee.startswith(basepath):
 
159
        relative = relativee[len(basepath):]
 
160
        if relative.startswith(os.sep):
 
161
            relative = relative[1:]
 
162
        return os.path.join(base, relative)
 
163
    raise ValueError("%s is not a subpath of %s" % (relativee, basepath))
 
164
 
 
165
 
 
166
def getDataFiles(dname, ignore=None, parent=None):
 
167
    """
 
168
    Get all the data files that should be included in this distutils Project.
 
169
 
 
170
    'dname' should be the path to the package that you're distributing.
 
171
 
 
172
    'ignore' is a list of sub-packages to ignore.  This facilitates
 
173
    disparate package hierarchies.  That's a fancy way of saying that
 
174
    the 'twisted' package doesn't want to include the 'twisted.conch'
 
175
    package, so it will pass ['conch'] as the value.
 
176
 
 
177
    'parent' is necessary if you're distributing a subpackage like
 
178
    twisted.conch.  'dname' should point to 'twisted/conch' and 'parent'
 
179
    should point to 'twisted'.  This ensures that your data_files are
 
180
    generated correctly, only using relative paths for the first element
 
181
    of the tuple ('twisted/conch/*').
 
182
    The default 'parent' is the current working directory.
 
183
    """
 
184
    parent = parent or "."
 
185
    ignore = ignore or []
 
186
    result = []
 
187
    for directory, subdirectories, filenames in os.walk(dname):
 
188
        resultfiles = []
 
189
        for exname in EXCLUDE_NAMES:
 
190
            if exname in subdirectories:
 
191
                subdirectories.remove(exname)
 
192
        for ig in ignore:
 
193
            if ig in subdirectories:
 
194
                subdirectories.remove(ig)
 
195
        for filename in _filterNames(filenames):
 
196
            resultfiles.append(filename)
 
197
        if resultfiles:
 
198
            result.append((relativeTo(parent, directory),
 
199
                           [relativeTo(parent,
 
200
                                       os.path.join(directory, filename))
 
201
                            for filename in resultfiles]))
 
202
    return result
 
203
 
 
204
def getPackages(dname, pkgname=None, results=None, ignore=None, parent=None):
 
205
    """
 
206
    Get all packages which are under dname. This is necessary for
 
207
    Python 2.2's distutils. Pretty similar arguments to getDataFiles,
 
208
    including 'parent'.
 
209
    """
 
210
    parent = parent or ""
 
211
    prefix = []
 
212
    if parent:
 
213
        prefix = [parent]
 
214
    bname = os.path.basename(dname)
 
215
    ignore = ignore or []
 
216
    if bname in ignore:
 
217
        return []
 
218
    if results is None:
 
219
        results = []
 
220
    if pkgname is None:
 
221
        pkgname = []
 
222
    subfiles = os.listdir(dname)
 
223
    abssubfiles = [os.path.join(dname, x) for x in subfiles]
 
224
    if '__init__.py' in subfiles:
 
225
        results.append(prefix + pkgname + [bname])
 
226
        for subdir in filter(os.path.isdir, abssubfiles):
 
227
            getPackages(subdir, pkgname=pkgname + [bname],
 
228
                        results=results, ignore=ignore,
 
229
                        parent=parent)
 
230
    res = ['.'.join(result) for result in results]
 
231
    return res
 
232
 
 
233
 
 
234
 
 
235
def getScripts(projname, basedir=''):
 
236
    """
 
237
    Returns a list of scripts for a Twisted subproject; this works in
 
238
    any of an SVN checkout, a project-specific tarball.
 
239
    """
 
240
    scriptdir = os.path.join(basedir, 'bin', projname)
 
241
    if not os.path.isdir(scriptdir):
 
242
        # Probably a project-specific tarball, in which case only this
 
243
        # project's bins are included in 'bin'
 
244
        scriptdir = os.path.join(basedir, 'bin')
 
245
        if not os.path.isdir(scriptdir):
 
246
            return []
 
247
    thingies = os.listdir(scriptdir)
 
248
    if '.svn' in thingies:
 
249
        thingies.remove('.svn')
 
250
    return filter(os.path.isfile,
 
251
                  [os.path.join(scriptdir, x) for x in thingies])
 
252
 
 
253
 
 
254
## Helpers and distutil tweaks
 
255
 
 
256
class build_py_twisted(build_py.build_py):
 
257
    """
 
258
    Changes behavior in Python 2.2 to support simultaneous specification of
 
259
    `packages' and `py_modules'.
 
260
    """
 
261
    def run(self):
 
262
        if self.py_modules:
 
263
            self.build_modules()
 
264
        if self.packages:
 
265
            self.build_packages()
 
266
        self.byte_compile(self.get_outputs(include_bytecode=0))
 
267
 
 
268
 
 
269
 
 
270
class build_scripts_twisted(build_scripts.build_scripts):
 
271
    """Renames scripts so they end with '.py' on Windows."""
 
272
 
 
273
    def run(self):
 
274
        build_scripts.build_scripts.run(self)
 
275
        if not os.name == "nt":
 
276
            return
 
277
        for f in os.listdir(self.build_dir):
 
278
            fpath=os.path.join(self.build_dir, f)
 
279
            if not fpath.endswith(".py"):
 
280
                try:
 
281
                    os.unlink(fpath + ".py")
 
282
                except EnvironmentError, e:
 
283
                    if e.args[1]=='No such file or directory':
 
284
                        pass
 
285
                os.rename(fpath, fpath + ".py")
 
286
 
 
287
 
 
288
 
 
289
class install_data_twisted(install_data.install_data):
 
290
    """I make sure data files are installed in the package directory."""
 
291
    def finalize_options(self):
 
292
        self.set_undefined_options('install',
 
293
            ('install_lib', 'install_dir')
 
294
        )
 
295
        install_data.install_data.finalize_options(self)
 
296
 
 
297
 
 
298
 
 
299
class build_ext_twisted(build_ext.build_ext):
 
300
    """
 
301
    Allow subclasses to easily detect and customize Extensions to
 
302
    build at install-time.
 
303
    """
 
304
 
 
305
    def prepare_extensions(self):
 
306
        """
 
307
        Prepare the C{self.extensions} attribute (used by
 
308
        L{build_ext.build_ext}) by checking which extensions in
 
309
        L{conditionalExtensions} should be built.  In addition, if we are
 
310
        building on NT, define the WIN32 macro to 1.
 
311
        """
 
312
        # always define WIN32 under Windows
 
313
        if os.name == 'nt':
 
314
            self.define_macros = [("WIN32", 1)]
 
315
        else:
 
316
            self.define_macros = []
 
317
        self.extensions = [x for x in self.conditionalExtensions
 
318
                           if x.condition(self)]
 
319
        for ext in self.extensions:
 
320
            ext.define_macros.extend(self.define_macros)
 
321
 
 
322
 
 
323
    def build_extensions(self):
 
324
        """
 
325
        Check to see which extension modules to build and then build them.
 
326
        """
 
327
        self.prepare_extensions()
 
328
        build_ext.build_ext.build_extensions(self)
 
329
 
 
330
 
 
331
    def _remove_conftest(self):
 
332
        for filename in ("conftest.c", "conftest.o", "conftest.obj"):
 
333
            try:
 
334
                os.unlink(filename)
 
335
            except EnvironmentError:
 
336
                pass
 
337
 
 
338
 
 
339
    def _compile_helper(self, content):
 
340
        conftest = open("conftest.c", "w")
 
341
        try:
 
342
            conftest.write(content)
 
343
            conftest.close()
 
344
 
 
345
            try:
 
346
                self.compiler.compile(["conftest.c"], output_dir='')
 
347
            except CompileError:
 
348
                return False
 
349
            return True
 
350
        finally:
 
351
            self._remove_conftest()
 
352
 
 
353
 
 
354
    def _check_header(self, header_name):
 
355
        """
 
356
        Check if the given header can be included by trying to compile a file
 
357
        that contains only an #include line.
 
358
        """
 
359
        self.compiler.announce("checking for %s ..." % header_name, 0)
 
360
        return self._compile_helper("#include <%s>\n" % header_name)
 
361