Merge lp:~ted/snapcraft/ros-catkin-plugin into lp:~snappy-dev/snapcraft/core

Proposed by Sergio Schvezov on 2015-10-01
Status: Merged
Approved by: Sergio Schvezov on 2015-10-16
Approved revision: 208
Merged at revision: 243
Proposed branch: lp:~ted/snapcraft/ros-catkin-plugin
Merge into: lp:~snappy-dev/snapcraft/core
Diff against target: 845 lines (+726/-0)
16 files modified
debian/control (+2/-0)
examples/ros/icon.svg (+1/-0)
examples/ros/snapcraft.yaml (+27/-0)
examples/ros/src/CMakeLists.txt (+47/-0)
examples/ros/src/beginner_tutorials/CMakeLists.txt (+39/-0)
examples/ros/src/beginner_tutorials/msg/Num.msg (+1/-0)
examples/ros/src/beginner_tutorials/package.xml (+42/-0)
examples/ros/src/beginner_tutorials/src/add_two_ints_client.cpp (+30/-0)
examples/ros/src/beginner_tutorials/src/add_two_ints_server.cpp (+23/-0)
examples/ros/src/beginner_tutorials/src/listener.cpp (+60/-0)
examples/ros/src/beginner_tutorials/src/talker.cpp (+87/-0)
examples/ros/src/beginner_tutorials/srv/AddTwoInts.srv (+4/-0)
snapcraft/meta.py (+3/-0)
snapcraft/plugins/catkin.py (+270/-0)
snapcraft/plugins/roscore.py (+83/-0)
snapcraft/yaml.py (+7/-0)
To merge this branch: bzr merge lp:~ted/snapcraft/ros-catkin-plugin
Reviewer Review Type Date Requested Status
Sergio Schvezov Approve on 2015-10-16
John Lenton 2015-10-01 Needs Fixing on 2015-10-01
Review via email: mp+273048@code.launchpad.net

Commit Message

Adding a catkin plugin

To post a comment you must log in.
lp:~ted/snapcraft/ros-catkin-plugin updated on 2015-10-01
184. By Ted Gould on 2015-10-01

Look elsewhere for deps

John Lenton (chipaca) wrote :

inline

review: Needs Fixing
John Lenton (chipaca) :
lp:~ted/snapcraft/ros-catkin-plugin updated on 2015-10-02
185. By Ted Gould on 2015-10-01

Switching to isolated under recommentation from the Erle folks

186. By Ted Gould on 2015-10-02

Build our own dependency system to ensure the packages can build correctly.

Leo Arias (elopio) wrote :

It's probably not a good idea to use config={} in the arguments of the methods because {} is mutable and it can cause some unexpected behaviors. See more info here: http://effbot.org/zone/default-values.htm
As the article suggest, it's better to use config=None, and then if config is None: config = {}.

Also, your python files are not passing the mccabe test:

./snapcraft/plugins/catkin.py:73:1: C901 'CatkinPlugin.find_package_deps' is too complex (10)
./snapcraft/plugins/catkin.py:128:1: C901 'CatkinPlugin.build' is too complex (13)

You should extract methods for the loops. The more the merrier.

lp:~ted/snapcraft/ros-catkin-plugin updated on 2015-10-02
187. By Ted Gould on 2015-10-02

Should use the default rosversion

188. By Sergio Schvezov on 2015-10-02

Handle not having stage-packages key

189. By Ted Gould on 2015-10-02

Fix the binary location to match make_isolated

Sergio Schvezov (sergiusens) wrote :

Hey good work on getting everything to this point. Seems like a really useful plugin. I do have some comments though.

I've been giving this a bit more of a look and I don't think we want config={} at all, parts should not have knowledge of config, it is what keeps them independent. We also don't want to have it write a services entry, that should be in the users control.

Last but not least, as elopio mentions, the code needs to be split out a bit.

review: Needs Fixing
Sergio Schvezov (sergiusens) wrote :

Also merge lp:~sergiusens/snapcraft/catkin which adds the roscore binaries

Ted Gould (ted) wrote :

On Fri, 2015-10-02 at 22:39 +0000, Sergio Schvezov wrote:

> I've been giving this a bit more of a look and I don't think we want
> config={} at all, parts should not have knowledge of config, it is
> what keeps them independent. We also don't want to have it write a
> services entry, that should be in the users control.

So, I actually think the opposite, we need it more or an actual step for
it.

I think that we do want the plugins to provide smarter generation of the
packages.yaml file, we can argue whether they should do services, but I
think we definitely want them to do frameworks and add capabilities on
individual binaries/services/etc. The case I'd mention is the QML
plugin. For QML to be useful it needs the Mir Framework and every binary
needs to have the mir_client capability. I don't think that we should
require the developer to understand those, just understand that he/she
needs to pull in QML. The QML plugin should add those onto the packages
file where it can.

Perhaps a solution for the services would be to provide a way to insert
a commented block into the packages.yaml file. So the plugin could see
if there was a ROS Master and if not throw some comments in saying "hey,
you might want these keys."

While for ROS I think this isn't as important, but as we start adding
things to the wiki you'd want to add postgres to your snap, not
configure postgres as a server in your snap. It seems that there needs
to be some way to have the service come along with the plugin.

lp:~ted/snapcraft/ros-catkin-plugin updated on 2015-10-05
190. By Ted Gould on 2015-10-02

Lint fixes

191. By Ted Gould on 2015-10-05

Restructuring code to pull XML parsing out of the file handling

