Repository: ros/urdfdom (language: python)

Issue:
urdfdom version 2 updates
Ever since we merged the ROS 2 version of urdfdom into this repository, there has been something of a split-brain here.  This repository is really meant to be ROS-agnostic, but the stuff on the `ros2` branch here clearly was not.  This is a problem for downstream packagers because the latest tag on the master branch is 1.0.4, but the latest tag overall on this repository is on the `ros2` branch (2.3.3).  One bug that mentions this is https://github.com/ros/urdfdom/issues/156 , but there have been others.

This PR is an attempt to solve these problems.  What this PR does is to rebase the `ros2` branch onto master, making a few changes along the way.  By getting the changes from the `ros2` branch onto master, it resolves the above ambiguity and also brings some of the latest development onto the master branch.  The changes are necessary so that the code from the `ros2` branch is not ROS 2 specific.

The changes on this branch can be summarized in the following:
1.  The only change to the API is the addition of the `parseCamera`, `parseRay`, `parseSensor`, and `parseModelState` to the public API.  Since those functions are already implemented internally, here is no ABI change here at all.  In some sense this change gets us further away from our goal of removing TinyXML, but I didn't want to do too many things in this PR, so I left it as-is.
2. Use modern CMake wherever possible.  This includes getting rid of global include directives (`include_directories`), global link directives (`link_directories`), custom setting of the CXX_STANDARD, etc.  It also includes creating modern exported interfaces for the libraries in this package.  One place where we could *not* use modern CMake was finding the include directories for `urdfdom_headers`.  That's because the modern interfaces for `urdfdom_headers` is not available in the version released in Ubuntu 20.04, so we can't rely on it.
3. Make finding the "vendor" packages QUIET, which essentially makes them optional.  That means that when building standalone (for Debian packaging) or in ROS 1, it will just use the system versions of the packages.  On ROS 2 (and more specifically ROS 2 Windows), it will find the vendor packages.
4. Add a package.xml into the source repository.  For ROS 1 we typically relied on overlaying the package.xml at bloom time, but there is no real reason to do that.  Systems that do understand package.xml can take easier advantage of it in the source, and systems that don't understand it will just ignore it.

I've tested this PR by building it standalone (`mkdir build ; cd build ; cmake .. ; make -j10`), building it with `catkin_make_isolated` in a ROS 1 overlay, and by building it with `colcon` in a ROS 2 workspace (on Linux and Windows), and it seems to work in all of those circumstances.

