~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to nova/openstack/common/setup.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-08-16 14:04:11 UTC
  • mto: This revision was merged to the branch mainline in revision 84.
  • Revision ID: package-import@ubuntu.com-20120816140411-0mr4n241wmk30t9l
Tags: upstream-2012.2~f3
ImportĀ upstreamĀ versionĀ 2012.2~f3

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
Utilities with minimum-depends for use in setup.py
20
20
"""
21
21
 
 
22
import datetime
22
23
import os
23
24
import re
24
25
import subprocess
 
26
import sys
 
27
 
 
28
from setuptools.command import sdist
25
29
 
26
30
 
27
31
def parse_mailmap(mailmap='.mailmap'):
31
35
        for l in fp:
32
36
            l = l.strip()
33
37
            if not l.startswith('#') and ' ' in l:
34
 
                canonical_email, alias = l.split(' ')
 
38
                canonical_email, alias = [x for x in l.split(' ')
 
39
                                          if x.startswith('<')]
35
40
                mapping[alias] = canonical_email
36
41
    return mapping
37
42
 
47
52
 
48
53
# Get requirements from the first file that exists
49
54
def get_reqs_from_files(requirements_files):
50
 
    reqs_in = []
51
55
    for requirements_file in requirements_files:
52
56
        if os.path.exists(requirements_file):
53
57
            return open(requirements_file, 'r').read().split('\n')
58
62
                                           'tools/pip-requires']):
59
63
    requirements = []
60
64
    for line in get_reqs_from_files(requirements_files):
 
65
        # For the requirements list, we need to inject only the portion
 
66
        # after egg= so that distutils knows the package it's looking for
 
67
        # such as:
 
68
        # -e git://github.com/openstack/nova/master#egg=nova
61
69
        if re.match(r'\s*-e\s+', line):
62
70
            requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1',
63
71
                                line))
 
72
        # such as:
 
73
        # http://github.com/openstack/nova/zipball/master#egg=nova
 
74
        elif re.match(r'\s*https?:', line):
 
75
            requirements.append(re.sub(r'\s*https?:.*#egg=(.*)$', r'\1',
 
76
                                line))
 
77
        # -f lines are for index locations, and don't get used here
64
78
        elif re.match(r'\s*-f\s+', line):
65
79
            pass
 
80
        # argparse is part of the standard library starting with 2.7
 
81
        # adding it to the requirements list screws distro installs
 
82
        elif line == 'argparse' and sys.version_info >= (2, 7):
 
83
            pass
66
84
        else:
67
85
            requirements.append(line)
68
86
 
72
90
def parse_dependency_links(requirements_files=['requirements.txt',
73
91
                                               'tools/pip-requires']):
74
92
    dependency_links = []
 
93
    # dependency_links inject alternate locations to find packages listed
 
94
    # in requirements
75
95
    for line in get_reqs_from_files(requirements_files):
 
96
        # skip comments and blank lines
76
97
        if re.match(r'(\s*#)|(\s*$)', line):
77
98
            continue
 
99
        # lines with -e or -f need the whole line, minus the flag
78
100
        if re.match(r'\s*-[ef]\s+', line):
79
101
            dependency_links.append(re.sub(r'\s*-[ef]\s+', '', line))
 
102
        # lines that are only urls can go in unmolested
 
103
        elif re.match(r'\s*https?:', line):
 
104
            dependency_links.append(line)
80
105
    return dependency_links
81
106
 
82
107
 
93
118
def _run_shell_command(cmd):
94
119
    output = subprocess.Popen(["/bin/sh", "-c", cmd],
95
120
                              stdout=subprocess.PIPE)
96
 
    return output.communicate()[0].strip()
97
 
 
98
 
 
99
 
def write_vcsversion(location):
100
 
    """Produce a vcsversion dict that mimics the old one produced by bzr.