192. By Ted Gould on 2015-10-05

Pulling the dependency resolution into its own function and using set logic to simplify

193. By Ted Gould on 2015-10-05

Making more sets instead of lists

194. By Ted Gould on 2015-10-05

Updates from Sergio

Ted Gould (ted) wrote :

So I think the complexity is taken care off and the other little comments.

I'm going to leave the *attr() ones because they match the other code, if we're going to change those I'm happy with that, but it should be one branch to change them all.

Wait for Sergio's comment/discussion on the config variable and how we want to handle that.

Ted Gould (ted) wrote :

Oh, also note, this push breaks the Erle Spider codebase as the switch to sets exposes some more dependency declaration that is needed in their codebase.

lp:~ted/snapcraft/ros-catkin-plugin updated on 2015-10-15
195. By Ted Gould on 2015-10-05

Change formatting of string to make it easier to read.

196. By Ted Gould on 2015-10-14

Update to trunk and roscore updates

197. By Ted Gould on 2015-10-14

Grab changes from roscore branch

198. By Ted Gould on 2015-10-15

Get schemas and other changes needed by the new plugins system

199. By Ted Gould on 2015-10-15

Drop 'config' parameters

200. By Ted Gould on 2015-10-15

Revert plugin.py changes

Sergio Schvezov (sergiusens) wrote :

I've done a first pass, the code looks really good, just a couple nits here and there.

One thing we haven't been doing and I started addressing was that all our methods our public and I'd make them private (prefix the method with _), this becomes more important now that we inherit from plugins instead of 'requiring' them.

Ted Gould (ted) wrote :

On Thu, 2015-10-15 at 23:44 +0000, Sergio Schvezov wrote:

> One thing we haven't been doing and I started addressing was that all our methods our public and I'd make them private (prefix the method with _), this becomes more important now that we inherit from plugins instead of 'requiring' them.

Fixed.

> > +binaries:
> > + listener:
> > + exec: opt/ros/indigo/beginner_tutorials/listener
> > + talker:
> > + exec: opt/ros/indigo/beginner_tutorials/talker
> > +
> > +parts:
> > + roscore:
> > + plugin: roscore
> > + catkin-tutorials:
> > + plugin: catkin
> > + source: .
> > + catkin-packages:
> > + - beginner_tutorials
> > +
> > +services:
>
> just a minor thing, but maybe put this upstairs (above) together with binaries

Fixed

> > + rosmaster:
> > + start: bin/roscore-rosmaster-service
> > + description: ROS Master Service
> > + ports:
>
> this can be dropped, I think it is being killed and nothing uses it so it can just go away to not confuse people (ports and everything under it)

Fixed, we should probably mark it deprecated in the schema then, no?

> > + def __init__(self, name, options):
> > + super().__init__(name, options)
> > + self.rosversion = options.rosversion
>
> the base class already saves options as self.options from way back and I guess we never noticed that, but maybe all these 'options' attribs don't need specific assigns.

Fixed.

> > + except lxml.etree.ParseError:
> > + logger.warning("Unable to read packages.xml file for '" + pkg + "'")
>
> use .format instead of + or maybe ('string %r' % pkg)

Fixed.

> > + return
>
> what are the consequences of this, even if handled internally maybe raising a RuntimeError is good here and logging from whoever calls this.

My thoughts here is that we can let the developer decide, if they care
that we can't parse it they can fix the file. If they don't care and
they just want to add the dependencies via stage-packages manually we
should allow that too. I kinda see packages.xml as nicety instead of a
requirement for a build.

> > +
> > + for deptype in ['buildtool_depend', 'build_depend', 'run_depend']:
>
> use () instead of [] here.

Fixed. But I don't understand why that would be better, it seems like a
list would always be less memory?

lp:~ted/snapcraft/ros-catkin-plugin updated on 2015-10-16
201. By Ted Gould on 2015-10-16

Making private functions private

202. By Ted Gould on 2015-10-16

Make directories us os.path.join()

203. By Ted Gould on 2015-10-16

Move services and drop port info

204. By Ted Gould on 2015-10-16

Don't cache the rosversion

205. By Ted Gould on 2015-10-16

Change string formatting

206. By Ted Gould on 2015-10-16

Set instead of a list

207. By Ted Gould on 2015-10-16

PEP8 is lame

lp:~ted/snapcraft/ros-catkin-plugin updated on 2015-10-16
208. By Ted Gould on 2015-10-16

Merge in trunk

Sergio Schvezov (sergiusens) wrote :