If we merge this, this will obsolete #146 .
Add initial error check when parsing URDF.
There was a lack of early error checking when parsing a URDF file.



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/ros1.yaml b/.github/workflows/ros1.yaml
new file mode 100644
index 00000000..adf18706
--- /dev/null
+++ b/.github/workflows/ros1.yaml
@@ -0,0 +1,19 @@
+---
+name: urdfdom ROS1 CI
+
+on:
+  push:
+  pull_request:
+
+jobs:
+  build_ros1:
+    name: ROS1 CI
+    runs-on: ubuntu-18.04
+    steps:
+      - uses: ros-tooling/setup-ros@v0.2
+        with:
+          required-ros-distributions: melodic
+      - uses: ros-tooling/action-ros-ci@v0.2
+        with:
+          package-name: urdfdom
+          target-ros1-distro: melodic
diff --git a/.github/workflows/ros2.yaml b/.github/workflows/ros2.yaml
new file mode 100644
index 00000000..0d7b3f07
--- /dev/null
+++ b/.github/workflows/ros2.yaml
@@ -0,0 +1,20 @@
+---
+name: urdfdom ROS2 CI
+
+on:
+  push:
+  pull_request:
+
+jobs:
+
+  build_ros2:
+    name: ROS2 CI
+    runs-on: ubuntu-20.04
+    steps:
+      - uses: ros-tooling/setup-ros@v0.2
+        with:
+          required-ros-distributions: galactic
+      - uses: ros-tooling/action-ros-ci@v0.2
+        with:
+          package-name: urdfdom
+          target-ros2-distro: galactic
diff --git a/.github/workflows/system.yml b/.github/workflows/system.yml
new file mode 100644
index 00000000..c255647a
--- /dev/null
+++ b/.github/workflows/system.yml
@@ -0,0 +1,69 @@
+---
+name: urdfdom system CI
+
+on:
+  push:
+  pull_request:
+
+jobs:
+  # This workflow contains a single job called "build"
+  build:
+    # The type of runner that the job will run on
+    name: ${{ matrix.config.name }}
+    runs-on: ${{ matrix.config.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        config:
+          - {
+              name: "Ubuntu Latest GCC",
+              os: ubuntu-latest,
+              build_type: "Release",
+              cc: "gcc",
+              cxx: "g++",
+            }
+          - {
+              name: "Ubuntu Latest GCC",
+              os: ubuntu-latest,
+              build_type: "Debug",
+              cc: "gcc",
+              cxx: "g++",
+            }
+          - {
+              name: "Ubuntu Bionic GCC",
+              os: ubuntu-18.04,
+              build_type: "Release",
+              cc: "gcc",
+              cxx: "g++",
+            }
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Install dependencies on ubuntu
+        if: startsWith(matrix.config.name, 'Ubuntu')
+        run: |
+          sudo apt-get -qq update
+          sudo apt-get -qq upgrade -y
+          sudo apt-get -qq install -y build-essential cmake libconsole-bridge-dev liburdfdom-headers-dev libtinyxml-dev
+          cmake --version
+          gcc --version
+
+      - name: Configure
+        shell: bash
+        run: |
+          mkdir build
+          cmake \
+            -S . \
+            -B build \
+            -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }}
+
+      - name: Build
+        shell: bash
+        run: cmake --build build --config ${{ matrix.config.build_type }}
+
+      - name: Run tests on Ubuntu
+        if: startsWith(matrix.config.name, 'Ubuntu')
+        shell: bash
+        run: |
+          cd build
+          make test ARGS="-VV"
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index c711dc97..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-sudo: required
-dist: trusty
-# Force travis to use its minimal image with default Python settings
-language: generic
-compiler:
-  - clang
-  - gcc
-script: "./.travis/build"
-before_install:
-  - sudo apt-get update -qq
-  - sudo apt-get install -qq libtinyxml-dev
-matrix:
-  allow_failures:
-    - compiler: clang
diff --git a/.travis/build b/.travis/build
deleted file mode 100755
index 930a627f..00000000
--- a/.travis/build
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/sh
-set -ev
-
-# Directories.
-root_dir=`pwd`
-build_dir="$root_dir/_travis/build"
-console_bridge_dir="$build_dir/console_bridge"
-urdfdom_headers_dir="$build_dir/urdfdom_headers"
-
-# Shortcuts.
-git_clone="git clone --quiet --recursive"
-
-# create locale for testing
-sudo locale-gen nl_NL.UTF-8
-
-# Create layout.
-rm -rf "$build_dir"
-mkdir -p "$build_dir"
-
-# Retrieve console_bridge
-echo "--> Compiling console_bridge"
-cd "$build_dir"
-$git_clone "git://github.com/ros/console_bridge.git"
-cd "$console_bridge_dir"
-cmake .
-sudo make install
-
-# Retrieve urdfdom_headers
-echo "--> Compiling urdfdom_headers"
-cd "$build_dir"
-$git_clone "git://github.com/ros/urdfdom_headers.git"
-cd "$urdfdom_headers_dir"
-cmake .
-sudo make install
-
-# Compile
-echo "--> Compiling urdfdom"
-cd "$root_dir"
-cmake .
-make
-make test ARGS="-VV"
-sudo make install
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f0d115d3..e0c036b8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required( VERSION 2.8 FATAL_ERROR )
+cmake_minimum_required( VERSION 3.5 FATAL_ERROR )
 project (urdfdom CXX C)
 
 set (URDF_MAJOR_VERSION 1)
@@ -24,6 +24,10 @@ if (HAS_VISIBILITY)
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
 endif()
 
+if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+  add_compile_options(-Wall -Wextra -Wpedantic)
+endif()
+
 # This shouldn't be necessary, but there has been trouble
 # with MSVC being set off, but MSVCXX ON.
 if(MSVC OR MSVC90 OR MSVC10)
@@ -32,23 +36,23 @@ endif (MSVC OR MSVC90 OR MSVC10)
 
 set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
 