101
 
    """
102
 
    if os.path.isdir('.git'):
103
 
        branch_nick_cmd = 'git branch | grep -Ei "\* (.*)" | cut -f2 -d" "'
104
 
        branch_nick = _run_shell_command(branch_nick_cmd)
105
 
        revid_cmd = "git rev-parse HEAD"
106
 
        revid = _run_shell_command(revid_cmd).split()[0]
107
 
        revno_cmd = "git log --oneline | wc -l"
108
 
        revno = _run_shell_command(revno_cmd)
109
 
        with open(location, 'w') as version_file:
110
 
            version_file.write("""
111
 
# This file is automatically generated by setup.py, So don't edit it. :)
112
 
version_info = {
113
 
    'branch_nick': '%s',
114
 
    'revision_id': '%s',
115
 
    'revno': %s
116
 
}
117
 
""" % (branch_nick, revid, revno))
 
121
    out = output.communicate()
 
122
    if len(out) == 0:
 
123
        return None
 
124
    if len(out[0].strip()) == 0:
 
125
        return None
 
126
    return out[0].strip()
 
127
 
 
128
 
 
129
def _get_git_next_version_suffix(branch_name):
 
130
    datestamp = datetime.datetime.now().strftime('%Y%m%d')
 
131
    if branch_name == 'milestone-proposed':
 
132
        revno_prefix = "r"
 
133
    else:
 
134
        revno_prefix = ""
 
135
    _run_shell_command("git fetch origin +refs/meta/*:refs/remotes/meta/*")
 
136
    milestone_cmd = "git show meta/openstack/release:%s" % branch_name
 
137
    milestonever = _run_shell_command(milestone_cmd)
 
138
    if not milestonever:
 
139
        milestonever = ""
 
140
    post_version = _get_git_post_version()
 
141
    # post version should look like:
 
142
    # 0.1.1.4.gcc9e28a
 
143
    # where the bit after the last . is the short sha, and the bit between
 
144
    # the last and second to last is the revno count
 
145
    (revno, sha) = post_version.split(".")[-2:]
 
146
    first_half = "%s~%s" % (milestonever, datestamp)
 
147
    second_half = "%s%s.%s" % (revno_prefix, revno, sha)
 
148
    return ".".join((first_half, second_half))
 
149
 
 
150
 
 
151
def _get_git_current_tag():
 
152
    return _run_shell_command("git tag --contains HEAD")
 
153
 
 
154
 
 
155
def _get_git_tag_info():
 
156
    return _run_shell_command("git describe --tags")
 
157
 
 
158
 
 
159
def _get_git_post_version():
 
160
    current_tag = _get_git_current_tag()
 
161
    if current_tag is not None:
 
162
        return current_tag
 
163
    else:
 
164
        tag_info = _get_git_tag_info()
 
165
        if tag_info is None:
 
166
            base_version = "0.0"
 
167
            cmd = "git --no-pager log --oneline"
 
168
            out = _run_shell_command(cmd)
 
169
            revno = len(out.split("\n"))
 
170
            sha = _run_shell_command("git describe --always")
 
171
        else:
 
172
            tag_infos = tag_info.split("-")
 
173
            base_version = "-".join(tag_infos[:-2])
 
174
            (revno, sha) = tag_infos[-2:]
 
175
        return "%s.%s.%s" % (base_version, revno, sha)
118
176
 
119
177
 
120
178
def write_git_changelog():
121
179
    """Write a changelog based on the git changelog."""
122
 
    if os.path.isdir('.git'):
123
 
        git_log_cmd = 'git log --stat'
124
 
        changelog = _run_shell_command(git_log_cmd)
125
 
        mailmap = parse_mailmap()
126
 
        with open("ChangeLog", "w") as changelog_file:
127
 
            changelog_file.write(canonicalize_emails(changelog, mailmap))
 
180
    new_changelog = 'ChangeLog'
 
181
    if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'):
 
182
        if os.path.isdir('.git'):
 
183
            git_log_cmd = 'git log --stat'
 
184
            changelog = _run_shell_command(git_log_cmd)
 
185
            mailmap = parse_mailmap()
 
186
            with open(new_changelog, "w") as changelog_file:
 
187
                changelog_file.write(canonicalize_emails(changelog, mailmap))
 
188
    else:
 
189
        open(new_changelog, 'w').close()
128
190
 
129
191
 
130
192
def generate_authors():
132
194
    jenkins_email = 'jenkins@review.openstack.org'
133
195
    old_authors = 'AUTHORS.in'
134
196
    new_authors = 'AUTHORS'
135
 
    if os.path.isdir('.git'):
136
 
        # don't include jenkins email address in AUTHORS file
137
 
        git_log_cmd = "git log --format='%aN <%aE>' | sort -u | " \
138
 
                      "grep -v " + jenkins_email
139
 
        changelog = _run_shell_command(git_log_cmd)
140
 
        mailmap = parse_mailmap()
141
 
        with open(new_authors, 'w') as new_authors_fh:
142
 
            new_authors_fh.write(canonicalize_emails(changelog, mailmap))
143
 
            if os.path.exists(old_authors):
144
 
                with open(old_authors, "r") as old_authors_fh:
145
 
                    new_authors_fh.write('\n' + old_authors_fh.read())
 
197
    if not os.getenv('SKIP_GENERATE_AUTHORS'):
 
198
        if os.path.isdir('.git'):
 
199
            # don't include jenkins email address in AUTHORS file
 
200
            git_log_cmd = ("git log --format='%aN <%aE>' | sort -u | "
 
201
                           "grep -v " + jenkins_email)
 
202
            changelog = _run_shell_command(git_log_cmd)
 
203
            mailmap = parse_mailmap()
 
204
            with open(new_authors, 'w') as new_authors_fh:
 
205
                new_authors_fh.write(canonicalize_emails(changelog, mailmap))
 
206
                if os.path.exists(old_authors):
 
207
                    with open(old_authors, "r") as old_authors_fh:
 
208
                        new_authors_fh.write('\n' + old_authors_fh.read())
 
209
    else:
 
210
        open(new_authors, 'w').close()
 
211
 
 
212
 
 
213
_rst_template = """%(heading)s
 