Great work! Great contribution!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2015-10-16 17:41:13 +0000
3+++ debian/control 2015-10-16 20:03:44 +0000
4@@ -9,6 +9,7 @@
5 python3 (>= 3.4),
6 python3-apt,
7 python3-jsonschema,
8+ python3-lxml,
9 python3-requests,
10 python3-setuptools,
11 python3-yaml,
12@@ -27,6 +28,7 @@
13 mercurial,
14 python3-apt,
15 python3-jsonschema,
16+ python3-lxml,
17 python3-requests,
18 python3-yaml,
19 sudo,
20
21=== added directory 'examples/ros'
22=== added file 'examples/ros/icon.svg'
23--- examples/ros/icon.svg 1970-01-01 00:00:00 +0000
24+++ examples/ros/icon.svg 2015-10-16 20:03:44 +0000
25@@ -0,0 +1,1 @@
26+<svg />
27
28=== added file 'examples/ros/snapcraft.yaml'
29--- examples/ros/snapcraft.yaml 1970-01-01 00:00:00 +0000
30+++ examples/ros/snapcraft.yaml 2015-10-16 20:03:44 +0000
31@@ -0,0 +1,27 @@
32+name: ros-example
33+version: 1.0
34+vendor: Number Five <number-five@is-alive.com>
35+summary: A robot example
36+icon: icon.svg
37+description: A really small ROS example
38+
39+binaries:
40+ listener:
41+ exec: opt/ros/indigo/beginner_tutorials/listener
42+ talker:
43+ exec: opt/ros/indigo/beginner_tutorials/talker
44+
45+services:
46+ rosmaster:
47+ start: bin/roscore-rosmaster-service
48+ description: ROS Master Service
49+
50+parts:
51+ roscore:
52+ plugin: roscore
53+ catkin-tutorials:
54+ plugin: catkin
55+ source: .
56+ catkin-packages:
57+ - beginner_tutorials
58+
59
60=== added directory 'examples/ros/src'
61=== added file 'examples/ros/src/CMakeLists.txt'
62--- examples/ros/src/CMakeLists.txt 1970-01-01 00:00:00 +0000
63+++ examples/ros/src/CMakeLists.txt 2015-10-16 20:03:44 +0000
64@@ -0,0 +1,47 @@
65+# toplevel CMakeLists.txt for a catkin workspace
66+# catkin/cmake/toplevel.cmake
67+
68+cmake_minimum_required(VERSION 2.8.3)
69+
70+# optionally provide a cmake file in the workspace to override arbitrary stuff
71+include(workspace.cmake OPTIONAL)
72+
73+set(CATKIN_TOPLEVEL TRUE)
74+
75+# include catkin directly or via find_package()
76+if(EXISTS "${CMAKE_SOURCE_DIR}/catkin/cmake/all.cmake" AND EXISTS "${CMAKE_SOURCE_DIR}/catkin/CMakeLists.txt")
77+ set(catkin_EXTRAS_DIR "${CMAKE_SOURCE_DIR}/catkin/cmake")
78+ # include all.cmake without add_subdirectory to let it operate in same scope
79+ include(catkin/cmake/all.cmake NO_POLICY_SCOPE)
80+ add_subdirectory(catkin)
81+
82+else()
83+ # use either CMAKE_PREFIX_PATH explicitly passed to CMake as a command line argument
84+ # or CMAKE_PREFIX_PATH from the environment
85+ if(NOT DEFINED CMAKE_PREFIX_PATH)
86+ if(NOT "$ENV{CMAKE_PREFIX_PATH}" STREQUAL "")
87+ string(REPLACE ":" ";" CMAKE_PREFIX_PATH $ENV{CMAKE_PREFIX_PATH})
88+ endif()
89+ endif()
90+
91+ # list of catkin workspaces
92+ set(catkin_search_path "")
93+ foreach(path ${CMAKE_PREFIX_PATH})
94+ if(EXISTS "${path}/.CATKIN_WORKSPACE")
95+ list(FIND catkin_search_path ${path} _index)
96+ if(_index EQUAL -1)
97+ list(APPEND catkin_search_path ${path})
98+ endif()
99+ endif()
100+ endforeach()
101+
102+ # search for catkin in all workspaces
103+ set(CATKIN_TOPLEVEL_FIND_PACKAGE TRUE)
104+ find_package(catkin REQUIRED
105+ NO_POLICY_SCOPE
106+ PATHS ${catkin_search_path}
107+ NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
108+ unset(CATKIN_TOPLEVEL_FIND_PACKAGE)
109+endif()
110+
111+catkin_workspace()
112
113=== added directory 'examples/ros/src/beginner_tutorials'
114=== added file 'examples/ros/src/beginner_tutorials/CMakeLists.txt'
115--- examples/ros/src/beginner_tutorials/CMakeLists.txt 1970-01-01 00:00:00 +0000
116+++ examples/ros/src/beginner_tutorials/CMakeLists.txt 2015-10-16 20:03:44 +0000
117@@ -0,0 +1,39 @@
118+# %Tag(FULLTEXT)%
119+cmake_minimum_required(VERSION 2.8.3)
120+project(beginner_tutorials)
121+
122+## Find catkin and any catkin packages
123+find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
124+
125+## Declare ROS messages and services
126+add_message_files(FILES Num.msg)
127+add_service_files(FILES AddTwoInts.srv)
128+
129+## Generate added messages and services
130+generate_messages(DEPENDENCIES std_msgs)
131+
132+## Declare a catkin package
133+catkin_package()
134+
135+## Build talker and listener
136+include_directories(include ${catkin_INCLUDE_DIRS})
137+
138+add_executable(talker src/talker.cpp)
139+target_link_libraries(talker ${catkin_LIBRARIES})
140+
141+add_executable(listener src/listener.cpp)
142+target_link_libraries(listener ${catkin_LIBRARIES})
143+
144+## Build service client and server
145+# %Tag(SRVCLIENT)%
146+add_executable(add_two_ints_server src/add_two_ints_server.cpp)
147+target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
148+add_dependencies(add_two_ints_server beginner_tutorials_gencpp)
149+
150+add_executable(add_two_ints_client src/add_two_ints_client.cpp)
151+target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
152+add_dependencies(add_two_ints_client beginner_tutorials_gencpp)
153+
154+# %EndTag(SRVCLIENT)%
155+
156+# %EndTag(FULLTEXT)%
157\ No newline at end of file
158
159=== added directory 'examples/ros/src/beginner_tutorials/msg'
160=== added file 'examples/ros/src/beginner_tutorials/msg/Num.msg'
161--- examples/ros/src/beginner_tutorials/msg/Num.msg 1970-01-01 00:00:00 +0000
162+++ examples/ros/src/beginner_tutorials/msg/Num.msg 2015-10-16 20:03:44 +0000
163@@ -0,0 +1,1 @@
164+int64 num
165
166=== added file 'examples/ros/src/beginner_tutorials/package.xml'
167--- examples/ros/src/beginner_tutorials/package.xml 1970-01-01 00:00:00 +0000
168+++ examples/ros/src/beginner_tutorials/package.xml 2015-10-16 20:03:44 +0000
169@@ -0,0 +1,42 @@
170+<?xml version="1.0"?>
171+<!-- %Tag(FULLTEXT,-1)% -->
172+<package>
173+ <!-- %Tag(NAME)% -->
174+ <name>beginner_tutorials</name>
175+ <!-- %EndTag(NAME)% -->
176+ <!-- %Tag(VERSION)% -->
177+ <version>0.1.0</version>
178+ <!-- %EndTag(VERSION)% -->
179+ <!-- %Tag(DESC)% -->
180+ <description>The beginner_tutorials package</description>
181+ <!-- %EndTag(DESC)% -->
182+
183+ <!-- %Tag(MAINTAINER)% -->
184+ <maintainer email="you@yourdomain.tld">Your Name</maintainer>
185+ <!-- %EndTag(MAINTAINER)% -->
186+ <!-- %Tag(LICENSE)% -->
187+ <license>BSD</license>
188+ <!-- %EndTag(LICENSE)% -->
189+ <!-- %Tag(URLS)% -->
190+ <url type="website">http://wiki.ros.org/beginner_tutorials</url>
191+ <!-- %EndTag(URLS)% -->
192+ <!-- %Tag(AUTHORS)% -->
193+ <author email="you@yourdomain.tld">Jane Doe</author>
194+ <!-- %EndTag(AUTHORS)% -->
195+
196+ <!-- %Tag(DEPS)% -->
197+ <buildtool_depend>catkin</buildtool_depend>
198+
199+ <build_depend>roscpp</build_depend>
200+ <build_depend>rospy</build_depend>
201+ <build_depend>std_msgs</build_depend>
202+
203+ <run_depend>roscpp</run_depend>
204+ <run_depend>rospy</run_depend>
205+ <run_depend>std_msgs</run_depend>
206+ <!-- %EndTag(DEPS)% -->
207+
208+ <!-- %Tag(EXPORT)% -->
209+ <!-- %EndTag(EXPORT)% -->
210+</package>
211+<!-- %EndTag(FULLTEXT)% -->
212\ No newline at end of file
213
214=== added directory 'examples/ros/src/beginner_tutorials/src'
215=== added file 'examples/ros/src/beginner_tutorials/src/add_two_ints_client.cpp'
216--- examples/ros/src/beginner_tutorials/src/add_two_ints_client.cpp 1970-01-01 00:00:00 +0000
217+++ examples/ros/src/beginner_tutorials/src/add_two_ints_client.cpp 2015-10-16 20:03:44 +0000
218@@ -0,0 +1,30 @@
219+#include "ros/ros.h"
220+#include "beginner_tutorials/AddTwoInts.h"
221+#include <cstdlib>
222+
223+int main(int argc, char **argv)
224+{
225+ ros::init(argc, argv, "add_two_ints_client");
226+ if (argc != 3)
227+ {
228+ ROS_INFO("usage: add_two_ints_client X Y");
229+ return 1;
230+ }
231+
232+ ros::NodeHandle n;
233+ ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
234+ beginner_tutorials::AddTwoInts srv;
235+ srv.request.a = atoll(argv[1]);
236+ srv.request.b = atoll(argv[2]);
237+ if (client.call(srv))
238+ {
239+ ROS_INFO("Sum: %ld", (long int)srv.response.sum);
240+ }
241+ else
242+ {
243+ ROS_ERROR("Failed to call service add_two_ints");
244+ return 1;
245+ }
246+
247+ return 0;
248+}
249
250=== added file 'examples/ros/src/beginner_tutorials/src/add_two_ints_server.cpp'
251--- examples/ros/src/beginner_tutorials/src/add_two_ints_server.cpp 1970-01-01 00:00:00 +0000
252+++ examples/ros/src/beginner_tutorials/src/add_two_ints_server.cpp 2015-10-16 20:03:44 +0000
253@@ -0,0 +1,23 @@
254+#include "ros/ros.h"
255+#include "beginner_tutorials/AddTwoInts.h"
256+
257+bool add(beginner_tutorials::AddTwoInts::Request &req,
258+ beginner_tutorials::AddTwoInts::Response &res)
259+{
260+ res.sum = req.a + req.b;
261+ ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
262+ ROS_INFO("sending back response: [%ld]", (long int)res.sum);
263+ return true;
264+}
265+
266+int main(int argc, char **argv)
267+{
268+ ros::init(argc, argv, "add_two_ints_server");
269+ ros::NodeHandle n;
270+
271+ ros::ServiceServer service = n.advertiseService("add_two_ints", add);
272+ ROS_INFO("Ready to add two ints.");
273+ ros::spin();
274+
275+ return 0;
276+}
277
278=== added file 'examples/ros/src/beginner_tutorials/src/listener.cpp'
279--- examples/ros/src/beginner_tutorials/src/listener.cpp 1970-01-01 00:00:00 +0000
280+++ examples/ros/src/beginner_tutorials/src/listener.cpp 2015-10-16 20:03:44 +0000
281@@ -0,0 +1,60 @@
282+#include "ros/ros.h"
283+#include "std_msgs/String.h"
284+
285+/**
286+ * This tutorial demonstrates simple receipt of messages over the ROS system.
287+ */
288+void chatterCallback(const std_msgs::String::ConstPtr& msg)
289+{
290+ ROS_INFO("I heard: [%s]", msg->data.c_str());
291+}
292+
293+int main(int argc, char **argv)
294+{
295+ /**
296+ * The ros::init() function needs to see argc and argv so that it can
297+ * perform any ROS arguments and name remapping that were provided
298+ * at the command line. For programmatic remappings you can use a
299+ * different version of init() which takes remappings directly, but
300+ * for most command-line programs, passing argc and argv is the easiest
301+ * way to do it. The third argument to init() is the name of the node.
302+ *
303+ * You must call one of the versions of ros::init() before using any other
304+ * part of the ROS system.
305+ */
306+ ros::init(argc, argv, "listener");
307+
308+ /**
309+ * NodeHandle is the main access point to communications with the
310+ * ROS system. The first NodeHandle constructed will fully initialize
311+ * this node, and the last NodeHandle destructed will close down the node.
312+ */
313+ ros::NodeHandle n;
314+
315+ /**
316+ * The subscribe() call is how you tell ROS that you want to receive
317+ * messages on a given topic. This invokes a call to the ROS master
318+ * node, which keeps a registry of who is publishing and who is subscribing.
319+ * Messages are passed to a callback function, here called chatterCallback.
320+ * subscribe() returns a Subscriber object that you must hold on to
321+ * until you want to unsubscribe. When all copies of the Subscriber
322+ * object go out of scope, this callback will automatically be
323+ * unsubscribed from this topic.
324+ *
325+ * The second parameter to the subscribe() function is the size of
326+ * the message queue. If messages are arriving faster than they are
327+ * being processed, this is the number of messages that will be
328+ * buffered up before beginning to throw away the oldest ones.
329+ */
330+ ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
331+
332+ /**
333+ * ros::spin() will enter a loop, pumping callbacks. With this
334+ * version, all callbacks will be called from within this thread
335+ * (the main one). ros::spin() will exit when Ctrl-C is pressed,
336+ * or the node is shutdown by the master.
337+ */
338+ ros::spin();
339+
340+ return 0;
341+}
342
343=== added file 'examples/ros/src/beginner_tutorials/src/talker.cpp'
344--- examples/ros/src/beginner_tutorials/src/talker.cpp 1970-01-01 00:00:00 +0000
345+++ examples/ros/src/beginner_tutorials/src/talker.cpp 2015-10-16 20:03:44 +0000
346@@ -0,0 +1,87 @@
347+#include "ros/ros.h"
348+#include "std_msgs/String.h"
349+
350+#include <sstream>
351+
352+/**
353+ * This tutorial demonstrates simple sending of messages over the ROS system.
354+ */
355+int main(int argc, char **argv)
356+{
357+ /**
358+ * The ros::init() function needs to see argc and argv so that it can
359+ * perform any ROS arguments and name remapping that were provided at
360+ * the command line. For programmatic remappings you can use a
361+ * different version of init() which takes remappings directly, but
362+ * for most command-line programs, passing argc and argv is the easiest
363+ * way to do it. The third argument to init() is the name of the node.
364+ *
365+ * You must call one of the versions of ros::init() before using any
366+ * other part of the ROS system.
367+ */
368+ ros::init(argc, argv, "talker");
369+
370+ /**
371+ * NodeHandle is the main access point to communications with the
372+ * ROS system. The first NodeHandle constructed will fully initialize
373+ * this node, and the last NodeHandle destructed will close down
374+ * the node.
375+ */
376+ ros::NodeHandle n;
377+
378+ /**
379+ * The advertise() function is how you tell ROS that you want to
380+ * publish on a given topic name. This invokes a call to the ROS
381+ * master node, which keeps a registry of who is publishing and who
382+ * is subscribing. After this advertise() call is made, the master
383+ * node will notify anyone who is trying to subscribe to this topic name,
384+ * and they will in turn negotiate a peer-to-peer connection with this
385+ * node. advertise() returns a Publisher object which allows you to
386+ * publish messages on that topic through a call to publish(). Once
387+ * all copies of the returned Publisher object are destroyed, the topic
388+ * will be automatically unadvertised.
389+ *
390+ * The second parameter to advertise() is the size of the message queue
391+ * used for publishing messages. If messages are published more quickly
392+ * than we can send them, the number here specifies how many messages to
393+ * buffer up before throwing some away.
394+ */
395+ ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
396+
397+ ros::Rate loop_rate(10);
398+
399+ /**
400+ * A count of how many messages we have sent. This is used to create
401+ * a unique string for each message.
402+ */
403+ int count = 0;
404+ while (ros::ok())
405+ {
406+ /**
407+ * This is a message object. You stuff it with data, and then publish it.
408+ */
409+ std_msgs::String msg;
410+
411+ std::stringstream ss;
412+ ss << "hello world " << count;
413+ msg.data = ss.str();
414+
415+ ROS_INFO("%s", msg.data.c_str());
416+
417+ /**
418+ * The publish() function is how you send messages. The parameter
419+ * is the message object. The type of this object must agree with the type
420+ * given as a template parameter to the advertise<>() call, as was done
421+ * in the constructor above.
422+ */
423+ chatter_pub.publish(msg);
424+
425+ ros::spinOnce();
426+
427+ loop_rate.sleep();
428+ ++count;
429+ }
430+
431+
432+ return 0;
433+}
434
435=== added directory 'examples/ros/src/beginner_tutorials/srv'
436=== added file 'examples/ros/src/beginner_tutorials/srv/AddTwoInts.srv'
437--- examples/ros/src/beginner_tutorials/srv/AddTwoInts.srv 1970-01-01 00:00:00 +0000
438+++ examples/ros/src/beginner_tutorials/srv/AddTwoInts.srv 2015-10-16 20:03:44 +0000
439@@ -0,0 +1,4 @@
440+int64 a
441+int64 b
442+---
443+int64 sum
444
445=== modified file 'snapcraft/meta.py'
446--- snapcraft/meta.py 2015-10-05 16:56:10 +0000
447+++ snapcraft/meta.py 2015-10-16 20:03:44 +0000
448@@ -16,6 +16,7 @@
449
450 import os
451 import logging
452+import re
453 import shlex
454 import shutil
455 import tempfile
456@@ -179,6 +180,8 @@
457
458 snap_dir = common.get_snapdir()
459 assembled_env = common.assemble_env().replace(snap_dir, '$SNAP_APP_PATH')
460+ replace_path = r'{}/.*/install'.format(common.get_partsdir())
461+ assembled_env = re.sub(replace_path, '$SNAP_APP_PATH', assembled_env)
462 script = ('#!/bin/sh\n' +
463 '{}\n'.format(assembled_env) +
464 '{}\n'.format(cwd) +
465
466=== added file 'snapcraft/plugins/catkin.py'
467--- snapcraft/plugins/catkin.py 1970-01-01 00:00:00 +0000
468+++ snapcraft/plugins/catkin.py 2015-10-16 20:03:44 +0000
469@@ -0,0 +1,270 @@
470+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
471+#
472+# Copyright © 2015 Canonical Ltd
473+#
474+# This program is free software: you can redistribute it and/or modify
475+# it under the terms of the GNU General Public License version 3 as
476+# published by the Free Software Foundation.
477+#
478+# This program is distributed in the hope that it will be useful,
479+# but WITHOUT ANY WARRANTY; without even the implied warranty of
480+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
481+# GNU General Public License for more details.
482+#
483+# You should have received a copy of the GNU General Public License
484+# along with this program. If not, see <http://www.gnu.org/licenses/>.
485+
486+import lxml.etree
487+import os
488+import tempfile
489+import logging
490+
491+import snapcraft
492+
493+logger = logging.getLogger(__name__)
494+
495+
496+class CatkinPlugin (snapcraft.BasePlugin):
497+
498+ _PLUGIN_STAGE_SOURCES = '''
499+deb http://packages.ros.org/ros/ubuntu/ trusty main
500+deb http://${prefix}.ubuntu.com/${suffix}/ trusty main universe
501+deb http://${prefix}.ubuntu.com/${suffix}/ trusty-updates main universe
502+deb http://${prefix}.ubuntu.com/${suffix}/ trusty-security main universe
503+deb http://${security}.ubuntu.com/${suffix} trusty-security main universe
504+'''
505+
506+ @classmethod
507+ def schema(cls):
508+ schema = super().schema()
509+ schema['properties']['rosversion'] = {
510+ 'type': 'string',
511+ 'default': 'indigo'
512+ }
513+ schema['properties']['catkin-packages'] = {
514+ 'type': 'array',
515+ 'minitems': 1,
516+ 'uniqueItems': True,
517+ 'items': {
518+ 'type': 'string'
519+ },
520+ 'default': [],
521+ }
522+
523+ schema['required'].append('catkin-packages')
524+
525+ return schema
526+
527+ def __init__(self, name, options):
528+ super().__init__(name, options)
529+ self.packages = set(options.catkin_packages)
530+ self.dependencies = ['ros-core']
531+ self.package_deps_found = False
532+ self.package_local_deps = {}
533+
534+ def env(self, root):
535+ return [
536+ 'PYTHONPATH={0}'.format(
537+ os.path.join(
538+ self.installdir,
539+ 'usr',
540+ 'lib',
541+ self.python_version,
542+ 'dist-packages')),
543+ 'DESTDIR={0}'.format(self.installdir),
544+ # ROS needs it but doesn't set it :-/
545+ 'CPPFLAGS="-std=c++11 $CPPFLAGS -I{0} -I{1}"'.format(
546+ os.path.join(root, 'usr', 'include', 'c++', self.gcc_version),
547+ os.path.join(root,
548+ 'usr',
549+ 'include',
550+ snapcraft.common.get_arch_triplet(),
551+ 'c++',
552+ self.gcc_version)),
553+ 'LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{0}/opt/ros/{1}/lib'.format(
554+ root,
555+ self.options.rosversion),
556+ 'ROS_MASTER_URI=http://localhost:11311',
557+ '_CATKIN_SETUP_DIR={}'.format(os.path.join(
558+ root, 'opt', 'ros', self.options.rosversion)),
559+ 'echo FOO=BAR\nif `test -e {0}` ; then\n. {0} ;\nfi\n'.format(
560+ os.path.join(
561+ root, 'opt', 'ros', self.options.rosversion, 'setup.sh'))
562+ ]
563+
564+ @property
565+ def python_version(self):
566+ return self.run_output(['pyversions', '-i'])
567+
568+ @property
569+ def gcc_version(self):
570+ return self.run_output(['gcc', '-dumpversion'])
571+
572+ @property
573+ def rosdir(self):
574+ return os.path.join(self.installdir,
575+ 'opt', 'ros', self.options.rosversion)
576+
577+ def _deps_from_packagesxml(self, f, pkg):
578+ try:
579+ tree = lxml.etree.parse(f)
580+ except lxml.etree.ParseError:
581+ logger.warning("Unable to read packages.xml file for '{}'".format(
582+ pkg))
583+ return
584+
585+ for deptype in ('buildtool_depend', 'build_depend', 'run_depend'):
586+ for xmldep in tree.xpath('/package/' + deptype):
587+ dep = xmldep.text
588+
589+ self.dependencies.append(dep)
590+
591+ # Make sure we're not providing the dep ourselves
592+ if dep in self.packages:
593+ self.package_local_deps[pkg].add(dep)
594+ continue
595+
596+ # If we're already getting this through a stage package,
597+ # we don't need it
598+ if self.options.stage_packages and (
599+ dep in self.options.stage_packages or
600+ dep.replace('_', '-') in self.options.stage_packages):
601+ continue
602+
603+ # Get the ROS package for it
604+ self.stage_packages.append(
605+ 'ros-'+self.options.rosversion+'-'+dep.replace('_', '-'))
606+
607+ if dep == 'roscpp':
608+ self.stage_packages.append('g++')
609+
610+ def _find_package_deps(self):
611+ if self.package_deps_found:
612+ return
613+
614+ # Look for a package definition and pull deps if there are any
615+ for pkg in self.packages:
616+ if pkg not in self.package_local_deps:
617+ self.package_local_deps[pkg] = set()
618+
619+ try:
620+ filename = os.path.join(
621+ self.builddir, 'src', pkg, 'package.xml')
622+ with open(filename, 'r') as f:
623+ self._deps_from_packagesxml(f, pkg)
624+ except os.FileNotFound:
625+ logger.warning("Unable to find packages.xml for '" + pkg + "'")
626+ pass
627+
628+ self.package_deps_found = True
629+
630+ def setup_stage_packages(self):
631+ if not self.handle_source_options():
632+ return False
633+
634+ self._find_package_deps()
635+
636+ return super().setup_stage_packages()
637+
638+ def _rosrun(self, commandlist, cwd=None):
639+ with tempfile.NamedTemporaryFile(mode='w') as f:
640+ f.write('set -ex\n')
641+ f.write('exec {}\n'.format(' '.join(commandlist)))
642+ f.flush()
643+
644+ return self.run(['/bin/bash', f.name], cwd=cwd)
645+
646+ def build(self):
647+ # Fixup ROS Cmake files that have hardcoded paths in them
648+ if not self.run([
649+ 'find', self.rosdir, '-name', '*.cmake',
650+ '-exec', 'sed', '-i', '-e',
651+ r's|\(\W\)/usr/lib/|\1{0}/usr/lib/|g'.format(self.installdir),
652+ '{}', ';'
653+ ]):
654+ return False
655+
656+ self._find_package_deps()
657+
658+ if not self._build_packages_deps():
659+ return False
660+
661+ # the hacks
662+ findcmd = ['find', self.installdir, '-name', '*.cmake', '-delete']
663+ if not self.run(findcmd):
664+ return False
665+
666+ if not self.run(
667+ ['rm', '-f',
668+ 'opt/ros/' + self.options.rosversion + '/.catkin',
669+ 'opt/ros/' + self.options.rosversion + '/.rosinstall',
670+ 'opt/ros/' + self.options.rosversion + '/setup.sh',
671+ 'opt/ros/' + self.options.rosversion + '/_setup_util.py'],
672+ cwd=self.installdir):
673+ return False
674+
675+ os.remove(os.path.join(self.installdir, 'usr/bin/xml2-config'))
676+
677+ return True
678+
679+ def _build_packages_deps(self):
680+ # Ugly dependency resolution, just loop through until we can
681+ # find something to build. When we do, build it. Loop until we
682+ # either can't build anything or we built everything.
683+ built = set()
684+ built_pkg = True
685+
686+ while len(built) < len(self.packages) and built_pkg:
687+ built_pkg = False
688+ for pkg in self.packages - built:
689+ if len(self.package_local_deps[pkg] - built) > 0:
690+ continue
691+
692+ if not self._handle_package(pkg):
693+ return False
694+
695+ built.add(pkg)
696+ built_pkg = True
697+
698+ if not built_pkg:
699+ return False
700+
701+ return True
702+
703+ def _handle_package(self, pkg):
704+ catkincmd = ['catkin_make_isolated']
705+
706+ catkincmd.append('--pkg')
707+ catkincmd.append(pkg)
708+
709+ # Define the location
710+ catkincmd.extend(['--directory', self.builddir])
711+
712+ # Start the CMake Commands
713+ catkincmd.append('--cmake-args')
714+
715+ # CMake directories
716+ catkincmd.append('-DCATKIN_DEVEL_PREFIX={}'.format(self.rosdir))
717+ catkincmd.append('-DCMAKE_INSTALL_PREFIX={}'.format(self.installdir))
718+
719+ # Dep CMake files
720+ for dep in self.dependencies:
721+ catkincmd.append('-D{0}_DIR={1}'.format(
722+ dep.replace('-', '_'),
723+ os.path.join(self.rosdir, 'share', dep, 'cmake')))
724+
725+ # Compiler fun
726+ catkincmd.extend([
727+ '-DCMAKE_C_FLAGS="$CFLAGS"',
728+ '-DCMAKE_CXX_FLAGS="$CPPFLAGS"',
729+ '-DCMAKE_LD_FLAGS="$LDFLAGS"',
730+ '-DCMAKE_C_COMPILER={}'.format(
731+ os.path.join(self.installdir, 'usr', 'bin', 'gcc')),
732+ '-DCMAKE_CXX_COMPILER={}'.format(
733+ os.path.join(self.installdir, 'usr', 'bin', 'g++'))
734+ ])
735+
736+ if not self._rosrun(catkincmd):
737+ return False
738+
739+ return True
740
741=== added file 'snapcraft/plugins/roscore.py'
742--- snapcraft/plugins/roscore.py 1970-01-01 00:00:00 +0000
743+++ snapcraft/plugins/roscore.py 2015-10-16 20:03:44 +0000
744@@ -0,0 +1,83 @@
745+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
746+#
747+# Copyright © 2015 Canonical Ltd
748+#
749+# This program is free software: you can redistribute it and/or modify
750+# it under the terms of the GNU General Public License version 3 as
751+# published by the Free Software Foundation.
752+#
753+# This program is distributed in the hope that it will be useful,
754+# but WITHOUT ANY WARRANTY; without even the implied warranty of
755+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
756+# GNU General Public License for more details.
757+#
758+# You should have received a copy of the GNU General Public License
759+# along with this program. If not, see <http://www.gnu.org/licenses/>.
760+
761+import snapcraft
762+import os
763+
764+
765+class RosCorePlugin(snapcraft.BasePlugin):
766+
767+ _PLUGIN_STAGE_SOURCES = '''
768+deb http://packages.ros.org/ros/ubuntu/ trusty main
769+deb http://${prefix}.ubuntu.com/${suffix}/ trusty main universe
770+deb http://${prefix}.ubuntu.com/${suffix}/ trusty-updates main universe
771+deb http://${prefix}.ubuntu.com/${suffix}/ trusty-security main universe
772+deb http://${security}.ubuntu.com/${suffix} trusty-security main universe
773+'''
774+
775+ @classmethod
776+ def schema(cls):
777+ return {
778+ '$schema': 'http://json-schema.org/draft-04/schema#',
779+ 'type': 'object',
780+ 'properties': {
781+ 'rosversion': {
782+ 'type': 'string',
783+ 'default': 'indigo'
784+ },
785+ }
786+ }
787+
788+ def __init__(self, name, options):
789+ super().__init__(name, options)
790+ self.stage_packages.append(
791+ 'ros-' + self.options.rosversion + '-ros-core'
792+ )
793+
794+ def build(self):
795+ os.makedirs(os.path.join(self.installdir, 'bin'), exist_ok=True)
796+ ros_dir = os.path.join(
797+ '$SNAP_APP_PATH',
798+ 'opt',
799+ 'ros',
800+ self.options.rosversion)
801+ service_wrapper = os.path.join(
802+ self.installdir, 'bin', '{}-rosmaster-service'.format(self.name))
803+
804+ with open(os.path.join(service_wrapper), 'w') as f:
805+ f.write('#!/bin/bash\n')
806+ f.write('_CATKIN_SETUP_DIR={}\n'.format(ros_dir))
807+ f.write('source {}/setup.bash\n'.format(ros_dir))
808+ f.write('export PYTHONPATH={}:$PYTHONPATH\n'.format(
809+ os.path.join(ros_dir, 'lib', 'python2.7', 'dist-packages')))
810+ f.write('exec {}/bin/rosmaster\n'.format(ros_dir))
811+
812+ os.chmod(service_wrapper, 0o755)
813+ return True
814+
815+ def snap_fileset(self):
816+ rospath = os.path.join('opt', 'ros', self.options.rosversion)
817+ return ([
818+ os.path.join('bin', self.name + '-rosmaster-service'),
819+ os.path.join(rospath, 'bin', '*'),
820+ os.path.join(rospath, 'lib', '*'),
821+ '-' + os.path.join(rospath, 'share', '*', 'cmake', '*'),
822+ '-' + os.path.join(rospath, 'include'),
823+ '-' + os.path.join(rospath, '.catkin'),
824+ '-' + os.path.join(rospath, '.rosinstall'),
825+ '-' + os.path.join(rospath, 'setup.sh'),
826+ '-' + os.path.join(rospath, '_setup_util.py')
827+ ])
828
829=== modified file 'snapcraft/yaml.py'
830--- snapcraft/yaml.py 2015-10-15 22:08:19 +0000
831+++ snapcraft/yaml.py 2015-10-16 20:03:44 +0000
832@@ -219,6 +219,13 @@
833 '-I{0}/usr/include/{1}',
834 '$CFLAGS'
835 ]).format(root, snapcraft.common.get_arch_triplet()) + '"')
836+ env.append('CPPFLAGS="' + ' '.join([
837+ '-I{0}/include',
838+ '-I{0}/usr/include',
839+ '-I{0}/include/{1}',
840+ '-I{0}/usr/include/{1}',
841+ '$CPPFLAGS'
842+ ]).format(root, snapcraft.common.get_arch_triplet()) + '"')
843 env.append('LDFLAGS="' + ' '.join([
844 '-L{0}/lib',
845 '-L{0}/usr/lib',

Subscribers

People subscribed via source and target branches

to all changes: