In this tutorial we will show how an external package can define its dependencies. There is basically 2 ways:

  • dependencies to other external packages.
  • explicit platform requirement through configurations.

Step 5 : add a new managed version

We continue to work on the yaml-cpp project defined in previous steps. This time we want to port another existing version of this external project: version 0.5.1. In this earlier version than the previously defined one, yaml-cpp depends on another external package called boost. boost is a famous project aiming at providing useful APIs to C++ programmers.

5.1 : create a new version folder in the wrapper

cd <yaml-cpp>/src
mkdir 0.5.1
touch 0.5.1/CMakeLists.txt
touch 0.5.1/deploy.cmake

5.2 : define deployment procedure (in src/0.5.1/deploy.cmake):

install_External_Project( PROJECT yaml-cpp
                          VERSION 0.5.1
                          URL https://github.com/jbeder/yaml-cpp/archive/release-0.5.1.tar.gz
                          ARCHIVE yaml_0.5.1.tar.gz
                          FOLDER yaml-cpp-release-0.5.1)

if(NOT ERROR_IN_SCRIPT)
    build_CMake_External_Project( PROJECT yaml-cpp FOLDER yaml-cpp-release-0.5.1 MODE Release
                            DEFINITIONS BUILD_SHARED_LIBS=ON
    )

  if(NOT EXISTS ${TARGET_INSTALL_DIR}/lib OR NOT EXISTS ${TARGET_INSTALL_DIR}/include)
    message("[PID] ERROR : during deployment of yaml-cpp version 0.5.1, cannot install yaml-cpp in worskpace.")
    set(ERROR_IN_SCRIPT TRUE)
  endif()
endif()

As you may have notice there is less user entries set by build_CMake_External_Project. This is because version 0.5.1 of yaml-cpp does not define these cache entries.

5.3 : define resulting components (in src/0.5.1/CMakeLists.txt):

PID_Wrapper_Version(VERSION 0.5.1 DEPLOY deploy.cmake SONAME 0.5)
PID_Wrapper_Component(COMPONENT libyaml INCLUDES include SHARED_LINKS lib/libyaml-cpp)

5.4 build the version 0.5.1

Now that the description is completed, we can try configuring and building the package.

cd <pid-workspace>/wrappers/yaml-cpp
pid build version=0.5.1

You can face two situations:

  • the build failed. This is surely explainable by the fact that boost is not installed in your operating system. This way you saw that there is an implicit dependency between yaml-cpp and boost projects.
  • the build succeeded. Well this is in fact not a so good situation because you did not see that there is an implicit dependency !

Step 6 : add a dependency to an external package

What we need to do now is to specify the dependency between yaml-cpp and boost projects. We will specify this dependency as an external dependency. This is possible because boost is provided as an external package in PID, in other words a wrapper exists for this project.

6.1 : describe the dependency (in src/0.5.1/CMakeLists.txt):

PID_Wrapper_Version(VERSION 0.5.1 DEPLOY deploy.cmake SONAME 0.5)      )
PID_Wrapper_Dependency(PACKAGE boost)#any version of boost can be used as we use only headers
PID_Wrapper_Component(COMPONENT libyaml INCLUDES include SHARED_LINKS lib/libyaml-cpp)
PID_Wrapper_Component_Dependency(COMPONENT libyaml EXPORT EXTERNAL boost-headers PACKAGE boost)

or shorter:

PID_Wrapper_Version(VERSION 0.5.1 DEPLOY deploy.cmake SONAME 0.5)      )
PID_Wrapper_Dependency(PACKAGE boost)#any version of boost can be used as we use only headers
PID_Wrapper_Component(COMPONENT libyaml INCLUDES include SHARED_LINKS lib/libyaml-cpp
                      EXPORT boost/boost-headers)

There are two operation to do, quite the same way as for native packages:

  • define the dependency between external packages using the PID_Wrapper_Dependency command. In the example yaml-cpp depends on any version of boost external package.
  • define the dependencies between components of yaml-cpp and components of boost, using either PID_Wrapper_Component_Dependency or EXPORT/DEPEND argument of PID_Wrapper_Component. In the example libyaml depends on boost-headers. We use the keyword EXPORT because some headers of boost are included in public headers of yaml-cpp. To detect that we did:
cd <pid-workspace>/wrappers/yaml-cpp/build/0.5.1/yaml-cpp-release-0.5.1
grep -nr boost include/

In output:

./yaml-cpp/noncopyable.h:12:	// this is basically boost::noncopyable
./yaml-cpp/node/detail/node_ref.h:13:#include <boost/utility.hpp>
./yaml-cpp/node/detail/node_ref.h:19:		class node_ref: private boost::noncopyable
./yaml-cpp/node/detail/impl.h:11:#include <boost/type_traits.hpp>
./yaml-cpp/node/detail/impl.h:25:		struct get_idx<Key, typename boost::enable_if_c<boost::is_unsigned<Key>::value && !boost::is_same<Key, bool>::value>::type> {
./yaml-cpp/node/detail/impl.h:40:		struct get_idx<Key, typename boost::enable_if<boost::is_signed<Key> >::type> {
./yaml-cpp/node/detail/node_iterator.h:11:#include <boost/iterator/iterator_facade.hpp>
./yaml-cpp/node/detail/node_iterator.h:12:#include <boost/utility/enable_if.hpp>
./yaml-cpp/node/detail/node_iterator.h:54:		class node_iterator_base: public boost::iterator_facade<
...

For instance we see that the header boost/utility.hpp is included in yaml-cpp/node/detail/node_ref.h.

6.2 : modify deploy script:

install_External_Project( PROJECT yaml-cpp
                          VERSION 0.5.1
                          URL https://github.com/jbeder/yaml-cpp/archive/release-0.5.1.tar.gz
                          ARCHIVE yaml_0.5.1.tar.gz
                          FOLDER yaml-cpp-release-0.5.1)

if(NOT ERROR_IN_SCRIPT)
  get_External_Dependencies_Info(INCLUDES all_includes) #getting info coming from dependencies
  build_CMake_External_Project( PROJECT yaml-cpp FOLDER yaml-cpp-release-0.5.1 MODE Release
                          DEFINITIONS BUILD_SHARED_LIBS=ON Boost_INCLUDE_DIR=${all_includes}
                          )

  if(NOT EXISTS ${TARGET_INSTALL_DIR}/lib OR NOT EXISTS ${TARGET_INSTALL_DIR}/include)
    message("[PID] ERROR : during deployment of yaml-cpp version 0.5.1, cannot install yaml-cpp in worskpace.")
    set(ERROR_IN_SCRIPT TRUE)
  endif()
endif()

In previous description we changed 2 things:

  • call to get_External_Dependencies_Info(INCLUDES all_includes) is used to get all adequate include directives that are necessary to use dependencies.
  • argument Boost_INCLUDE_DIR=${all_includes} is passed to CMake definitions of the external project. It simply sets the include folder where to find boost. In this example boost being the only dependency we can directly use all includes variable as this later contains only path to boost include folder. This argument is required by the CMake script of the original project to configure boost adequately. Here the boost version used will be the one provided by the PID workspace.

6.3 build again the version 0.5.1

cd <pid-workspace>/wrappers/yaml-cpp
pid build version=0.5.1

This time everything should work as expected and furthermore the build process may have automatically deploy the boost external package.

Step 7 : define a required configuration for target platform

Another way to deal with dependencies is by using platform configurations, as for native packages. Using this kind of dependencies should be limited as far as possible but is sometimes required or more easy to use than external packages.

This last step of the tutorial simply shows how to write the dependency to boost as a check of the platform configuration. Indeed a configuration called boost is also available in PID and is mainly used to check which version of the external project boost has been installed in system (probably using system packager). But in this example we will use it directly to get also all the compilation/linker flags used to configrue libyaml.

The tutorial consists in rewriting the previous example for version 0.5.1 of yaml-cpp.

7.1 : describe the dependency (in src/0.5.1/CMakeLists.txt):

PID_Wrapper_Version(VERSION 0.5.1 DEPLOY deploy.cmake SONAME 0.5)
PID_Wrapper_Configuration(CONFIGURATION boost)
PID_Wrapper_Component(COMPONENT libyaml INCLUDES include boost_INCLUDE_DIRS SHARED_LINKS lib/libyaml-cpp)

What changed:

  • the call to PID_Wrapper_Configuration explicitly checks if the target platform has boost system library installed. This call generates various variables, including boost_INCLUDE_DIRS that contains the path to the include folder for boost.
  • the call to PID_Wrapper_Component now adds the variable boost_INCLUDE_DIRS to the set of includes exported by the project. We use the variable instead of its value (i.e. boost_INCLUDE_DIRS rather than ${boost_INCLUDE_DIRS}) because we want the resulting binary package to be relocatable: the variable boost_INCLUDE_DIRS will be interpreted anytime the binary package of yaml-cpp will be used while if its value was used this variable would be only interpreted when the external project is built and so will match only the current build environment in use).

7.2 : build the project

There is no more modification required because the deployment procedure is still valid. So we simply build the project:

cd <pid-workspace>/wrappers/yaml-cpp
pid build version=0.5.1

Everything should work as expected.

7.3 : Discussion

One bad thing about the previous design is that we will face troubles anytime we need to use yaml-cpp because it will require to use system version of boost which can conflict with other packages using the PID version. Furthermore it is more convenient to use external project wrapper because they define the components and so dependency are far more simple to write.

What we would like is to be able to use the system version “as if” it was a PID version. This way we would be able to use the description of boost components. To do this we need to change a bit the description:

PID_Wrapper_Version(VERSION 0.5.1 DEPLOY deploy.cmake SONAME 0.5)
PID_Wrapper_Configuration(CONFIGURATION boost)
PID_Wrapper_Dependency(boost VERSION SYSTEM)
PID_Wrapper_Component(COMPONENT libyaml INCLUDES include SHARED_LINKS lib/libyaml-cpp
                      EXPORT boost/boost-headers)

In this later example the platform configuration boost is still checked (see PID_Wrapper_Configuration). This check is performed to ensure that boost is really installed in operating system.

Then an explicit dependency to the boost external package is specified (PID_Wrapper_Dependency), and this time there is a version constraint to SYSTEM version. The SYSTEM keyword simply tells that the dependency version is the version installed in operating system and retrieved (automatically) using boost configuration. So we have to memorize that using the SYSTEM keyword for version constraint requires the configuration with same name to exist. In the end the call to PID_Wrapper_Configuration is optional because the call to PID_Wrapper_Dependency with SYSTEM version constraint will do the job automatically.

Using the SYSTEM version constraint is possible since external project wrappers are provided with a way to build equivalent system version of a given PID version. You can test it on boost wrapper by doing:

cd <pid-workspace>/wrappers/boost
pid build version=1.58.0 os_variant=true

The use of os_variant argument:

  • will check if the version of boost installed in operating system matches the version to build.
  • if everything is OK then the command generates an external package for this boost version but without using the deploy script : instead of compiling boost it rather generates symlinks to equivalent binary components and include folders that are installed in system.

This way users can use packages description provided for the given version of boost while in fact using the boost version installed in operating system.

Main problem with previous description is that the yaml-cpp version defined must use the OS installed version of boost in the end, which is not so flexible. We want to be able to use any version of boost whether it is OS installed or PID built one. To do this we simply have to go back to the initial description:

PID_Wrapper_Version(VERSION 0.5.1 DEPLOY deploy.cmake SONAME 0.5)
PID_Wrapper_Dependency(boost)
PID_Wrapper_Component(COMPONENT libyaml INCLUDES include SHARED_LINKS lib/libyaml-cpp
                      EXPORT boost/boost-headers)

Indeed with this description we just say that any version of boost can be used to build yaml-cpp and this also include OS installed version !! The choice of the version and the variant is let to the build process dependencing on constraints coming from packages using yaml-cpp as a dependency.