214
%(underline)s
 
215
 
 
216
.. automodule:: %(module)s
 
217
  :members:
 
218
  :undoc-members:
 
219
  :show-inheritance:
 
220
"""
 
221
 
 
222
 
 
223
def read_versioninfo(project):
 
224
    """Read the versioninfo file. If it doesn't exist, we're in a github
 
225
       zipball, and there's really no way to know what version we really
 
226
       are, but that should be ok, because the utility of that should be
 
227
       just about nil if this code path is in use in the first place."""
 
228
    versioninfo_path = os.path.join(project, 'versioninfo')
 
229
    if os.path.exists(versioninfo_path):
 
230
        with open(versioninfo_path, 'r') as vinfo:
 
231
            version = vinfo.read().strip()
 
232
    else:
 
233
        version = "0.0.0"
 
234
    return version
 
235
 
 
236
 
 
237
def write_versioninfo(project, version):
 
238
    """Write a simple file containing the version of the package."""
 
239
    open(os.path.join(project, 'versioninfo'), 'w').write("%s\n" % version)
 
240
 
 
241
 
 
242
def get_cmdclass():
 
243
    """Return dict of commands to run from setup.py."""
 
244
 
 
245
    cmdclass = dict()
 
246
 
 
247
    def _find_modules(arg, dirname, files):
 
248
        for filename in files:
 
249
            if filename.endswith('.py') and filename != '__init__.py':
 
250
                arg["%s.%s" % (dirname.replace('/', '.'),
 
251
                               filename[:-3])] = True
 
252
 
 
253
    class LocalSDist(sdist.sdist):
 
254
        """Builds the ChangeLog and Authors files from VC first."""
 
255
 
 
256
        def run(self):
 
257
            write_git_changelog()
 
258
            generate_authors()
 
259
            # sdist.sdist is an old style class, can't use super()
 
260
            sdist.sdist.run(self)
 
261
 
 
262
    cmdclass['sdist'] = LocalSDist
 
263
 
 
264
    # If Sphinx is installed on the box running setup.py,
 
265
    # enable setup.py to build the documentation, otherwise,
 
266
    # just ignore it
 
267
    try:
 
268
        from sphinx.setup_command import BuildDoc
 
269
 
 
270
        class LocalBuildDoc(BuildDoc):
 
271
            def generate_autoindex(self):
 
272
                print "**Autodocumenting from %s" % os.path.abspath(os.curdir)
 
273
                modules = {}
 
274
                option_dict = self.distribution.get_option_dict('build_sphinx')
 
275
                source_dir = os.path.join(option_dict['source_dir'][1], 'api')
 
276
                if not os.path.exists(source_dir):
 
277
                    os.makedirs(source_dir)
 
278
                for pkg in self.distribution.packages:
 
279
                    if '.' not in pkg:
 
280
                        os.path.walk(pkg, _find_modules, modules)
 
281
                module_list = modules.keys()
 
282
                module_list.sort()
 
283
                autoindex_filename = os.path.join(source_dir, 'autoindex.rst')
 
284
                with open(autoindex_filename, 'w') as autoindex:
 
285
                    autoindex.write(""".. toctree::
 
