1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2011 OpenStack LLC.
6
# Licensed under the Apache License, Version 2.0 (the "License"); you may
7
# not use this file except in compliance with the License. You may obtain
8
# a copy of the License at
10
# http://www.apache.org/licenses/LICENSE-2.0
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
# License for the specific language governing permissions and limitations
19
Utilities with minimum-depends for use in setup.py
26
from setuptools.command import sdist
29
def parse_mailmap(mailmap='.mailmap'):
31
if os.path.exists(mailmap):
32
fp = open(mailmap, 'r')
35
if not l.startswith('#') and ' ' in l:
36
canonical_email, alias = [x for x in l.split(' ')
38
mapping[alias] = canonical_email
42
def canonicalize_emails(changelog, mapping):
43
"""Takes in a string and an email alias mapping and replaces all
44
instances of the aliases in the string with their real email.
46
for alias, email in mapping.iteritems():
47
changelog = changelog.replace(alias, email)
51
# Get requirements from the first file that exists
52
def get_reqs_from_files(requirements_files):
54
for requirements_file in requirements_files:
55
if os.path.exists(requirements_file):
56
return open(requirements_file, 'r').read().split('\n')
60
def parse_requirements(requirements_files=['requirements.txt',
61
'tools/pip-requires']):
63
for line in get_reqs_from_files(requirements_files):
64
# For the requirements list, we need to inject only the portion
65
# after egg= so that distutils knows the package it's looking for
67
# -e git://github.com/openstack/nova/master#egg=nova
68
if re.match(r'\s*-e\s+', line):
69
requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1',
72
# http://github.com/openstack/nova/zipball/master#egg=nova
73
elif re.match(r'\s*https?:', line):
74
requirements.append(re.sub(r'\s*https?:.*#egg=(.*)$', r'\1',
76
# -f lines are for index locations, and don't get used here
77
elif re.match(r'\s*-f\s+', line):
80
requirements.append(line)
85
def parse_dependency_links(requirements_files=['requirements.txt',
86
'tools/pip-requires']):
88
# dependency_links inject alternate locations to find packages listed
90
for line in get_reqs_from_files(requirements_files):
91
# skip comments and blank lines
92
if re.match(r'(\s*#)|(\s*$)', line):
94
# lines with -e or -f need the whole line, minus the flag
95
if re.match(r'\s*-[ef]\s+', line):
96
dependency_links.append(re.sub(r'\s*-[ef]\s+', '', line))
97
# lines that are only urls can go in unmolested
98
elif re.match(r'\s*https?:', line):
99
dependency_links.append(line)
100
return dependency_links
103
def write_requirements():
104
venv = os.environ.get('VIRTUAL_ENV', None)
106
with open("requirements.txt", "w") as req_file:
107
output = subprocess.Popen(["pip", "-E", venv, "freeze", "-l"],
108
stdout=subprocess.PIPE)
109
requirements = output.communicate()[0].strip()
110
req_file.write(requirements)
113
def _run_shell_command(cmd):
114
output = subprocess.Popen(["/bin/sh", "-c", cmd],
115
stdout=subprocess.PIPE)
116
return output.communicate()[0].strip()
119
def write_vcsversion(location):
120
"""Produce a vcsversion dict that mimics the old one produced by bzr.
122
if os.path.isdir('.git'):
123
branch_nick_cmd = 'git branch | grep -Ei "\* (.*)" | cut -f2 -d" "'
124
branch_nick = _run_shell_command(branch_nick_cmd)
125
revid_cmd = "git rev-parse HEAD"
126
revid = _run_shell_command(revid_cmd).split()[0]
127
revno_cmd = "git log --oneline | wc -l"
128
revno = _run_shell_command(revno_cmd)
129
with open(location, 'w') as version_file:
130
version_file.write("""
131
# This file is automatically generated by setup.py, So don't edit it. :)
137
""" % (branch_nick, revid, revno))
140
def write_git_changelog():
141
"""Write a changelog based on the git changelog."""
142
if os.path.isdir('.git'):
143
git_log_cmd = 'git log --stat'
144
changelog = _run_shell_command(git_log_cmd)
145
mailmap = parse_mailmap()
146
with open("ChangeLog", "w") as changelog_file:
147
changelog_file.write(canonicalize_emails(changelog, mailmap))
150
def generate_authors():
151
"""Create AUTHORS file using git commits."""
152
jenkins_email = 'jenkins@review.openstack.org'
153
old_authors = 'AUTHORS.in'
154
new_authors = 'AUTHORS'
155
if os.path.isdir('.git'):
156
# don't include jenkins email address in AUTHORS file
157
git_log_cmd = ("git log --format='%aN <%aE>' | sort -u | "
158
"grep -v " + jenkins_email)
159
changelog = _run_shell_command(git_log_cmd)
160
mailmap = parse_mailmap()
161
with open(new_authors, 'w') as new_authors_fh:
162
new_authors_fh.write(canonicalize_emails(changelog, mailmap))
163
if os.path.exists(old_authors):
164
with open(old_authors, "r") as old_authors_fh:
165
new_authors_fh.write('\n' + old_authors_fh.read())
169
"""Return dict of commands to run from setup.py."""
173
class LocalSDist(sdist.sdist):
174
"""Builds the ChangeLog and Authors files from VC first."""
177
write_git_changelog()
179
# sdist.sdist is an old style class, can't use super()
180
sdist.sdist.run(self)
182
cmdclass['sdist'] = LocalSDist
184
# If Sphinx is installed on the box running setup.py,
185
# enable setup.py to build the documentation, otherwise,
188
from sphinx.setup_command import BuildDoc
190
class LocalBuildDoc(BuildDoc):
192
for builder in ['html', 'man']:
193
self.builder = builder
194
self.finalize_options()
196
cmdclass['build_sphinx'] = LocalBuildDoc