-find_package(TinyXML REQUIRED)
-include_directories(SYSTEM ${TinyXML_INCLUDE_DIRS})
-
-find_package(urdfdom_headers 1.0.3 REQUIRED)
-include_directories(SYSTEM ${urdfdom_headers_INCLUDE_DIRS})
-if (NOT MSVC)
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+find_package(tinyxml_vendor QUIET)
+find_package(TinyXML)
+# bionic has not cmake module, workaround
+if (NOT TinyXML_FOUND AND UNIX)
+  include(FindPkgConfig)
+  pkg_check_modules (TinyXML tinyxml)
+else()
+  # Make it fail in platforms without pkgconfig
+  find_package(TinyXML REQUIRED) # bionic has not cmake module
 endif()
-
+find_package(urdfdom_headers 1.0 REQUIRED)
+find_package(console_bridge_vendor QUIET) # Provides console_bridge 0.4.0 on platforms without it.
 find_package(console_bridge REQUIRED)
-include_directories(SYSTEM ${console_bridge_INCLUDE_DIRS})
-link_directories(${console_bridge_LIBRARY_DIRS})
 
-#In Visual Studio a special postfix for 
-#libraries compiled in debug is used
+#In Visual Studio a special postfix for libraries compiled in debug is used
 if(MSVC)
-set(CMAKE_DEBUG_POSTFIX "d")
+  set(CMAKE_DEBUG_POSTFIX "d")
 endif(MSVC)
 
 # Control where libraries and executables are placed during the build
@@ -58,19 +62,23 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}"
 
 enable_testing()
 
-add_subdirectory(urdf_parser)
-
 if(WIN32 AND NOT CYGWIN)
   set(CMAKE_CONFIG_INSTALL_DIR CMake)
 else()
-    set(CMAKE_CONFIG_INSTALL_DIR ${CMAKE_INSTALL_FULL_LIBDIR}/${PROJECT_NAME}/cmake/)
+  set(CMAKE_CONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake)
 endif()
+string(REGEX REPLACE "[^/]+" ".." RELATIVE_PATH_CMAKE_DIR_TO_PREFIX "${CMAKE_CONFIG_INSTALL_DIR}")
+string(REGEX REPLACE "[^/]+" ".." RELATIVE_PATH_LIBDIR_TO_PREFIX "${CMAKE_INSTALL_LIBDIR}")
+
+add_subdirectory(urdf_parser)
 
 set(PKG_NAME ${PROJECT_NAME})
 set(PKG_LIBRARIES urdfdom_sensor urdfdom_model_state urdfdom_model urdfdom_world)
 set(PKG_DEPENDS urdfdom_headers console_bridge)
+set(PKG_EXPORTS urdfdom)
 set(cmake_conf_file "cmake/urdfdom-config.cmake")
 configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${cmake_conf_file}.in" "${CMAKE_BINARY_DIR}/${cmake_conf_file}" @ONLY)
+install(FILES package.xml DESTINATION share/${PROJECT_NAME})
 install(FILES ${CMAKE_BINARY_DIR}/${cmake_conf_file}
   DESTINATION ${CMAKE_CONFIG_INSTALL_DIR} COMPONENT cmake)
 
@@ -85,10 +93,14 @@ if (NOT MSVC)
     DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig/ COMPONENT pkgconfig)
 endif()
 
+
 # Add uninstall target
 # Ref: http://www.cmake.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F
-configure_file("${PROJECT_SOURCE_DIR}/cmake/uninstall.cmake.in" "${PROJECT_BINARY_DIR}/uninstall.cmake" IMMEDIATE @ONLY)
-add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${PROJECT_BINARY_DIR}/uninstall.cmake")
+configure_file(
+  "${PROJECT_SOURCE_DIR}/cmake/uninstall.cmake.in"
+  "${PROJECT_BINARY_DIR}/uninstall.cmake" IMMEDIATE @ONLY)
+add_custom_target(uninstall
+  "${CMAKE_COMMAND}" -P "${PROJECT_BINARY_DIR}/uninstall.cmake")
 
 message(STATUS "Configuration successful. Type make to compile urdfdom")
 
diff --git a/cmake/pkgconfig/urdfdom.pc.in b/cmake/pkgconfig/urdfdom.pc.in
index 3c944475..7dac91d0 100644
--- a/cmake/pkgconfig/urdfdom.pc.in
+++ b/cmake/pkgconfig/urdfdom.pc.in
@@ -1,5 +1,5 @@
 # This file was generated by CMake for @PROJECT_NAME@
-prefix=@CMAKE_INSTALL_PREFIX@
+prefix=${pcfiledir}/../@RELATIVE_PATH_LIBDIR_TO_PREFIX@
 exec_prefix=${prefix}
 libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
 includedir=${prefix}/include
