diff --git a/android.toolchain.cmake b/android.toolchain.cmake new file mode 100644 index 0000000..8e44702 --- /dev/null +++ b/android.toolchain.cmake @@ -0,0 +1,17 @@ + +if(ANDROID_NDK_TOOLCHAIN_INCLUDED) + return() +endif(ANDROID_NDK_TOOLCHAIN_INCLUDED) +include($ENV{ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake) + +## Build without debug symbols in Release mode and add RelWithDebInfo mode. +foreach(_var ANDROID_COMPILER_FLAGS CMAKE_C_FLAGS CMAKE_CXX_FLAGS CMAKE_ASM_FLAGS) + string(REPLACE "-g " "" ${_var} "${${_var}}") + set(${_var}_DEBUG "-g ${${_var}_DEBUG}") + set(${_var}_RELWITHDEBINFO "-g ${${_var}_RELEASE}") +endforeach() +unset(_var) + +if(ANDROID_TOOLCHAIN STREQUAL clang) + list(APPEND ANDROID_COMPILER_FLAGS_RELWITHDEBINFO -fno-limit-debug-info) +endif() diff --git a/apply_patches.sh b/apply_patches.sh new file mode 100755 index 0000000..ec6fe1b --- /dev/null +++ b/apply_patches.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Abort script on any failures +set -e + +my_loc="$(cd "$(dirname $0)" && pwd)" +source $my_loc/config.sh +source $my_loc/utils.sh + +if [ $# != 2 ] || [ $1 == '-h' ] || [ $1 == '--help' ]; then + echo "Usage: $0 patch_prefix output_prefix" + echo " example: $0 /home/user/ros_android/patches /home/user/my_workspace/output" + exit 1 +fi + +patch_prefix=$1 +output_prefix=$2 + +echo +echo -e '\e[34mApplying patches.\e[39m' +echo + +for patch_file in $patch_prefix/*.patch; do + apply_patch $patch_file -d $output_prefix +done diff --git a/build_cpp.sh b/build_catkin_workspace.sh similarity index 97% rename from build_cpp.sh rename to build_catkin_workspace.sh index fe067ed..4bdcea8 100755 --- a/build_cpp.sh +++ b/build_catkin_workspace.sh @@ -86,7 +86,7 @@ if [ "$WORKSPACE" = "" ]; then exit 1 fi -: ${RBA_TOOLCHAIN:=$ANDROID_NDK/build/cmake/android.toolchain.cmake} +: ${RBA_TOOLCHAIN:=$my_loc/android.toolchain.cmake} # get the prefix path prefix=$(cd $PREFIX_PATH && pwd) diff --git a/config.sh b/config.sh index ee8075a..5971111 100644 --- a/config.sh +++ b/config.sh @@ -6,7 +6,3 @@ export ANDROID_PLATFORM=android-24 # Enable this value for debug build #CMAKE_BUILD_TYPE=Debug CMAKE_BUILD_TYPE=Release - -# Enable this if you need to use pluginlib in Android. -# The plugins will be statically linked -use_pluginlib=1 diff --git a/do_everything.sh b/do_everything.sh index 2b0b746..f5c5c9b 100755 --- a/do_everything.sh +++ b/do_everything.sh @@ -105,246 +105,43 @@ mkdir -p $prefix/libs export TARGET_DIR=$prefix/target [ -d $TARGET_DIR ] || mkdir -p $TARGET_DIR -# Get the android ndk build helper script -# If file doesn't exist, then download and patch it -#if ! [ -e $prefix/android.toolchain.cmake ]; then -# cd $prefix -# download 'https://raw.githubusercontent.com/taka-no-me/android-cmake/556cc14296c226f753a3778d99d8b60778b7df4f/android.toolchain.cmake' -# patch -p0 -N -d $prefix < $my_loc/patches/android.toolchain.cmake.patch -# cat $my_loc/files/android.toolchain.cmake.addendum >> $prefix/android.toolchain.cmake -#fi +export RBA_TOOLCHAIN=$my_loc/android.toolchain.cmake -export RBA_TOOLCHAIN=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -apply_patch $my_loc/patches/android.toolchain.cmake.patch -d $ANDROID_NDK_HOME/build/cmake - -# Now get boost with a specialized build -[ -d $prefix/libs/boost ] || run_cmd get_library boost $prefix/libs -[ -d $prefix/libs/bzip2 ] || run_cmd get_library bzip2 $prefix/libs -[ -d $prefix/libs/uuid ] || run_cmd get_library uuid $prefix/libs -[ -d $prefix/libs/poco-1.8.0 ] || run_cmd get_library poco $prefix/libs -[ -d $prefix/libs/tinyxml ] || run_cmd get_library tinyxml $prefix/libs -[ -d $prefix/libs/tinyxml2 ] || run_cmd get_library tinyxml2 $prefix/libs -[ -d $prefix/libs/console_bridge ] || run_cmd get_library console_bridge $prefix/libs -[ -d $prefix/libs/lz4-r131 ] || run_cmd get_library lz4 $prefix/libs -[ -d $prefix/libs/curl-7.47.0 ] || run_cmd get_library curl $prefix/libs -[ -d $prefix/libs/urdfdom/ ] || run_cmd get_library urdfdom $prefix/libs -[ -d $prefix/libs/urdfdom_headers ] || run_cmd get_library urdfdom_headers $prefix/libs -[ -d $prefix/libs/libiconv-1.15 ] || run_cmd get_library libiconv $prefix/libs -[ -d $prefix/libs/libxml2-2.9.7 ] || run_cmd get_library libxml2 $prefix/libs -[ -d $prefix/libs/collada_dom ] || run_cmd get_library collada_dom $prefix/libs -[ -d $prefix/libs/eigen-3.3.5 ] || run_cmd get_library eigen $prefix/libs -[ -d $prefix/libs/assimp-3.1.1 ] || run_cmd get_library assimp $prefix/libs -[ -d $prefix/libs/qhull-2015.2 ] || run_cmd get_library qhull $prefix/libs -[ -d $prefix/libs/yaml-cpp-yaml-cpp-0.6.2 ] || run_cmd get_library yaml-cpp $prefix/libs -[ -d $prefix/libs/flann ] || run_cmd get_library flann $prefix/libs -[ -d $prefix/libs/pcl-pcl-1.8.1 ] || run_cmd get_library pcl $prefix/libs -[ -d $prefix/libs/bullet ] || run_cmd get_library bullet $prefix/libs -[ -d $prefix/libs/SDL-1.2.15 ] || run_cmd get_library sdl $prefix/libs -[ -d $prefix/libs/SDL_image ] || run_cmd get_library sdl-image $prefix/libs -[ -d $prefix/libs/libogg-1.3.3 ] || run_cmd get_library ogg $prefix/libs -[ -d $prefix/libs/libvorbis-1.3.6 ] || run_cmd get_library vorbis $prefix/libs -[ -d $prefix/libs/libtheora-1.1.1 ] || run_cmd get_library theora $prefix/libs - -# get rospkg dependency for pluginlib support at build time -[ -d $my_loc/files/rospkg ] || run_cmd get_library rospkg $my_loc/files +# Get all library dependencies. +run_cmd get_system_dependencies $my_loc/system_deps.rosinstall $prefix/libs $my_loc/files echo echo -e '\e[34mGetting ROS packages\e[39m' echo if [[ $skip -ne 1 ]] ; then - run_cmd get_ros_stuff $prefix - - echo - echo -e '\e[34mApplying patches.\e[39m' - echo - - # patch CMakeLists.txt for lz4 library - Build as a library - apply_patch $my_loc/patches/lz4.patch - - # patch rosbag_storage - Fix static linking due to missing BZIP2 dependency - apply_patch $my_loc/patches/rosbag_storage.patch - - # Patch collada - Build as static lib - apply_patch $my_loc/patches/collada_dom.patch - - # Patch assimp - Build as static lib - apply_patch $my_loc/patches/assimp.patch - - # Patch console_bridge - Disable unit tests (unsatisfied dependencies) - apply_patch $my_loc/patches/console_bridge.patch - - # Patch urdfdom - Build as static lib - apply_patch $my_loc/patches/urdfdom.patch - - # Patch qhull - Don't install shared libraries - # TODO: Remove shared libraries to avoid hack in parse_libs.py - # apply_patch /opt/roscpp_android/patches/qhull.patch - - # Patch bfl - Build as static lib - apply_patch $my_loc/patches/bfl.patch - - # Patch orocos_kdl - Build as static lib - apply_patch $my_loc/patches/orocos_kdl.patch - - # Patch PCL - Disable optionals. - apply_patch $my_loc/patches/pcl-1.8.1.patch - - # Patch uuid - Avoiding stdlib.h include - apply_patch $my_loc/patches/uuid.patch - - # Patch yaml - Avoid building tests - apply_patch $my_loc/patches/yaml-cpp.patch - - # Patch bullet - Avoid building examples - apply_patch $my_loc/patches/bullet.patch - - ## ROS patches - - # Patch rosconsole - Add android backend - apply_patch $my_loc/patches/rosconsole.patch - - # Patch catkin - Fix transitive linking of interface libraries for static builds - apply_patch $my_loc/patches/catkin.patch - - # Patch map_server - Fix find yaml - apply_patch $my_loc/patches/map_server.patch - - # Patch bondcpp - Fix transitive linking problems - apply_patch $my_loc/patches/bondcpp.patch - - # Patch image_publisher - Fix linking problems, transitive linking, - # and changed shared to static library building. - apply_patch $my_loc/patches/image_publisher.patch - - # Patch image_rotate - Fix find opencv and transitive linking problem - apply_patch $my_loc/patches/image_rotate.patch - - # Patch opencv - Fix installation path - apply_patch $my_loc/patches/opencv.patch - - # Patch actionlib - problems with Boost changes. - apply_patch $my_loc/patches/actionlib.patch - - # Patch rospack - problems with Boost changes - # Also emptied some unnecessary functions to avoid problems related to including Python. - apply_patch $my_loc/patches/rospack.patch - - # Patch xmlrpcpp - problems with Boost changes. - apply_patch $my_loc/patches/xmlrpcpp.patch - - # Patch roslib - weird issue with rospack. - # TODO: Need to look further (only on catkin_make_isolated) - # apply_patch /opt/roscpp_android/patches/roslib.patch - - # Patch collada_parser - cmake detects mkstemps even though Android does not support it - # TODO: investigate how to prevent cmake to detect system mkstemps -# apply_patch $my_loc/patches/collada_parser.patch - - # Patch laser_assembler - Remove testing for Android - # TODO: It seems like there may be a better way to handle the test issues - # http://stackoverflow.com/questions/22055741/googletest-for-android-ndk - # apply_patch $my_loc/patches/laser_assembler.patch - - # Patch laser_filters - Remove testing for Android - # TODO: It seems like there may be a better way to handle the test issues - # http://stackoverflow.com/questions/22055741/googletest-for-android-ndk - # https://source.android.com/reference/com/android/tradefed/testtype/GTest.html - # apply_patch $my_loc/patches/laser_filters.patch - - # Patch camera_info_manager - remove testing for Android - # TODO: It seems like there may be a better way to handle the test issues - # http://stackoverflow.com/questions/22055741/googletest-for-android-ndk - # https://source.android.com/reference/com/android/tradefed/testtype/GTest.html - apply_patch $my_loc/patches/camera_info_manager.patch - - # Patch camera_calibration_parsers - deleted python things and solved problem finding Boost. - apply_patch $my_loc/patches/camera_calibration_parsers.patch - - # Patch cv_bridge - fix transitive linking in cv_bridge-extras.cmake - apply_patch $my_loc/patches/cv_bridge.patch - - # Patch theora_image_transport - fix transitive linking - apply_patch $my_loc/patches/theora_image_transport.patch - - # Patch robot_pose_ekf - Add bfl library cmake variables, also, remove tests - # TODO: The correct way to handle this would be to create .cmake files for bfl and do a findpackage(orocos-bfl) - # apply_patch $my_loc/patches/robot_pose_ekf.patch - - # Patch robot_state_publisher - Add ARCHIVE DESTINATION - # TODO: Create PR to add ARCHIVE DESTINATION - apply_patch $my_loc/patches/robot_state_publisher.patch - - # Patch moveit_core - Add fcl library cmake variables - # TODO: The correct way to handle this would be to create .cmake files for fcl and do a findpackage(fcl) - #apply_patch $my_loc/patches/moveit_core.patch - - # Patch moveit_core plugins - Add ARCHIVE DESTINATION - # TODO: PR merged: https://github.com/ros-planning/moveit_core/pull/251 - # Wait for next release to remove (current 0.6.15) - #apply_patch $my_loc/patches/moveit_core_plugins.patch - - # Patch camera_calibration_parsers - Fix yaml-cpp dependency - # TODO: PR created: https://github.com/ros-perception/image_common/pull/36 - # apply_patch $my_loc/patches/camera_calibration_parsers.patch - - # Patch image_view - Solved YAML linking problems, and transitive linking. - apply_patch $my_loc/patches/image_view.patch - - # Patch depth_image_proc - Solved transitive linking problems - apply_patch $my_loc/patches/depth_image_proc.patch - - # Patch urdf - Fixed linking with pluginlib and dependencies in downstream packages - apply_patch $my_loc/patches/urdf.patch - - # Patch move_base - Solved transitive linking problems - apply_patch $my_loc/patches/move_base.patch - - # Patch global_planner - Add angles dependency - # TODO: PR merged: https://github.com/ros-planning/navigation/pull/359 - # Wait for next release to remove (current 1.12.4) - # apply_patch $my_loc/patches/global_planner.patch - - #Patch Poco lib - apply_patch $my_loc/patches/poco.patch - - # Plugin specific patches - if [ $use_pluginlib -ne 0 ]; then - # Patch pluginlib for static loading - apply_patch $my_loc/patches/pluginlib.patch - # apply_patch image_transport # to fix faulty export plugins - apply_patch $my_loc/patches/image_transport.patch - fi - - ## Demo Application specific patches - + run_cmd get_catkin_packages $prefix + run_cmd apply_patches $my_loc/patches $prefix fi # Before build # Search packages that depend on pluginlib and generate plugin loader. -if [ $use_pluginlib -ne 0 ]; then - echo - echo -e '\e[34mBuilding pluginlib support...\e[39m' - echo +echo +echo -e '\e[34mBuilding pluginlib support...\e[39m' +echo - pluginlib_helper_file=pluginlib_helper.cpp - $my_loc/files/pluginlib_helper/pluginlib_helper.py -scanroot $prefix/catkin_ws/src $user_workspace -cppout $my_loc/files/pluginlib_helper/$pluginlib_helper_file - cp $my_loc/files/pluginlib_helper/$pluginlib_helper_file $prefix/catkin_ws/src/pluginlib/src/ - line="add_library(pluginlib STATIC src/pluginlib_helper.cpp)" - # temporally turn off error detection - set +e - grep "$line" $prefix/catkin_ws/src/pluginlib/CMakeLists.txt - # if line is not already added, then add it to the pluginlib cmake - if [ $? -ne 0 ]; then - # backup the file - cp $prefix/catkin_ws/src/pluginlib/CMakeLists.txt $prefix/catkin_ws/src/pluginlib/CMakeLists.txt.bak - sed -i '/INCLUDE_DIRS include/a LIBRARIES ${PROJECT_NAME}' $prefix/catkin_ws/src/pluginlib/CMakeLists.txt - echo -e "\n"$line >> $prefix/catkin_ws/src/pluginlib/CMakeLists.txt - echo 'install(TARGETS pluginlib RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION})' >> $prefix/catkin_ws/src/pluginlib/CMakeLists.txt - fi - # turn error detection back on - set -e +pluginlib_helper_file=pluginlib_helper.cpp +$my_loc/files/pluginlib_helper/pluginlib_helper.py -scanroot $prefix/catkin_ws/src $user_workspace -cppout $my_loc/files/pluginlib_helper/$pluginlib_helper_file +cp $my_loc/files/pluginlib_helper/$pluginlib_helper_file $prefix/catkin_ws/src/pluginlib/src/ +line="add_library(pluginlib STATIC src/pluginlib_helper.cpp)" +# temporally turn off error detection +set +e +grep "$line" $prefix/catkin_ws/src/pluginlib/CMakeLists.txt +# if line is not already added, then add it to the pluginlib cmake +if [ $? -ne 0 ]; then + # backup the file + cp $prefix/catkin_ws/src/pluginlib/CMakeLists.txt $prefix/catkin_ws/src/pluginlib/CMakeLists.txt.bak + sed -i '/INCLUDE_DIRS include/a LIBRARIES ${PROJECT_NAME}' $prefix/catkin_ws/src/pluginlib/CMakeLists.txt + echo -e "\n"$line >> $prefix/catkin_ws/src/pluginlib/CMakeLists.txt + echo 'install(TARGETS pluginlib RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION})' >> $prefix/catkin_ws/src/pluginlib/CMakeLists.txt fi +# turn error detection back on +set -e echo echo -e '\e[34mBuilding library dependencies.\e[39m' @@ -354,29 +151,29 @@ echo [ -f $TARGET_DIR/lib/libbz2.a ] || run_cmd build_library bzip2 $prefix/libs/bzip2 [ -f $TARGET_DIR/lib/libuuid.a ] || run_cmd build_library uuid $prefix/libs/uuid [ -f $TARGET_DIR/lib/libboost_system.a ] || run_cmd copy_boost $prefix/libs/boost -[ -f $TARGET_DIR/lib/libPocoFoundation.a ] || run_cmd build_library_with_toolchain poco $prefix/libs/poco-1.8.0 +[ -f $TARGET_DIR/lib/libPocoFoundation.a ] || run_cmd build_library_with_toolchain poco $prefix/libs/poco [ -f $TARGET_DIR/lib/libtinyxml.a ] || run_cmd build_library tinyxml $prefix/libs/tinyxml [ -f $TARGET_DIR/lib/libtinyxml2.a ] || run_cmd build_library tinyxml2 $prefix/libs/tinyxml2 [ -f $TARGET_DIR/lib/libconsole_bridge.a ] || run_cmd build_library console_bridge $prefix/libs/console_bridge -[ -f $TARGET_DIR/lib/liblz4.a ] || run_cmd build_library lz4 $prefix/libs/lz4-r131/cmake_unofficial -[ -f $TARGET_DIR/lib/libcurl.a ] || run_cmd build_library_with_toolchain curl $prefix/libs/curl-7.47.0 +[ -f $TARGET_DIR/lib/liblz4.a ] || run_cmd build_library lz4 $prefix/libs/lz4/cmake_unofficial +[ -f $TARGET_DIR/lib/libcurl.a ] || run_cmd build_library_with_toolchain curl $prefix/libs/curl [ -f $TARGET_DIR/include/urdf_model/model.h ] || run_cmd build_library urdfdom_headers $prefix/libs/urdfdom_headers [ -f $TARGET_DIR/lib/liburdfdom_model.a ] || run_cmd build_library urdfdom $prefix/libs/urdfdom -[ -f $TARGET_DIR/lib/libiconv.a ] || run_cmd build_library_with_toolchain libiconv $prefix/libs/libiconv-1.15 -[ -f $TARGET_DIR/lib/libxml2.a ] || run_cmd build_library_with_toolchain libxml2 $prefix/libs/libxml2-2.9.7 +[ -f $TARGET_DIR/lib/libiconv.a ] || run_cmd build_library_with_toolchain libiconv $prefix/libs/libiconv +[ -f $TARGET_DIR/lib/libxml2.a ] || run_cmd build_library_with_toolchain libxml2 $prefix/libs/libxml2 [ -f $TARGET_DIR/lib/libcollada-dom2.4-dp.a ] || run_cmd build_library collada_dom $prefix/libs/collada_dom -[ -f $TARGET_DIR/lib/libassimp.a ] || run_cmd build_library assimp $prefix/libs/assimp-3.1.1 -[ -f $TARGET_DIR/include/eigen3/signature_of_eigen3_matrix_library ] || run_cmd build_library eigen $prefix/libs/eigen-3.3.5 -[ -f $TARGET_DIR/lib/libqhullstatic.a ] || run_cmd build_library qhull $prefix/libs/qhull-2015.2 -[ -f $TARGET_DIR/lib/libyaml-cpp.a ] || run_cmd build_library yaml-cpp $prefix/libs/yaml-cpp-yaml-cpp-0.6.2 +[ -f $TARGET_DIR/lib/libassimp.a ] || run_cmd build_library assimp $prefix/libs/assimp +[ -f $TARGET_DIR/include/eigen3/signature_of_eigen3_matrix_library ] || run_cmd build_library eigen $prefix/libs/eigen +[ -f $TARGET_DIR/lib/libqhullstatic.a ] || run_cmd build_library qhull $prefix/libs/qhull +[ -f $TARGET_DIR/lib/libyaml-cpp.a ] || run_cmd build_library yaml-cpp $prefix/libs/yaml-cpp [ -f $TARGET_DIR/lib/libflann_cpp_s.a ] || run_cmd build_library flann $prefix/libs/flann -[ -f $TARGET_DIR/lib/libpcl_common.a ] || run_cmd build_library pcl $prefix/libs/pcl-pcl-1.8.1 +[ -f $TARGET_DIR/lib/libpcl_common.a ] || run_cmd build_library pcl $prefix/libs/pcl [ -f $TARGET_DIR/lib/libBulletSoftBody.a ] || run_cmd build_library bullet $prefix/libs/bullet -[ -f $TARGET_DIR/lib/libSDL.a ] || run_cmd build_library_with_toolchain sdl $prefix/libs/SDL-1.2.15 -[ -f $TARGET_DIR/lib/libSDL_image.a ] || run_cmd build_library_with_toolchain sdl-image $prefix/libs/SDL_image -[ -f $TARGET_DIR/lib/libogg.a ] || run_cmd build_library_with_toolchain ogg $prefix/libs/libogg-1.3.3 -[ -f $TARGET_DIR/lib/libvorbis.a ] || run_cmd build_library_with_toolchain vorbis $prefix/libs/libvorbis-1.3.6 -[ -f $TARGET_DIR/lib/libtheora.a ] || run_cmd build_library_with_toolchain theora $prefix/libs/libtheora-1.1.1 +[ -f $TARGET_DIR/lib/libSDL.a ] || run_cmd build_library_with_toolchain sdl $prefix/libs/sdl +[ -f $TARGET_DIR/lib/libSDL_image.a ] || run_cmd build_library_with_toolchain sdl-image $prefix/libs/sdl-image +[ -f $TARGET_DIR/lib/libogg.a ] || run_cmd build_library_with_toolchain ogg $prefix/libs/ogg +[ -f $TARGET_DIR/lib/libvorbis.a ] || run_cmd build_library_with_toolchain vorbis $prefix/libs/vorbis +[ -f $TARGET_DIR/lib/libtheora.a ] || run_cmd build_library_with_toolchain theora $prefix/libs/theora echo echo -e '\e[34mCross-compiling ROS.\e[39m' @@ -385,10 +182,10 @@ echo if [[ $debugging -eq 1 ]];then echo "Build type = DEBUG" - run_cmd build_cpp -w $prefix/catkin_ws -p $prefix -b Debug -v $verbose + run_cmd build_catkin_workspace -w $prefix/catkin_ws -p $prefix -b Debug -v $verbose else echo "Build type = RELEASE" - run_cmd build_cpp -w $prefix/catkin_ws -p $prefix -b Release -v $verbose + run_cmd build_catkin_workspace -w $prefix/catkin_ws -p $prefix -b Release -v $verbose fi if [[ $samples -eq 1 && "$user_workspace" != "" ]];then @@ -397,8 +194,8 @@ if [[ $samples -eq 1 && "$user_workspace" != "" ]];then echo if [[ $debugging -eq 1 ]];then - run_cmd build_cpp -w $user_workspace -p $prefix -b Debug -v $verbose + run_cmd build_catkin_workspace -w $user_workspace -p $prefix -b Debug -v $verbose else - run_cmd build_cpp -w $user_workspace -p $prefix -b Release -v $verbose + run_cmd build_catkin_workspace -w $user_workspace -p $prefix -b Release -v $verbose fi fi diff --git a/docker/.dockerignore b/docker/.dockerignore new file mode 100644 index 0000000..adc6666 --- /dev/null +++ b/docker/.dockerignore @@ -0,0 +1,5 @@ +build.sh +Dockerfile +.dockerignore +README.md +run.sh diff --git a/docker/Dockerfile b/docker/Dockerfile index a1a9d93..7a9e169 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -23,14 +23,8 @@ RUN apt-get update && apt-get install -y openjdk-8-jdk RUN wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip RUN unzip sdk-tools-linux-4333796.zip -d /opt/android/sdk RUN yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses -RUN $ANDROID_HOME/tools/bin/sdkmanager "build-tools;26.0.2" "platform-tools" "platforms;android-26" --sdk_root=/opt/android/sdk -ENV PATH /opt/android/sdk/tools:/opt/android/sdk/platform-tools:$PATH - -# Install CMake 3.6 -WORKDIR /opt -RUN wget https://cmake.org/files/v3.6/cmake-3.6.3-Linux-x86_64.sh && chmod +x cmake-3.6.3-Linux-x86_64.sh -RUN yes | sh '/opt/cmake-3.6.3-Linux-x86_64.sh' -RUN ln -s /opt/cmake-3.6.3-Linux-x86_64/bin/* -t /usr/local/bin +RUN $ANDROID_HOME/tools/bin/sdkmanager "build-tools;26.0.2" "platform-tools" "platforms;android-26" "cmake;3.6.4111459" --sdk_root=/opt/android/sdk +ENV PATH /opt/android/sdk/tools:/opt/android/sdk/platform-tools:/opt/android/sdk/cmake/3.6.4111459/bin:$PATH # Install Python libraries RUN apt-get install python-lxml -y @@ -46,3 +40,7 @@ ENV PATH /usr/lib/ccache:$PATH # Clear entrypoint (do not source /opt/ros/kinetic) ENTRYPOINT [] + +VOLUME /opt/ros_android +WORKDIR /opt/ros_android +CMD ["bash"] diff --git a/docker/build.sh b/docker/build.sh index 3f49a46..352adf6 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -3,5 +3,5 @@ IMAGE=android_ndk pushd "$( dirname "${BASH_SOURCE[0]}" )" -docker build -t ${IMAGE} . +docker build -t ${IMAGE} "$@" . popd diff --git a/docker/run.sh b/docker/run.sh index 4008f43..3badb25 100755 --- a/docker/run.sh +++ b/docker/run.sh @@ -4,14 +4,34 @@ SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" REPO_ROOT=$SCRIPTPATH/../ IMAGE=android_ndk -if [ "$#" == 0 ]; then - EXTRA_ARGS="bash" -else - EXTRA_ARGS="bash -c \"$@\"" -fi +DOCKEROPTS=() +while [ $# -gt 0 ]; do + case "$1" in + --help) + # print usage + echo "Usage: $0 [DOCKER OPTIONS] [-- [COMMAND]]" + echo + echo "Docker options:" + docker run --help | tail -n +7 + exit 0 + ;; + --) + # end-of-options: all arguments after the -- will be interpreted as a command to run inside the container. + shift + break + ;; + *) + # collect docker options + DOCKEROPTS+=("$1") + shift + ;; + esac +done +set -x docker run \ -v ${REPO_ROOT}:/opt/ros_android \ --privileged \ -it \ - ${IMAGE} "${EXTRA_ARGS}" + "${DOCKEROPTS[@]}" \ + ${IMAGE} "$@" diff --git a/example_workspace/src/hello_world_example_app/app/src/main/cpp/CMakeLists.txt b/example_workspace/src/hello_world_example_app/app/CMakeLists.txt similarity index 63% rename from example_workspace/src/hello_world_example_app/app/src/main/cpp/CMakeLists.txt rename to example_workspace/src/hello_world_example_app/app/CMakeLists.txt index 632356c..5d75332 100644 --- a/example_workspace/src/hello_world_example_app/app/src/main/cpp/CMakeLists.txt +++ b/example_workspace/src/hello_world_example_app/app/CMakeLists.txt @@ -20,30 +20,30 @@ project(hello_ros) find_package(catkin REQUIRED COMPONENTS roscpp std_msgs rosconsole) -include_directories(${catkin_INCLUDE_DIRS}) +include_directories(include ${catkin_INCLUDE_DIRS}) # build native_app_glue as a static lib -set(${CMAKE_C_FLAGS}, "${CMAKE_C_FLAGS}") -add_library(native_app_glue STATIC - ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) +# set(${CMAKE_C_FLAGS}, "${CMAKE_C_FLAGS}") +# add_library(native_app_glue STATIC +# ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) # now build app's shared lib set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall -Werror") # Export ANativeActivity_onCreate(), # Refer to: https://github.com/android-ndk/ndk/issues/381. -set(CMAKE_SHARED_LINKER_FLAGS - "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") +# set(CMAKE_SHARED_LINKER_FLAGS + # "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") -add_library(native-activity SHARED main.cpp) +add_library(native-activity SHARED src/main/jni/com_ros_example_hello_ros_MainActivity_RosThread.cpp src/main/cpp/main.cpp src/main/cpp/main_thread.cpp) -target_include_directories(native-activity PRIVATE - ${ANDROID_NDK}/sources/android/native_app_glue) +# target_include_directories(native-activity PRIVATE + # ${ANDROID_NDK}/sources/android/native_app_glue) # add lib dependencies target_link_libraries(native-activity android - native_app_glue + # native_app_glue ${catkin_LIBRARIES} - log + # log ) diff --git a/example_workspace/src/hello_world_example_app/app/build.gradle b/example_workspace/src/hello_world_example_app/app/build.gradle index 9741d70..52087e4 100644 --- a/example_workspace/src/hello_world_example_app/app/build.gradle +++ b/example_workspace/src/hello_world_example_app/app/build.gradle @@ -4,7 +4,7 @@ android { compileSdkVersion 28 defaultConfig { - applicationId = 'com.example.hello_ros' + applicationId = 'com.ros.example.hello_ros' minSdkVersion 24 targetSdkVersion 24 externalNativeBuild { @@ -32,7 +32,7 @@ android { } externalNativeBuild { cmake { - path 'src/main/cpp/CMakeLists.txt' + path 'CMakeLists.txt' } } } diff --git a/example_workspace/src/hello_world_example_app/app/include/ros_android/main_thread.h b/example_workspace/src/hello_world_example_app/app/include/ros_android/main_thread.h new file mode 100644 index 0000000..4233962 --- /dev/null +++ b/example_workspace/src/hello_world_example_app/app/include/ros_android/main_thread.h @@ -0,0 +1,40 @@ +#ifndef __ROS_ANDROID_MAIN_THREAD_H__ +#define __ROS_ANDROID_MAIN_THREAD_H__ + +#include +#include +#include + +namespace ros_android { +class MainThread { + public: + MainThread(); + explicit MainThread(std::string); + virtual ~MainThread() = 0; + + typedef std::shared_ptr Ptr; + static Ptr Instance(void); + + virtual void run() = 0; + virtual void stop() = 0; + bool check_ros_master(std::string master_ip, std::string my_ip); + private: + std::string node_name; + + static std::mutex s_instance_mutex; + static Ptr s_instance; +}; +} // namespace ros_android + +#define MY_ROS_ANDROID_MAIN_THREAD(class_name) \ + ros_android::MainThread::Ptr ros_android::MainThread::Instance() { \ + Ptr instance = s_instance; \ + if (!instance) { \ + std::lock_guard lock(s_instance_mutex); \ + if (!s_instance) { \ + instance = s_instance = std::make_shared(); \ + } \ + } \ + return instance; \ + } +#endif // #ifndef __ROS_ANDROID_MAIN_THREAD_H__ diff --git a/example_workspace/src/hello_world_example_app/app/src/main/AndroidManifest.xml b/example_workspace/src/hello_world_example_app/app/src/main/AndroidManifest.xml index d36cdb8..b1970ea 100644 --- a/example_workspace/src/hello_world_example_app/app/src/main/AndroidManifest.xml +++ b/example_workspace/src/hello_world_example_app/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ - @@ -9,22 +8,16 @@ - + android:hasCode="true"> - - - - @@ -33,4 +26,3 @@ - diff --git a/example_workspace/src/hello_world_example_app/app/src/main/cpp/main.cpp b/example_workspace/src/hello_world_example_app/app/src/main/cpp/main.cpp index b0471a3..23243fa 100644 --- a/example_workspace/src/hello_world_example_app/app/src/main/cpp/main.cpp +++ b/example_workspace/src/hello_world_example_app/app/src/main/cpp/main.cpp @@ -1,94 +1,54 @@ -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include +#include -#include "ros/ros.h" +#include +#include #include -#include +class HelloRos : public ros_android::MainThread { + public: + HelloRos() : ros_android::MainThread("hello_ros") {} -int loop_count_ = 0; -ros::Publisher chatter_pub; + virtual void run() override { + ros::NodeHandle n; -void chatterCallback(const std_msgs::String::ConstPtr& msg){ - ROS_INFO("%s", msg->data.c_str()); - loop_count_++; - std_msgs::String msgo; - std::stringstream ss; - ss << "hello world from android ndk " << loop_count_; - msgo.data = ss.str(); - chatter_pub.publish(msgo); - ROS_INFO_STREAM(msg->data.c_str()); -} + /* Write your main code here */ + ROS_INFO("GOING TO PUBLISHER"); -void android_main(android_app *state) { + // Creating a publisher and a subscriber + // When something is received in chatter topic, a message is published in a_chatter topic + chatter_pub = n.advertise("a_chatter", 1000); + ros::Subscriber sub = n.subscribe("chatter", 1000, std::bind(&HelloRos::chatterCallback, this, std::placeholders::_1)); - int argc = 3; + // Rate 1Hz + ros::WallRate loop_rate(1); - //*********************** NOTE: HARDCODE rosmaster ip addresses in __master, and hardcode the ip address of the android device in __ip ************************* - char* argv[] = {const_cast("nothing_important") , const_cast("__master:=http://10.34.0.120:11311"), const_cast("__ip:=10.34.0.121")}; - //********************************************************************************************************************************************************* - - for (int i = 0; i < argc; i++) { - ROS_INFO("%s",argv[i]); + while(ros::ok()) { + ros::spinOnce(); + loop_rate.sleep(); + } } - ros::init(argc, &argv[0], "android_ndk_native_cpp"); - - ROS_INFO("GOING TO NODEHANDLE"); - std::string master_uri = ros::master::getURI(); - - if (ros::master::check()) { - ROS_INFO("ROS MASTER IS UP!"); - } else { - ROS_INFO("NO ROS MASTER."); + virtual void stop() override { + /* Write your clean-up code here */ + ros::shutdown(); } - ROS_INFO("%s", master_uri.c_str()); - - ros::NodeHandle n; - - ROS_INFO("GOING TO PUBLISHER"); - - // Creating a publisher and a subscriber - // When something is received in chatter topic, a message is published in a_chatter topic - chatter_pub = n.advertise("a_chatter", 1000); - ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); - - // Rate 1Hz - ros::WallRate loop_rate(1); - while(1) { - int events; - struct android_poll_source* source; - - // Poll android events, without locking - while (ALooper_pollAll(0, NULL, &events, (void**)&source) >= 0) { - // Process this event - if (source != NULL) { - source->process(state, source); - } - - // Check if we are exiting. - if (state->destroyRequested != 0) { - ROS_INFO("APP DESTROYED BYE BYE"); - return; - } - } - - ros::spinOnce(); - - if (!ros::ok()) { - ROS_INFO("ROS ISN'T OK, BYE BYE"); - return; - } - - loop_rate.sleep(); + private: + int loop_count_ = 0; + ros::Publisher chatter_pub; + + void chatterCallback(const std_msgs::String::ConstPtr& msg){ + ROS_INFO("%s", msg->data.c_str()); + loop_count_++; + std_msgs::String msgo; + std::stringstream ss; + ss << "hello world from android ndk " << loop_count_; + msgo.data = ss.str(); + chatter_pub.publish(msgo); + ROS_INFO("%s", msg->data.c_str()); } -} +}; + +MY_ROS_ANDROID_MAIN_THREAD(HelloRos) diff --git a/example_workspace/src/hello_world_example_app/app/src/main/cpp/main_thread.cpp b/example_workspace/src/hello_world_example_app/app/src/main/cpp/main_thread.cpp new file mode 100644 index 0000000..4a34b6d --- /dev/null +++ b/example_workspace/src/hello_world_example_app/app/src/main/cpp/main_thread.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include +#include + + +namespace ros_android { + std::mutex MainThread::s_instance_mutex; + MainThread::Ptr MainThread::s_instance; + MainThread::MainThread() : node_name("ros_android_node") {} + MainThread::MainThread(std::string name) : node_name(name) {} + MainThread::~MainThread() {} + + bool MainThread::check_ros_master(std::string master_ip, std::string my_ip) { + int argc = 3; + bool rv; + + const char* argv[] = {"nothing_important", + (std::string("__master:=http://") + master_ip + std::string(":11311")).c_str(), + (std::string("__ip:=") + my_ip).c_str() + }; + + ros::init(argc, const_cast(&argv[0]), node_name.c_str()); + rv = ros::master::check(); + if (rv) { + ROS_INFO("ROS MASTER IS UP!"); + } else { + ROS_INFO("NO ROS MASTER."); + } + ros::start(); + return rv; + } +} \ No newline at end of file diff --git a/example_workspace/src/hello_world_example_app/app/src/main/java/com/ros/example/hello_ros/MainActivity.java b/example_workspace/src/hello_world_example_app/app/src/main/java/com/ros/example/hello_ros/MainActivity.java new file mode 100644 index 0000000..2bc1459 --- /dev/null +++ b/example_workspace/src/hello_world_example_app/app/src/main/java/com/ros/example/hello_ros/MainActivity.java @@ -0,0 +1,159 @@ +package com.ros.example.hello_ros; + +import android.Manifest; +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import java.lang.Runnable; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Enumeration; + +public class MainActivity extends Activity { + static { + System.loadLibrary("native-activity"); + } + + private final String TAG = "HELLO-WORLD-EXAMPLE"; + + private enum Status { + WAITING, RUNNING + } + + private class RosThread implements Runnable { + public RosThread() { + Log.i(TAG, "calling __RosThread"); + __RosThread(); + Log.i(TAG, "ended __RosThread"); + } + @Override + public native void run(); + public native void stop(); + public native boolean checkRosMaster(String masterIP, String myIP); + private native void __RosThread(); + } + + private RosThread mainThread; + private EditText masterIP; + private EditText myIP; + private Button runButton; + private TextView statusText; + private Status status; + + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "OnCreate()"); + super.onCreate(savedInstanceState); + setContentView(R.layout.logging); + + masterIP = (EditText) findViewById(R.id.master_ip); + myIP = (EditText) findViewById(R.id.my_ip); + runButton = (Button) findViewById(R.id.run_button); + statusText = (TextView) findViewById(R.id.status); + status = Status.WAITING; + + Log.i(TAG, "new RosThread"); + mainThread = new RosThread(); + Log.i(TAG, "end RosThread constructor"); + + runButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + runButtonCallback(); + } + }); + + String ip = getLocalIpAddress(); + if (ip != null) { + myIP.setText(ip); + } + } + + @Override + protected void onResume() { + super.onResume(); + if (status == Status.RUNNING) { + new Thread(mainThread).start(); + } + } + + @Override + protected void onPause() { + super.onPause(); + mainThread.stop(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mainThread.stop(); + } + + private void runButtonCallback() { + String ipRegexExpression = + "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"; + + switch (status) { + case WAITING: + String sMasterIP = masterIP.getText().toString(); + if (! sMasterIP.matches(ipRegexExpression)) { + statusText.setText(R.string.status_masterIP_error); + break; + } + Log.i(TAG, "master ip is fine: " + sMasterIP); + + String sMyIP = myIP.getText().toString(); + if (! sMyIP.matches(ipRegexExpression)) { + statusText.setText(R.string.status_myIP_error); + break; + } + Log.i(TAG, "my ip is fine: " + sMyIP); + + if (! mainThread.checkRosMaster(sMasterIP, sMyIP)) { + statusText.setText(R.string.status_check_master_error); + break; + } + Log.i(TAG, "Master is ready"); + + statusText.setText(R.string.status_running); + runButton.setText(R.string.button_stop); + + new Thread(mainThread).start(); + status = Status.RUNNING; + break; + + case RUNNING: + statusText.setText(R.string.status_waiting); + runButton.setText(R.string.button_run); + + mainThread.stop(); + status = Status.WAITING; + break; + } + } + + private String getLocalIpAddress(){ + try { + for (Enumeration en = NetworkInterface.getNetworkInterfaces(); + en.hasMoreElements();) { + NetworkInterface intf = en.nextElement(); + for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { + InetAddress inetAddress = enumIpAddr.nextElement(); + if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) { + return inetAddress.getHostAddress(); + } + } + } + } catch (Exception ex) { + Log.e("IP Address", ex.toString()); + } + return null; + } +} diff --git a/example_workspace/src/hello_world_example_app/app/src/main/jni/com_ros_example_hello_ros_MainActivity_RosThread.cpp b/example_workspace/src/hello_world_example_app/app/src/main/jni/com_ros_example_hello_ros_MainActivity_RosThread.cpp new file mode 100644 index 0000000..39c52f2 --- /dev/null +++ b/example_workspace/src/hello_world_example_app/app/src/main/jni/com_ros_example_hello_ros_MainActivity_RosThread.cpp @@ -0,0 +1,24 @@ +#include +#include "com_ros_example_hello_ros_MainActivity_RosThread.h" + +using namespace ros_android; + +JNIEXPORT void JNICALL Java_com_ros_example_hello_1ros_MainActivity_00024RosThread_run + (JNIEnv *, jobject) { + MainThread::Instance()->run(); +} + +JNIEXPORT void JNICALL Java_com_ros_example_hello_1ros_MainActivity_00024RosThread_stop + (JNIEnv *, jobject) { + MainThread::Instance()->stop(); +} + +JNIEXPORT jboolean JNICALL Java_com_ros_example_hello_1ros_MainActivity_00024RosThread_checkRosMaster + (JNIEnv *env, jobject, jstring j_master_ip, jstring j_my_ip) { + return MainThread::Instance()->check_ros_master(env->GetStringUTFChars(j_master_ip, nullptr), env->GetStringUTFChars(j_my_ip, nullptr)); +} + +JNIEXPORT void JNICALL Java_com_ros_example_hello_1ros_MainActivity_00024RosThread__1_1RosThread + (JNIEnv *, jobject) { + MainThread::Instance(); +} \ No newline at end of file diff --git a/example_workspace/src/hello_world_example_app/app/src/main/jni/com_ros_example_hello_ros_MainActivity_RosThread.h b/example_workspace/src/hello_world_example_app/app/src/main/jni/com_ros_example_hello_ros_MainActivity_RosThread.h new file mode 100644 index 0000000..0228a2d --- /dev/null +++ b/example_workspace/src/hello_world_example_app/app/src/main/jni/com_ros_example_hello_ros_MainActivity_RosThread.h @@ -0,0 +1,45 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_ros_example_hello_ros_MainActivity_RosThread */ + +#ifndef _Included_com_ros_example_hello_ros_MainActivity_RosThread +#define _Included_com_ros_example_hello_ros_MainActivity_RosThread +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_ros_example_hello_ros_MainActivity_RosThread + * Method: run + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_ros_example_hello_1ros_MainActivity_00024RosThread_run + (JNIEnv *, jobject); + +/* + * Class: com_ros_example_hello_ros_MainActivity_RosThread + * Method: stop + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_ros_example_hello_1ros_MainActivity_00024RosThread_stop + (JNIEnv *, jobject); + +/* + * Class: com_ros_example_hello_ros_MainActivity_RosThread + * Method: checkRosMaster + * Signature: (Ljava/lang/String;Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_com_ros_example_hello_1ros_MainActivity_00024RosThread_checkRosMaster + (JNIEnv *, jobject, jstring, jstring); + +/* + * Class: com_ros_example_hello_ros_MainActivity_RosThread + * Method: __RosThread + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_ros_example_hello_1ros_MainActivity_00024RosThread__1_1RosThread + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/example_workspace/src/hello_world_example_app/app/src/main/res/layout/logging.xml b/example_workspace/src/hello_world_example_app/app/src/main/res/layout/logging.xml new file mode 100644 index 0000000..4a8f710 --- /dev/null +++ b/example_workspace/src/hello_world_example_app/app/src/main/res/layout/logging.xml @@ -0,0 +1,34 @@ + + + + + + + + + +