Note: This tutorial assumes that you have completed the previous tutorials: ROS tutorials. |
Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags. |
Writing a ROS Python Makefile
Description: I know what you're saying: "Python... Makefile?" Believe it or not, you do need Makefile (and CMakeLists), even if you just hack Python all day.Tutorial Level: BEGINNER
Next Tutorial: Setting Up Your PYTHONPATH
This tutorial repeats information from earlier tutorials with a focus on the Makefiles. You can skip this tutorial unless you just need information about the makefile and CMakelists.txt.
Intro
These build files (CMakeLists.txt, Makefile) are fairly simple, but they provide important functionality such as:
- autogenerating message and service code
- running tests
The test functionality is especially important as we have the ability to run tests on your package plus every package that depends on it (rospack pkg test), which is very important for finding regressions prior to checkin.
Contents
These build files are not difficult to write. In fact, we have an automated tool that will create a all the necessary build files for you:
$ roscreate-pkg my_pkg rospy
This will create my_pkg and add in a dependency on rospy so that you can use in it your code.
If you want to write the necessary build files by hand, here's what you need:
Makefile
Your Makefile only needs to be one line:
mypackage/Makefile
include $(shell rospack find mk)/cmake.mk
This loads up our cmake definitions, which ensure our builds are cross-platform.
CMakeLists.txt
You also need to provide the CMakeLists file, which is also fairly simple. Here is an example for a Package that has Messages, Services, and tests:
mypackage/CMakeLists.txt
cmake_minimum_required(VERSION 2.4.6) include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake) rosbuild_init() rosbuild_genmsg() rosbuild_gensrv() rosbuild_add_rostest(test/talker-listener-test.launch)
Feel free to omit the rosbuild_genmsg/rosbuild_gensrv/rosbuild_add_rostest lines as appropriate.
With catkin, you only need a CMakefile, which will generate a Makefile in your buildfolder.
Your package.xml should contain the lines:
<buildtool_depend>catkin</buildtool_depend>
You can create a new package in your catkin workspace like this:
$ cd ~/catkin_ws/src $ catkin_create_pkg my_pkg message_generation rospy
This will create my_pkg and add in a dependency on rospy so that you can use in it your code. We use message_generation here for the second part of the tutorial, you only need this if your catkin packages defines new ROS messages or services.
Your CMakeLists.txt should at least contain the lines:
cmake_minimum_required(VERSION 2.8.3) project(my_pkg) find_package(catkin REQUIRED COMPONENTS message_generation rospy ...) catkin_package()
Messages and Services
To add Messages or Services, you need a few additions to your package.xml and CMakeLists.txt.
Your package.xml should contain the lines:
<build_depend>message_generation</build_depend>
This line will have no effect when message_generation exists outside your workspace, since message_generation is already installed. But for people using your package in the same workspace as message_generation will benefit from this line.
You should add the following runtime dependency:
<run_depend>message_runtime</run_depend>
Your CMakelists.txt should contain:
cmake_minimum_required(VERSION 2.8.3) project(my_pkg) find_package(catkin REQUIRED COMPONENTS message_generation rospy) add_message_files( FILES # e.g. Floats.msg HeaderString.msg ) add_service_files( DIRECTORY srv FILES AddTwoInts.srv BadTwoInts.srv ) ## Generate services in the 'srv' folder # add_service_files( # FILES # e.g. Floats.srv HeaderString.srv #) ## Generate added messages and services with any dependencies generate_messages() catkin_package( CATKIN_DEPENDS message_runtime )
Installing scripts and exporting modules
With catkin, ROS packages can have an install target. This makes it much easier for other package managers to automatically create installable packages than apt-get, e.g. also for MacOS, Arch, BSD, Windows, etc.
However the responsibility is with the developer to describe what needs to be installed, since it is generally bad if every file in your source tree is installed on other people's computers. So for python projects what we want to install are scripts, python modules for others to use as libraries (Other resources are currently not supported for declaration in setup.py, use CMakeLists.txt instead).
For the sake of this tutorial, create an example python package in the source folder:
$ cd ~/catkin_ws/src/my_pkg # new catkin package, in the workspace $ mkdir bin $ mkdir src $ mkdir src/tutorial_package $ touch src/tutorial_package/__init__.py
This defines a python package called tutorial_package. Usually, you should call the package the same as your catkin package (my_pkg in this case) to avoid confusion, but here we name them differently so that you see what goes where.
We also add a simple module and script to our project. Within my_pkg, create file src/tutorial_package/hello.py
def say(name): print('Hello ' + name)
And also create file bin/hello:
Make the file executable:
$ chmod u+x bin/hello
if we try to run the executable just like that, we get an import error:
$ bin/hello Traceback (most recent call last): File "bin/hello", line 3, in <module> import tutorial_package.hello ImportError: No module named tutorial_package.hello
One way to quickly fix this would be to change your PYTHONPATH, but that would only work for you. In order to make this work generally, you need to define an installation procedure that moves the files into the PYTHONPATH.
For python packages and scripts, catkin provides a cmake macro that gets this information from a file called setup.py.
So create a file called setup.py with these contents in the root of the my_pkg project:
1 ## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD
2
3 from distutils.core import setup
4 from catkin_pkg.python_setup import generate_distutils_setup
5
6 # fetch values from package.xml
7 setup_args = generate_distutils_setup(
8 packages=['tutorial_package'],
9 package_dir={'': 'src'},
10 )
11
12 setup(**setup_args)
Remember this: The setup.py is for catkin to use, and not for users to invoke. catkin will make sure setup.py installs files to the right location. If you invoke setup.py manually, you may break your ROS installation.
The first line shows we will be using distutils (setuptools is not recommended since it generates files into your src folder).
Then we use a function generate_distutils_setup to read out values from the package.xml, and also perform some convenience transformations, such as nicely listing all maintainers and authors in the package.xml.
So all we additionally have to provide is the information that is not in the package.xml, the name of the scripts you want to have installed, the name of the python packages, where to find those packages, and python package dependencies (Those can have different names than what we use in the package.xml, so we need a separate list).
In order for our setup.py to be used by catkin, we also need to uncomment this line in the CMakeLists.txt:
## Uncomment if the package has a setup.py catkin_python_setup()
Finally, for our script to be installed to the right location, if users install your package, this line is required in CMakeLists.txt:
catkin_install_python(PROGRAMS bin/hello DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
Now we can build our package:
$ cd ~/catkin_ws $ catkin_make
To setup the environment to include the new devel space:
$ . devel/setup.bash
Now your scripts and modules are available via rosrun, and will also be available If other people install your package.
$ rosrun my_pkg hello Hello my friend!
Also refer to ROS/Tutorials/CreatingMsgAndSrv for making message and service.
Next tutorial: Setting Up Your PYTHONPATH.