286
   :maxdepth: 1
 
287
 
 
288
""")
 
289
                    for module in module_list:
 
290
                        output_filename = os.path.join(source_dir,
 
291
                                                       "%s.rst" % module)
 
292
                        heading = "The :mod:`%s` Module" % module
 
293
                        underline = "=" * len(heading)
 
294
                        values = dict(module=module, heading=heading,
 
295
                                      underline=underline)
 
296
 
 
297
                        print "Generating %s" % output_filename
 
298
                        with open(output_filename, 'w') as output_file:
 
299
                            output_file.write(_rst_template % values)
 
300
                        autoindex.write("   %s.rst\n" % module)
 
301
 
 
302
            def run(self):
 
303
                if not os.getenv('SPHINX_DEBUG'):
 
304
                    self.generate_autoindex()
 
305
 
 
306
                for builder in ['html', 'man']:
 
307
                    self.builder = builder
 
308
                    self.finalize_options()
 
309
                    self.project = self.distribution.get_name()
 
310
                    self.version = self.distribution.get_version()
 
311
                    self.release = self.distribution.get_version()
 
312
                    BuildDoc.run(self)
 
313
        cmdclass['build_sphinx'] = LocalBuildDoc
 
314
    except ImportError:
 
315
        pass
 
316
 
 
317
    return cmdclass
 
318
 
 
319
 
 
320
def get_git_branchname():
 
321
    for branch in _run_shell_command("git branch --color=never").split("\n"):
 
322
        if branch.startswith('*'):
 
323
            _branch_name = branch.split()[1].strip()
 
324
    if _branch_name == "(no":
 
325
        _branch_name = "no-branch"
 
326
    return _branch_name
 
327
 
 
328
 
 
329
def get_pre_version(projectname, base_version):
 
330
    """Return a version which is leading up to a version that will
 
331
       be released in the future."""
 
332
    if os.path.isdir('.git'):
 
333
        current_tag = _get_git_current_tag()
 
334
        if current_tag is not None:
 
335
            version = current_tag
 
336
        else:
 
337
            branch_name = os.getenv('BRANCHNAME',
 
338
                                    os.getenv('GERRIT_REFNAME',
 
339
                                              get_git_branchname()))
 
340
            version_suffix = _get_git_next_version_suffix(branch_name)
 
341
            version = "%s~%s" % (base_version, version_suffix)
 
342
        write_versioninfo(projectname, version)
 
343
        return version
 
344
    else:
 
345
        version = read_versioninfo(projectname)
 
346
    return version
 
347
 
 
348
 
 
349
def get_post_version(projectname):
 
350
    """Return a version which is equal to the tag that's on the current
 
351
    revision if there is one, or tag plus number of additional revisions
 
352
    if the current revision has no tag."""
 
353
 
 
354
    if os.path.isdir('.git'):
 
355
        version = _get_git_post_version()
 
356
        write_versioninfo(projectname, version)
 
357
        return version
 
358
    return read_versioninfo(projectname)