diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 40f1cc7b..8f8a1adf 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -8,8 +8,9 @@ jobs: steps: - name: Install Java run: | - sudo apt update -qq - sudo apt install -y default-jdk + sudo apt-get update -qq + sudo apt-get install -y default-jdk gradle + - uses: actions/checkout@v2 - uses: ros-tooling/setup-ros@0.0.14 with: required-ros-distributions: dashing @@ -17,4 +18,70 @@ jobs: with: package-name: rosidl_generator_java rcljava_common rcljava source-ros-binary-installation: dashing - vcs-repo-file-url: https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_desktop.repos + vcs-repo-file-url: ${{ github.workspace }}/ros2_java_desktop.repos + + build_android: + runs-on: ubuntu-18.04 + steps: + - name: Install Java + run: | + sudo apt-get update -qq + sudo apt-get install -y default-jdk gradle + - uses: actions/checkout@v2 + - name: Setup locale for ROS 2 + run: | + sudo locale-gen en_US en_US.UTF-8 + sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 + export LANG=en_US.UTF-8 + - name: Setup sources for ROS 2 + run: | + sudo apt-get update && sudo apt-get install -y curl gnupg2 lsb-release + curl -sL https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add - + sudo sh -c 'echo "deb [arch=$(dpkg --print-architecture)] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/ros2-latest.list' + - name: Install ROS 2 dependencies + run: | + sudo apt-get update && sudo apt-get install -y python3-colcon-common-extensions python3-vcstool python3-lark-parser python3-dev + - name: Install colcon extensions for Gradle + run: | + sudo pip3 install git+git://github.com/colcon/colcon-gradle.git + sudo pip3 install git+git://github.com/colcon/colcon-ros-gradle.git + - name: Install Android NDK + run: | + curl -LO https://dl.google.com/android/repository/android-ndk-r21d-linux-x86_64.zip + unzip android-ndk-r21d-linux-x86_64.zip + - name: Setup workspace with VCS repo file + run: | + mkdir -p ros2_java_ws/src + cd ros2_java_ws + curl -sL file://${{ github.workspace }}/ros2_java_android.repos | vcs import src + # Use checked out version of ros2_java + rm -rf src/ros2_java/ros2_java + ln --symbolic ${{ github.workspace }} src/ros2_java + - name: Build ros2_java for Android + run: | + export PYTHON3_EXEC="$( which python3 )" + export PYTHON3_LIBRARY="$( ${PYTHON3_EXEC} -c 'import os.path; from distutils import sysconfig; print(os.path.realpath(os.path.join(sysconfig.get_config_var("LIBPL"), sysconfig.get_config_var("LDLIBRARY"))))' )" + export PYTHON3_INCLUDE_DIR="$( ${PYTHON3_EXEC} -c 'from distutils import sysconfig; print(sysconfig.get_config_var("INCLUDEPY"))' )" + export ANDROID_ABI=armeabi-v7a + export ANDROID_NATIVE_API_LEVEL=android-21 + export ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang + export ANDROID_NDK=${PWD}/android-ndk-r21d + + cd ros2_java_ws + colcon build \ + --packages-ignore cyclonedds rcl_logging_log4cxx rosidl_generator_py \ + --packages-up-to rcljava \ + --cmake-args \ + -DPYTHON_EXECUTABLE=${PYTHON3_EXEC} \ + -DPYTHON_LIBRARY=${PYTHON3_LIBRARY} \ + -DPYTHON_INCLUDE_DIR=${PYTHON3_INCLUDE_DIR} \ + -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ + -DANDROID_FUNCTION_LEVEL_LINKING=OFF \ + -DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} \ + -DANDROID_TOOLCHAIN_NAME=${ANDROID_TOOLCHAIN_NAME} \ + -DANDROID_STL=c++_shared \ + -DANDROID_ABI=${ANDROID_ABI} \ + -DANDROID_NDK=${ANDROID_NDK} \ + -DTHIRDPARTY=ON \ + -DCOMPILE_EXAMPLES=OFF \ + -DCMAKE_FIND_ROOT_PATH="${PWD}/install" diff --git a/README.md b/README.md index ae538cd9..8bbc8ca4 100644 --- a/README.md +++ b/README.md @@ -109,8 +109,6 @@ Make sure you have Gradle 3.2 (or later) installed. ### Download and Build ROS 2 Java for Android -> TODO: This section needs updated instructions for ROS 2 Dashing and newer - The Android setup is slightly more complex, you'll need the SDK and NDK installed, and an Android device where you can run the examples. Make sure to download at least the SDK for Android Lollipop (or greater), the examples require the API level 21 at least and NDK 14. @@ -121,48 +119,43 @@ We'll also need to have the [Android SDK](https://developer.android.com/studio/# Although the `ros2_java_android.repos` file contains all the repositories for the Android bindings to compile, we'll have to disable certain packages (`python_cmake_module`, `rosidl_generator_py`, `test_msgs`) that are included the repositories and that we either don't need or can't cross-compile properly (e.g. the Python generator) -``` -# define paths -ROOT_DIR = ${HOME} -AMENT_WORKSPACE=${ROOT_DIR}/ament_ws -ROS2_ANDROID_WORKSPACE=${ROOT_DIR}/ros2_android_ws - -# pull and build ament -mkdir -p ${AMENT_WORKSPACE}/src -cd ${AMENT_WORKSPACE} -curl https://raw.githubusercontent.com/ros2-java/ament_java/master/ament_java.repos -vcs import ${AMENT_WORKSPACE}/src < ament_java.repos -src/ament/ament_tools/scripts/ament.py build --symlink-install --isolated - -# android build configuration -export PYTHON3_EXEC="$( which python3 )" -export ANDROID_ABI=armeabi-v7a -export ANDROID_NATIVE_API_LEVEL=android-21 -export ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang - -# pull and build ros2 for android -mkdir -p ${ROS2_ANDROID_WORKSPACE}/src -cd ${ROS2_ANDROID_WORKSPACE} -curl https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_android.repos -vcs import ${ROS2_ANDROID_WORKSPACE}/src < ros2_java_android.repos -source ${AMENT_WORKSPACE}/install_isolated/local_setup.sh -ament build --isolated --skip-packages test_msgs \ - --cmake-args \ - -DPYTHON_EXECUTABLE=${PYTHON3_EXEC} \ - -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \ - -DANDROID_FUNCTION_LEVEL_LINKING=OFF \ - -DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} \ - -DANDROID_TOOLCHAIN_NAME=${ANDROID_TOOLCHAIN_NAME} \ - -DANDROID_STL=gnustl_shared \ - -DANDROID_ABI=${ANDROID_ABI} \ - -DANDROID_NDK=${ANDROID_NDK} \ - -DTHIRDPARTY=ON \ - -DCOMPILE_EXAMPLES=OFF \ - -DCMAKE_FIND_ROOT_PATH="$AMENT_WORKSPACE/install_isolated;$ROS2_ANDROID_WORKSPACE/install_isolated" \ - -- \ - --parallel \ - --ament-gradle-args \ - -Pament.android_stl=gnustl_shared -Pament.android_abi=$ANDROID_ABI -Pament.android_ndk=$ANDROID_NDK -- -``` +1. Download the [Android NDK](https://developer.android.com/ndk/downloads/index.html) and set the environment variable `ANDROID_NDK` to the path where it is extracted. + +1. Download the [Android SDK](https://developer.android.com/studio/#downloads) and set the environment variable `ANDROID_HOME` to the path where it is extracted. + +1. Clone ROS 2 and ROS 2 Java source code: + + mkdir -p $HOME/ros2_android_ws/src + cd $HOME/ros2_android_ws + curl https://raw.githubusercontent.com/ros2-java/ros2_java/dashing/ros2_java_android.repos | vcs import src + +1. Set Android build configuration: + + export PYTHON3_EXEC="$( which python3 )" + export PYTHON3_LIBRARY="$( ${PYTHON3_EXEC} -c 'import os.path; from distutils import sysconfig; print(os.path.realpath(os.path.join(sysconfig.get_config_var("LIBPL"), sysconfig.get_config_var("LDLIBRARY"))))' )" + export PYTHON3_INCLUDE_DIR="$( ${PYTHON3_EXEC} -c 'from distutils import sysconfig; print(sysconfig.get_config_var("INCLUDEPY"))' )" + export ANDROID_ABI=armeabi-v7a + export ANDROID_NATIVE_API_LEVEL=android-21 + export ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang + +1. Build (skipping packages that we don't need or can't cross-compile): + + colcon build \ + --packages-ignore cyclonedds rcl_logging_log4cxx rosidl_generator_py \ + --packages-up-to rcljava \ + --cmake-args \ + -DPYTHON_EXECUTABLE=${PYTHON3_EXEC} \ + -DPYTHON_LIBRARY=${PYTHON3_LIBRARY} \ + -DPYTHON_INCLUDE_DIR=${PYTHON3_INCLUDE_DIR} \ + -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \ + -DANDROID_FUNCTION_LEVEL_LINKING=OFF \ + -DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} \ + -DANDROID_TOOLCHAIN_NAME=${ANDROID_TOOLCHAIN_NAME} \ + -DANDROID_STL=c++_shared \ + -DANDROID_ABI=${ANDROID_ABI} \ + -DANDROID_NDK=${ANDROID_NDK} \ + -DTHIRDPARTY=ON \ + -DCOMPILE_EXAMPLES=OFF \ + -DCMAKE_FIND_ROOT_PATH="${PWD}/install" You can find more information about the Android examples at https://github.com/ros2-java/ros2_android_examples diff --git a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java index 9fdc1c27..8bf5e69d 100644 --- a/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java +++ b/rcljava/src/main/java/org/ros2/rcljava/node/NodeImpl.java @@ -47,8 +47,6 @@ import java.lang.ref.WeakReference; -import java.lang.reflect.Method; - import java.util.ArrayList; import java.util.Collection; import java.util.List; diff --git a/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java b/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java index f4724d9b..cf01c02e 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/client/ClientTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import java.lang.ref.WeakReference; +import java.lang.reflect.Method; import java.time.Duration; import java.util.Arrays; @@ -46,7 +47,18 @@ public class ClientTest { @BeforeClass public static void setupOnce() throws Exception { RCLJava.rclJavaInit(); - org.apache.log4j.BasicConfigurator.configure(); + try + { + // Configure log4j. Doing this dynamically so that Android does not complain about missing + // the log4j JARs, SLF4J uses Android's native logging mechanism instead. + Class c = Class.forName("org.apache.log4j.BasicConfigurator"); + Method m = c.getDeclaredMethod("configure", (Class[]) null); + Object o = m.invoke(null, (Object[]) null); + } + catch (Exception e) + { + e.printStackTrace(); + } } public class TestClientConsumer implements TriConsumer[]) null); + Object o = m.invoke(null, (Object[]) null); + } + catch (Exception e) + { + e.printStackTrace(); + } } public class TestConsumer implements Consumer { diff --git a/rcljava/src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java b/rcljava/src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java index a458c6a2..1c94bce5 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/parameters/AsyncParametersClientTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import java.lang.ref.WeakReference; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -69,7 +70,18 @@ public TestConsumer(RCLFuture resultFuture) { @BeforeClass public static void setupOnce() throws Exception { RCLJava.rclJavaInit(); - org.apache.log4j.BasicConfigurator.configure(); + try + { + // Configure log4j. Doing this dynamically so that Android does not complain about missing + // the log4j JARs, SLF4J uses Android's native logging mechanism instead. + Class c = Class.forName("org.apache.log4j.BasicConfigurator"); + Method m = c.getDeclaredMethod("configure", (Class[]) null); + Object o = m.invoke(null, (Object[]) null); + } + catch (Exception e) + { + e.printStackTrace(); + } } @Before diff --git a/rcljava/src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java b/rcljava/src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java index 8ec4266a..197e5cef 100644 --- a/rcljava/src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java +++ b/rcljava/src/test/java/org/ros2/rcljava/parameters/SyncParametersClientTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import java.lang.ref.WeakReference; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -52,7 +53,18 @@ public class SyncParametersClientTest { @BeforeClass public static void setupOnce() throws Exception { RCLJava.rclJavaInit(); - org.apache.log4j.BasicConfigurator.configure(); + try + { + // Configure log4j. Doing this dynamically so that Android does not complain about missing + // the log4j JARs, SLF4J uses Android's native logging mechanism instead. + Class c = Class.forName("org.apache.log4j.BasicConfigurator"); + Method m = c.getDeclaredMethod("configure", (Class[]) null); + Object o = m.invoke(null, (Object[]) null); + } + catch (Exception e) + { + e.printStackTrace(); + } } @Before diff --git a/ros2_java_android.repos b/ros2_java_android.repos index f6b76ae2..e1dd0920 100644 --- a/ros2_java_android.repos +++ b/ros2_java_android.repos @@ -1,19 +1,51 @@ repositories: + ament/ament_cmake: + type: git + url: https://github.com/ament/ament_cmake.git + version: dashing + ament/ament_index: + type: git + url: https://github.com/ament/ament_index.git + version: dashing + ament/ament_lint: + type: git + url: https://github.com/ament/ament_lint.git + version: dashing + ament/ament_package: + type: git + url: https://github.com/ament/ament_package.git + version: dashing + ament/googletest: + type: git + url: https://github.com/ament/googletest.git + version: dashing + ament/uncrustify_vendor: + type: git + url: https://github.com/ament/uncrustify_vendor.git + version: dashing + ament/ament_java: + type: git + url: https://github.com/ros2-java/ament_java.git + version: master eProsima/Fast-CDR: type: git url: https://github.com/eProsima/Fast-CDR.git version: v1.0.11 - eProsima/Fast-RTPS: + eProsima/Fast-DDS: type: git - url: https://github.com/eProsima/Fast-RTPS.git + url: https://github.com/eProsima/Fast-DDS.git version: v1.8.2 - ros2/ament_cmake_ros: + osrf/osrf_pycommon: type: git - url: https://github.com/ros2/ament_cmake_ros.git + url: https://github.com/osrf/osrf_pycommon.git version: dashing - ros2/rcutils: + osrf/osrf_testing_tools_cpp: type: git - url: https://github.com/ros2/rcutils.git + url: https://github.com/ros2-java/osrf_testing_tools_cpp.git + version: dashing-android + ros2/ament_cmake_ros: + type: git + url: https://github.com/ros2/ament_cmake_ros.git version: dashing ros2/common_interfaces: type: git @@ -23,9 +55,9 @@ repositories: type: git url: https://github.com/ros2/example_interfaces.git version: dashing - ros2/libyaml_vendor: + ros2/launch: type: git - url: https://github.com/ros2/libyaml_vendor.git + url: https://github.com/ros2/launch.git version: dashing ros2/poco_vendor: type: git @@ -39,10 +71,30 @@ repositories: type: git url: https://github.com/ros2/rcl_interfaces.git version: dashing + ros2/rcl_logging: + type: git + url: https://github.com/ros2/rcl_logging.git + version: dashing + ros2/rcpputils: + type: git + url: https://github.com/ros2/rcpputils.git + version: dashing + ros2/rcutils: + type: git + url: https://github.com/ros2/rcutils.git + version: dashing ros2/rmw: type: git url: https://github.com/ros2/rmw.git version: dashing + ros2/rmw_connext: + type: git + url: https://github.com/ros2/rmw_connext.git + version: dashing + ros2/rmw_cyclonedds: + type: git + url: https://github.com/ros2/rmw_cyclonedds.git + version: dashing-eloquent ros2/rmw_fastrtps: type: git url: https://github.com/ros2/rmw_fastrtps.git @@ -51,6 +103,10 @@ repositories: type: git url: https://github.com/ros2/rmw_implementation.git version: dashing + ros2/rmw_opensplice: + type: git + url: https://github.com/ros2/rmw_opensplice.git + version: dashing ros2/rosidl: type: git url: https://github.com/ros2/rosidl.git @@ -63,13 +119,37 @@ repositories: type: git url: https://github.com/ros2/rosidl_defaults.git version: dashing + ros2/rosidl_python: + type: git + url: https://github.com/ros2/rosidl_python.git + version: dashing ros2/rosidl_typesupport: type: git url: https://github.com/ros2/rosidl_typesupport.git version: dashing - ros2/osrf_testing_tools_cpp: + ros2/rosidl_typesupport_connext: + type: git + url: https://github.com/ros2/rosidl_typesupport_connext.git + version: dashing + ros2/rosidl_typesupport_fastrtps: + type: git + url: https://github.com/ros2/rosidl_typesupport_fastrtps.git + version: dashing + ros2/rosidl_typesupport_opensplice: + type: git + url: https://github.com/ros2/rosidl_typesupport_opensplice.git + version: dashing + ros2/test_interface_files: + type: git + url: https://github.com/ros2/test_interface_files.git + version: dashing + ros2/tinydir_vendor: + type: git + url: https://github.com/ros2/tinydir_vendor.git + version: dashing + ros2/unique_identifier_msgs: type: git - url: https://github.com/osrf/osrf_testing_tools_cpp + url: https://github.com/ros2/unique_identifier_msgs.git version: dashing ros2_java/ros2_java: type: git @@ -77,7 +157,7 @@ repositories: version: dashing ros2_java/ros2_android: type: git - url: https://github.com/esteve/ros2_android.git + url: https://github.com/ros2-java/ros2_android.git version: master ros2_java/ros2_android_examples: type: git diff --git a/rosidl_generator_java/resource/msg.cpp.em b/rosidl_generator_java/resource/msg.cpp.em index 9c868e60..d21c40d7 100644 --- a/rosidl_generator_java/resource/msg.cpp.em +++ b/rosidl_generator_java/resource/msg.cpp.em @@ -202,7 +202,7 @@ JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getDestructor JNIEnv * env = nullptr; // TODO(esteve): check return status assert(g_vm != nullptr); - g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8); + g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); assert(env != nullptr); if (ros_message == nullptr) { @@ -331,7 +331,7 @@ jobject @(underscore_separated_type_name)__convert_to_java(@(msg_normalized_type JNIEnv * env = nullptr; // TODO(esteve): check return status assert(g_vm != nullptr); - g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8); + g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); assert(env != nullptr); if (_jmessage_obj == nullptr) { @@ -435,7 +435,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * vm, void *) } JNIEnv * env; - if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8) != JNI_OK) { + if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { return JNI_ERR; } else { @[for normalized_type, jni_type in cache.items()]@ @@ -484,7 +484,7 @@ if value_method: @[ end if]@ @[end for]@ } - return JNI_VERSION_1_8; + return JNI_VERSION_1_6; } JNIEXPORT void JNICALL JNI_OnUnload(JavaVM * vm, void *) @@ -493,7 +493,7 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM * vm, void *) assert(g_vm == vm); JNIEnv * env; - if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8) == JNI_OK) { + if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) == JNI_OK) { @[for normalized_type, jni_type in cache.items()]@ if (_j@(normalized_type)_class_global != nullptr) { env->DeleteGlobalRef(_j@(normalized_type)_class_global); diff --git a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java index 00638e09..d64af3eb 100644 --- a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java +++ b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import java.lang.reflect.Method; import java.util.concurrent.Callable; import java.util.Arrays; import java.util.List; @@ -30,7 +31,18 @@ public class InterfacesTest { @BeforeClass public static void setupOnce() { - org.apache.log4j.BasicConfigurator.configure(); + try + { + // Configure log4j. Doing this dynamically so that Android does not complain about missing + // the log4j JARs, SLF4J uses Android's native logging mechanism instead. + Class c = Class.forName("org.apache.log4j.BasicConfigurator"); + Method m = c.getDeclaredMethod("configure", (Class[]) null); + Object o = m.invoke(null, (Object[]) null); + } + catch (Exception e) + { + e.printStackTrace(); + } } @Rule public ExpectedException thrown = ExpectedException.none();