-
Notifications
You must be signed in to change notification settings - Fork 3
/
setup.py
230 lines (191 loc) · 8.93 KB
/
setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#!/usr/bin/env python
import os
import shutil
import subprocess
import sys
import tempfile
import setuptools
import runpy
# Ref : https://packaging.python.org/single_source_version/#single-sourcing-the-version
# runpy is safer and a better habit than exec
version = runpy.run_path('pyros_msgs/_version.py')
__version__ = version.get('__version__')
# Best Flow :
# Clean previous build & dist
# $ gitchangelog >CHANGELOG.rst
# change version in code and changelog
# $ python setup.py prepare_release
# WAIT FOR TRAVIS CHECKS
# $ python setup.py publish
# => TODO : try to do a simpler "release" command
# TODO : command to retrieve extra ROS stuff from a third party release repo ( for ROS devs ). useful in dev only so maybe "rosdevelop" ? or via catkin_pip ?
# TODO : command to release to Pip and ROS (bloom) same version one after the other...
# Clean way to add a custom "python setup.py <command>"
# Ref setup.py command extension : https://blog.niteoweb.com/setuptools-run-custom-code-in-setup-py/
class PrepareReleaseCommand(setuptools.Command):
"""Command to release this package to Pypi"""
description = "prepare a release of pyros"
user_options = []
def initialize_options(self):
"""init options"""
pass
def finalize_options(self):
"""finalize options"""
pass
def run(self):
"""runner"""
# TODO :
# $ gitchangelog >CHANGELOG.rst
# change version in code and changelog
subprocess.check_call(
"git commit CHANGELOG.rst pyros_msgs/_version.py -m 'v{0}'".format(__version__), shell=True)
subprocess.check_call("git push", shell=True)
print("You should verify travis checks, and you can publish this release with :")
print(" python setup.py publish")
sys.exit()
# Clean way to add a custom "python setup.py <command>"
# Ref setup.py command extension : https://blog.niteoweb.com/setuptools-run-custom-code-in-setup-py/
class PublishCommand(setuptools.Command):
"""Command to release this package to Pypi"""
description = "releases pyros to Pypi"
user_options = []
def initialize_options(self):
"""init options"""
# TODO : register option
pass
def finalize_options(self):
"""finalize options"""
pass
def run(self):
"""runner"""
# TODO : clean build/ and dist/ before building...
subprocess.check_call("python setup.py sdist", shell=True)
subprocess.check_call("python setup.py bdist_wheel", shell=True)
# OLD way:
# os.system("python setup.py sdist bdist_wheel upload")
# NEW way:
# Ref: https://packaging.python.org/distributing/
subprocess.check_call("twine upload dist/*", shell=True)
subprocess.check_call("git tag -a {0} -m 'version {0}'".format(__version__), shell=True)
subprocess.check_call("git push --tags", shell=True)
sys.exit()
# Clean way to add a custom "python setup.py <command>"
# Ref setup.py command extension : https://blog.niteoweb.com/setuptools-run-custom-code-in-setup-py/
class RosDevelopCommand(setuptools.Command):
"""Command to mutate this package to a ROS package, using its ROS release repository"""
description = "mutate this package to a ROS package using its release repository"
user_options = []
def initialize_options(self):
"""init options"""
# TODO : add distro selector
pass
def finalize_options(self):
"""finalize options"""
pass
def run(self):
# dynamic import for this command only to not need these in usual python case...
import git
import yaml
"""runner"""
repo_path = tempfile.mkdtemp(prefix='rosdevelop-' + os.path.dirname(__file__)) # TODO get actual package name ?
print("Getting ROS release repo in {0}...".format(repo_path))
# TODO : get release repo from ROSdistro
rosrelease_repo = git.Repo.clone_from('https://github.com/asmodehn/pyros-msgs-rosrelease.git', repo_path)
# Reset our working tree to master
origin = rosrelease_repo.remotes.origin
rosrelease_repo.remotes.origin.fetch() # assure we actually have data. fetch() returns useful information
# Setup a local tracking branch of a remote branch
rosrelease_repo.create_head('master', origin.refs.master).set_tracking_branch(origin.refs.master).checkout()
print("Reading tracks.yaml...")
with open(os.path.join(rosrelease_repo.working_tree_dir, 'tracks.yaml'), 'r') as tracks:
try:
tracks_dict = yaml.load(tracks)
except yaml.YAMLError as exc:
raise
patch_dir = tracks_dict.get('tracks', {}).get('indigo', {}).get('patches', {})
print("Found patches for indigo in {0}".format(patch_dir))
src_files = os.listdir(os.path.join(rosrelease_repo.working_tree_dir, patch_dir))
working_repo = git.Repo(os.path.dirname(os.path.abspath(__file__)))
# adding patched files to ignore list if needed (to prevent accidental commit of patch)
# => BETTER if the patch do not erase previous file. TODO : fix problem with both .travis.yml
with open(os.path.join(working_repo.working_tree_dir, '.gitignore'), 'a+') as gitignore:
skipit = []
for line in gitignore:
if line in src_files:
skipit += line
else: # not found, we are at the eof
for f in src_files:
if f not in skipit:
gitignore.write(f+'\n') # append missing data
working_repo.git.add(['.gitignore']) # adding .gitignore to the index so git applies it (and hide new files)
for file_name in src_files:
print("Patching {0}".format(file_name))
full_file_name = os.path.join(rosrelease_repo.working_tree_dir, patch_dir, file_name)
if os.path.isfile(full_file_name):
# Special case for package.xml and version template string
if file_name == 'package.xml':
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'package.xml'), "wt") as fout:
with open(full_file_name, "rt") as fin:
for line in fin:
fout.write(line.replace(':{version}', __version__)) # TODO: proper template engine ?
else:
shutil.copy(full_file_name, os.path.dirname(os.path.abspath(__file__)))
sys.exit()
class ROSPublishCommand(setuptools.Command):
"""Command to release this package to Pypi"""
description = "releases pyros-msgs to ROS"
user_options = []
def initialize_options(self):
"""init options"""
pass
def finalize_options(self):
"""finalize options"""
pass
def run(self):
"""runner"""
# TODO : distro from parameter. default : ['indigo', 'jade', 'kinetic']
subprocess.check_call("git tag -a ros-{0} -m 'version {0} for ROS'".format(__version__), shell=True)
subprocess.check_call("git push --tags", shell=True)
# TODO : guess the ROS package name
subprocess.check_call("bloom-release --rosdistro indigo --track indigo pyros_msgs", shell=True)
sys.exit()
setuptools.setup(name='pyros_msgs',
version=__version__,
description='Pyros messages and services definition',
url='http://github.com/asmodehn/pyros-msgs',
author='AlexV',
author_email='asmodehn@gmail.com',
license='MIT',
packages=[
'pyros_msgs',
'pyros_msgs.msg', # catkin build should generate it. python build can assume client uses rosimport.
'pyros_msgs.typecheck',
'pyros_msgs.opt_as_array',
'pyros_msgs.opt_as_nested',
],
package_data={
'': ['*.msg', '*.srv']
},
# this is better than using package data ( since behavior is a bit different from distutils... )
#include_package_data=True, # use MANIFEST.in during install.
# Reference for optional dependencies : http://stackoverflow.com/questions/4796936/does-pip-handle-extras-requires-from-setuptools-distribute-based-sources
install_requires=[
'six',
'pyyaml>=3.10', # genpy relies on this...
],
tests_require=[
'filefinder2',
'rosimport', # we rely on this for generating ROS message if necessary before importing
'pytest>=2.8.0', # as per hypothesis requirement (careful with 2.5.1 on trusty)
'pytest-xdist', # for --boxed (careful with the version it will be moved out of xdist)
'hypothesis>=3.0.1', # to target xenial LTS version
'numpy>=1.8.2', # from trusty version
],
cmdclass={
'prepare_release': PrepareReleaseCommand,
'publish': PublishCommand,
'rosdevelop': RosDevelopCommand,
'rospublish': ROSPublishCommand,
},
zip_safe=False, # TODO testing... including using rosimport to generate code from message definition...
)