diff --git a/cmake/urdfdom-config.cmake.in b/cmake/urdfdom-config.cmake.in
index fb81b476..3a0916df 100644
--- a/cmake/urdfdom-config.cmake.in
+++ b/cmake/urdfdom-config.cmake.in
@@ -3,21 +3,33 @@ if (@PKG_NAME@_CONFIG_INCLUDED)
 endif()
 set(@PKG_NAME@_CONFIG_INCLUDED TRUE)
 
-set(@PKG_NAME@_INCLUDE_DIRS "@CMAKE_INSTALL_PREFIX@/include" "@TinyXML_INCLUDE_DIRS@")
+set(@PKG_NAME@_INCLUDE_DIRS "${@PROJECT_NAME@_DIR}/@RELATIVE_PATH_CMAKE_DIR_TO_PREFIX@/include" "@TinyXML_INCLUDE_DIRS@")
 
 foreach(lib @PKG_LIBRARIES@)
   set(onelib "${lib}-NOTFOUND")
+  set(onelibd "${lib}-NOTFOUND")
   find_library(onelib ${lib}
-      PATHS "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@"
-    NO_DEFAULT_PATH
-    )
-  if(NOT onelib)
+    PATHS "${@PROJECT_NAME@_DIR}/@RELATIVE_PATH_CMAKE_DIR_TO_PREFIX@/@CMAKE_INSTALL_LIBDIR@"
+    NO_DEFAULT_PATH)
+  find_library(onelibd ${lib}@CMAKE_DEBUG_POSTFIX@
+    PATHS "${@PROJECT_NAME@_DIR}/@RELATIVE_PATH_CMAKE_DIR_TO_PREFIX@/@CMAKE_INSTALL_LIBDIR@"
+    NO_DEFAULT_PATH)
+  if(onelib-NOTFOUND AND onelibd-NOTFOUND)
     message(FATAL_ERROR "Library '${lib}' in package @PKG_NAME@ is not installed properly")
   endif()
-  list(APPEND @PKG_NAME@_LIBRARIES ${onelib})
+  if(onelib AND onelibd)
+    list(APPEND @PKG_NAME@_LIBRARIES $<$<NOT:$<CONFIG:Debug>>:${onelib}>)
+    list(APPEND @PKG_NAME@_LIBRARIES $<$<CONFIG:Debug>:${onelibd}>)
+  else()
+    if(onelib)
+      list(APPEND @PKG_NAME@_LIBRARIES ${onelib})
+    else()
+      list(APPEND @PKG_NAME@_LIBRARIES ${onelibd})
+    endif()
+  endif()
+  list(APPEND @PKG_NAME@_TARGETS @PROJECT_NAME@::${lib})
 endforeach()
 
-
 foreach(dep @PKG_DEPENDS@)
   if(NOT ${dep}_FOUND)
     find_package(${dep})
@@ -25,3 +37,7 @@ foreach(dep @PKG_DEPENDS@)
   list(APPEND @PKG_NAME@_INCLUDE_DIRS ${${dep}_INCLUDE_DIRS})
   list(APPEND @PKG_NAME@_LIBRARIES ${${dep}_LIBRARIES})
 endforeach()
+
+foreach(exp @PKG_EXPORTS@)
+  include(${@PROJECT_NAME@_DIR}/${exp}Export.cmake)
+endforeach()
diff --git a/package.xml b/package.xml
new file mode 100644
index 00000000..809fdc72
--- /dev/null
+++ b/package.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<package format="2">
+  <name>urdfdom</name>
+  <version>2.3.5</version>
+  <description>A library to access URDFs using the DOM model.</description>
+  <maintainer email="steven@osrfoundation.org">Steven! Ragnarök</maintainer>
+
+  <license>BSD</license>
+
+  <author>Wim Meeussen</author>
+  <author>John Hsu</author>
+  <author>Ioan Sucan</author>
+
+  <build_depend>console_bridge_vendor</build_depend>
+  <build_depend>libconsole-bridge-dev</build_depend>
+  <build_depend>tinyxml</build_depend>
+  <build_depend>tinyxml_vendor</build_depend>
+  <build_depend version_gte="0.2.3">urdfdom_headers</build_depend>
+  
+  <buildtool_depend>cmake</buildtool_depend>
+
+  <exec_depend>console_bridge_vendor</exec_depend>
+  <exec_depend>libconsole-bridge-dev</exec_depend>
+  <exec_depend>tinyxml</exec_depend>
+  <exec_depend>tinyxml_vendor</exec_depend>
+  <exec_depend version_gte="0.2.3">urdfdom_headers</exec_depend>
+
+  <export>
+    <build_type>cmake</build_type>
+  </export>
+</package>
diff --git a/urdf_parser/CMakeLists.txt b/urdf_parser/CMakeLists.txt
index 65669c6a..6efc9554 100644
--- a/urdf_parser/CMakeLists.txt
+++ b/urdf_parser/CMakeLists.txt
@@ -1,24 +1,78 @@
-include_directories(include)
+macro(add_urdfdom_library)
+  set(oneValueArgs LIBNAME)
+  set(multiValueArgs SOURCES LINK)
+  cmake_parse_arguments(add_urdfdom_library "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
 
-add_library(urdfdom_world SHARED src/pose.cpp src/model.cpp src/link.cpp src/joint.cpp src/world.cpp)
-target_link_libraries(urdfdom_world ${TinyXML_LIBRARIES} ${console_bridge_LIBRARIES})
-set_target_properties(urdfdom_world PROPERTIES SOVERSION ${URDF_MAJOR_MINOR_VERSION})
+  add_library(${add_urdfdom_library_LIBNAME} SHARED
+    ${add_urdfdom_library_SOURCES})
+  target_include_directories(${add_urdfdom_library_LIBNAME} SYSTEM PUBLIC
+    ${TinyXML_INCLUDE_DIRS})
+  target_include_directories(${add_urdfdom_library_LIBNAME} PUBLIC
+    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
+    "$<INSTALL_INTERFACE:include>")
+  target_link_libraries(${add_urdfdom_library_LIBNAME} PUBLIC
+    ${add_urdfdom_library_LINK}
+    ${console_bridge_link_libs}
+    ${urdfdom_headers_link_libs}
+    ${TinyXML_LIBRARIES})
+  if(NOT CMAKE_CXX_STANDARD)
+    target_compile_features(${add_urdfdom_library_LIBNAME} PUBLIC cxx_std_14)
+  endif()
+  set_target_properties(${add_urdfdom_library_LIBNAME} PROPERTIES
+    DEFINE_SYMBOL URDFDOM_EXPORTS
+    SOVERSION ${URDF_MAJOR_MINOR_VERSION})
+endmacro()
 
-add_library(urdfdom_model SHARED src/pose.cpp src/model.cpp src/link.cpp src/joint.cpp)
-target_link_libraries(urdfdom_model ${TinyXML_LIBRARIES} ${console_bridge_LIBRARIES})
-set_target_properties(urdfdom_model PROPERTIES SOVERSION ${URDF_MAJOR_MINOR_VERSION})
+if(TARGET console_bridge::console_bridge)
+  set(console_bridge_link_libs "console_bridge::console_bridge")
+else()
+  set(console_bridge_link_libs "console_bridge")
+endif()
+
+if(TARGET urdfdom_headers::urdfdom_headers)
+  set(urdfdom_headers_link_libs "urdfdom_headers::urdfdom_headers")
+else()
+  set(urdfdom_headers_link_libs "")
+endif()
 
-add_library(urdfdom_sensor SHARED src/urdf_sensor.cpp)
-target_link_libraries(urdfdom_sensor urdfdom_model ${TinyXML_LIBRARIES} ${console_bridge_LIBRARIES})
-set_target_properties(urdfdom_sensor PROPERTIES SOVERSION ${URDF_MAJOR_MINOR_VERSION})
+add_urdfdom_library(
+  LIBNAME
+    urdfdom_world
+  SOURCES
+    src/pose.cpp
+    src/model.cpp
+    src/link.cpp
+    src/joint.cpp
+    src/world.cpp)
 
-add_library(urdfdom_model_state SHARED src/urdf_model_state.cpp src/twist.cpp)
-target_link_libraries(urdfdom_model_state ${TinyXML_LIBRARIES} ${console_bridge_LIBRARIES})
-set_target_properties(urdfdom_model_state PROPERTIES SOVERSION ${URDF_MAJOR_MINOR_VERSION})
+add_urdfdom_library(
+  LIBNAME
+    urdfdom_model
+  SOURCES
+    src/pose.cpp
+    src/model.cpp
+    src/link.cpp
+    src/joint.cpp)
+
+add_urdfdom_library(
+  LIBNAME
+    urdfdom_sensor
+  SOURCES
+    src/urdf_sensor.cpp
+  LINK
+    urdfdom_model)
+
+add_urdfdom_library(
+  LIBNAME
+    urdfdom_model_state
+  SOURCES
+    src/urdf_model_state.cpp
+    src/twist.cpp)
 
 # --------------------------------
 
 add_executable(check_urdf src/check_urdf.cpp)
+target_include_directories(check_urdf PUBLIC include)
 target_link_libraries(check_urdf urdfdom_model urdfdom_world)
 
 # Deprecated executable
@@ -26,21 +80,47 @@ add_executable(urdf_to_graphiz src/urdf_to_graphviz.cpp)
 target_link_libraries(urdf_to_graphiz urdfdom_model)
 
 add_executable(urdf_to_graphviz src/urdf_to_graphviz.cpp)
+target_include_directories(urdf_to_graphviz PUBLIC include)
 target_link_libraries(urdf_to_graphviz urdfdom_model)
 
 # urdf_mem_test is a binary for testing, not a unit test
 add_executable(urdf_mem_test test/memtest.cpp)
+target_include_directories(urdf_mem_test PUBLIC include)
 target_link_libraries(urdf_mem_test urdfdom_model)
 
 include(CTest)
 if(BUILD_TESTING)
+  # TODO: check Shane's comment https://github.com/ros/urdfdom/pull/157/files#r664960227
   add_subdirectory(test)
 endif()
 
-INSTALL(TARGETS urdfdom_model DESTINATION ${CMAKE_INSTALL_LIBDIR})
-INSTALL(TARGETS urdfdom_world DESTINATION ${CMAKE_INSTALL_LIBDIR})
-INSTALL(TARGETS check_urdf urdf_to_graphiz urdf_to_graphviz urdf_mem_test
-  DESTINATION ${CMAKE_INSTALL_BINDIR})
-INSTALL(TARGETS urdfdom_sensor DESTINATION ${CMAKE_INSTALL_LIBDIR})
-INSTALL(TARGETS urdfdom_model_state DESTINATION ${CMAKE_INSTALL_LIBDIR})
+INSTALL(
+  TARGETS
+  check_urdf
+  urdf_to_graphiz
+  urdf_to_graphviz
+  urdf_mem_test
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+INSTALL(
+  TARGETS
+  urdfdom_model
+  urdfdom_world
+  urdfdom_sensor
+  urdfdom_model_state
+  EXPORT
+  urdfdom
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+install(
+  EXPORT urdfdom
+  DESTINATION ${CMAKE_CONFIG_INSTALL_DIR}
+  NAMESPACE "${PROJECT_NAME}::"
+  FILE "urdfdomExport.cmake"
+)
+
 INSTALL(DIRECTORY include/urdf_parser DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
diff --git a/urdf_parser/include/urdf_parser/urdf_parser.h b/urdf_parser/include/urdf_parser/urdf_parser.h
index 247f2076..a293379e 100644
--- a/urdf_parser/include/urdf_parser/urdf_parser.h
+++ b/urdf_parser/include/urdf_parser/urdf_parser.h
@@ -44,8 +44,10 @@
 #include <tinyxml.h>
 #include <urdf_model/model.h>
 #include <urdf_model/color.h>
-#include <urdf_world/types.h>
 #include <urdf_model/utils.h>
+#include <urdf_model_state/model_state.h>
+#include <urdf_sensor/sensor.h>
+#include <urdf_world/types.h>
 
 #include "exportdecl.h"
 
@@ -144,6 +146,10 @@ namespace urdf{
   URDFDOM_DLLAPI TiXmlDocument*  exportURDF(ModelInterfaceSharedPtr &model);
   URDFDOM_DLLAPI TiXmlDocument*  exportURDF(const ModelInterface &model);
   URDFDOM_DLLAPI bool parsePose(Pose&, TiXmlElement*);
+  URDFDOM_DLLAPI bool parseCamera(Camera&, TiXmlElement*);
+  URDFDOM_DLLAPI bool parseRay(Ray&, TiXmlElement*);
+  URDFDOM_DLLAPI bool parseSensor(Sensor&, TiXmlElement*);
+  URDFDOM_DLLAPI bool parseModelState(ModelState&, TiXmlElement*);
 }
 
 #endif
diff --git a/urdf_parser/src/urdf_model_state.cpp b/urdf_parser/src/urdf_model_state.cpp
index aaaca76d..40169781 100644
--- a/urdf_parser/src/urdf_model_state.cpp
+++ b/urdf_parser/src/urdf_model_state.cpp
@@ -46,6 +46,7 @@
 #include <tinyxml.h>
 #include <console_bridge/console.h>
 
+#include <urdf_parser/urdf_parser.h>
 namespace urdf{
 
 bool parseModelState(ModelState &ms, TiXmlElement* config)
diff --git a/urdf_parser/src/urdf_sensor.cpp b/urdf_parser/src/urdf_sensor.cpp
index 29626f4d..ee863944 100644
--- a/urdf_parser/src/urdf_sensor.cpp
+++ b/urdf_parser/src/urdf_sensor.cpp
@@ -45,6 +45,8 @@
 #include <tinyxml.h>
 #include <console_bridge/console.h>
 
+#include <urdf_parser/urdf_parser.h>
+
 namespace urdf{
 
 bool parsePose(Pose &pose, TiXmlElement* xml);


Test patch:
diff --git a/urdf_parser/test/CMakeLists.txt b/urdf_parser/test/CMakeLists.txt
index 2f9f8631..98b43623 100644
--- a/urdf_parser/test/CMakeLists.txt
+++ b/urdf_parser/test/CMakeLists.txt
@@ -1,4 +1,4 @@
-include_directories (
+include_directories(
   ${CMAKE_CURRENT_SOURCE_DIR}/gtest/include
   ${CMAKE_CURRENT_SOURCE_DIR}/gtest
   ${CMAKE_CURRENT_SOURCE_DIR}
@@ -11,7 +11,6 @@ target_link_libraries(gtest_main gtest)
 
 execute_process(COMMAND cmake -E remove_directory ${CMAKE_BINARY_DIR}/test_results)
 execute_process(COMMAND cmake -E make_directory ${CMAKE_BINARY_DIR}/test_results)
-include_directories(${GTEST_INCLUDE_DIRS})
 
 # unit test to fix geometry problems
 set(tests
diff --git a/urdf_parser/test/memtest.cpp b/urdf_parser/test/memtest.cpp
index d835eb3d..86d231ea 100644
--- a/urdf_parser/test/memtest.cpp
+++ b/urdf_parser/test/memtest.cpp
@@ -3,6 +3,10 @@
 #include <iostream>
 
 int main(int argc, char** argv){
+  if (argc != 2) {
+    fprintf(stderr, "Usage: %s <urdf>\n", argv[0]);
+    return 1;
+  }
   while (true){
     std::string xml_string;
     std::fstream xml_file(argv[1], std::fstream::in);
@@ -17,4 +21,5 @@ int main(int argc, char** argv){
 
     urdf::parseURDF(xml_string);
   }
+  return 0;
 }
diff --git a/urdf_parser/test/urdf_unit_test.cpp b/urdf_parser/test/urdf_unit_test.cpp
index eb1c6658..8349b1de 100644
--- a/urdf_parser/test/urdf_unit_test.cpp
+++ b/urdf_parser/test/urdf_unit_test.cpp
@@ -7,6 +7,9 @@
 #include "urdf_model/pose.h"
 #include "urdf_parser/urdf_parser.h"
 
+#ifndef M_PI
+  # define M_PI 3.141592653589793
+#endif
 
 bool quat_are_near(urdf::Rotation left, urdf::Rotation right)
 {
@@ -190,8 +193,8 @@ TEST(URDF_UNIT_TEST, parse_joint_doubles)
 
   urdf::ModelInterfaceSharedPtr urdf = urdf::parseURDF(joint_str);
 
-  EXPECT_EQ(2, urdf->links_.size());
-  EXPECT_EQ(2, urdf->joints_.size());
+  EXPECT_EQ(2u, urdf->links_.size());
+  EXPECT_EQ(2u, urdf->joints_.size());
   EXPECT_EQ("test", urdf->name_);
 
   EXPECT_EQ(87.098, urdf->joints_["j1"]->dynamics->damping);
@@ -248,8 +251,8 @@ TEST(URDF_UNIT_TEST, parse_link_doubles)
 
   urdf::ModelInterfaceSharedPtr urdf = urdf::parseURDF(joint_str);
 
-  EXPECT_EQ(2, urdf->links_.size());
-  EXPECT_EQ(2, urdf->joints_.size());
+  EXPECT_EQ(2u, urdf->links_.size());
+  EXPECT_EQ(2u, urdf->joints_.size());
 
   EXPECT_EQ(urdf::Geometry::SPHERE, urdf->links_["l1"]->visual->geometry->type);
   std::shared_ptr<urdf::Sphere> s = std::dynamic_pointer_cast<urdf::Sphere>(urdf->links_["l1"]->visual->geometry);
@@ -310,16 +313,16 @@ TEST(URDF_UNIT_TEST, parse_color_doubles)
 
   urdf::ModelInterfaceSharedPtr urdf = urdf::parseURDF(joint_str);
 
-  EXPECT_EQ(2, urdf->links_.size());
-  EXPECT_EQ(2, urdf->joints_.size());
+  EXPECT_EQ(2u, urdf->links_.size());
+  EXPECT_EQ(2u, urdf->joints_.size());
 
   EXPECT_EQ(urdf::Geometry::SPHERE, urdf->links_["l1"]->visual->geometry->type);
   std::shared_ptr<urdf::Sphere> s = std::dynamic_pointer_cast<urdf::Sphere>(urdf->links_["l1"]->visual->geometry);
   EXPECT_EQ(1.349, s->radius);
   EXPECT_FLOAT_EQ(1.0, urdf->links_["l1"]->visual->material->color.r);
-  EXPECT_FLOAT_EQ(0.65, urdf->links_["l1"]->visual->material->color.g);
+  EXPECT_FLOAT_EQ(0.65f, static_cast<float>(urdf->links_["l1"]->visual->material->color.g));
   EXPECT_FLOAT_EQ(0.0, urdf->links_["l1"]->visual->material->color.b);
-  EXPECT_FLOAT_EQ(0.01, urdf->links_["l1"]->visual->material->color.a);
+  EXPECT_FLOAT_EQ(0.01f, static_cast<float>(urdf->links_["l1"]->visual->material->color.a));
   EXPECT_EQ("", urdf->links_["l1"]->visual->material->name);
   EXPECT_EQ("", urdf->links_["l1"]->visual->material->texture_filename);
 
@@ -328,7 +331,7 @@ TEST(URDF_UNIT_TEST, parse_color_doubles)
   EXPECT_EQ(3.349, c->radius);
   EXPECT_EQ(7.5490, c->length);
   EXPECT_FLOAT_EQ(1.0, urdf->links_["l2"]->visual->material->color.r);
-  EXPECT_FLOAT_EQ(0.0001, urdf->links_["l2"]->visual->material->color.g);
+  EXPECT_FLOAT_EQ(0.0001f, static_cast<float>(urdf->links_["l2"]->visual->material->color.g));
   EXPECT_FLOAT_EQ(0.0, urdf->links_["l2"]->visual->material->color.b);
   EXPECT_FLOAT_EQ(1.0, urdf->links_["l2"]->visual->material->color.a);
   EXPECT_EQ("red ish", urdf->links_["l2"]->visual->material->name);
diff --git a/urdf_parser/test/urdf_version_test.cpp b/urdf_parser/test/urdf_version_test.cpp
index b1f1e259..e6735f9f 100644
--- a/urdf_parser/test/urdf_version_test.cpp
+++ b/urdf_parser/test/urdf_version_test.cpp
@@ -88,16 +88,16 @@ TEST(URDF_VERSION_CLASS, test_null_ptr)
 {
   urdf_export_helpers::URDFVersion vers1(nullptr);
 
-  EXPECT_EQ(vers1.getMajor(), 1);
-  EXPECT_EQ(vers1.getMinor(), 0);
+  EXPECT_EQ(vers1.getMajor(), 1u);
+  EXPECT_EQ(vers1.getMinor(), 0u);
 }
 
 TEST(URDF_VERSION_CLASS, test_correct_string)
 {
   urdf_export_helpers::URDFVersion vers2("1.0");
 
-  EXPECT_EQ(vers2.getMajor(), 1);
-  EXPECT_EQ(vers2.getMinor(), 0);
+  EXPECT_EQ(vers2.getMajor(), 1u);
+  EXPECT_EQ(vers2.getMinor(), 0u);
 }
 
 TEST(URDF_VERSION_CLASS, test_too_many_dots)