Using wrappers
This part provides a detailed description on the usage of PID methodology’s core concepts and tools used to deal with wrappers.
Wrapper definition
When developing a PID wrapper one needs to handle information such as:
- meta-information : who is involved in its development ? what is its general purpose ? where to find this package on the network ? what is the license of the code ? etc.
- build-related information : what are the components provided by the wrapper and how to compile/link them from source code ? How components are tested ? what is the version of the package ? what are its functional dependencies ?
The whole package development is organized and described with CMake, and the CMakeLists.txt files used contain the whole meta-information and build-related information used by the wrapper. Each package contains several CMakeLists.txt files that are generally used to define components :
- the root
CMakeLists.txtfile is used to define meta-information and dependencies of the wrapper. - the
CMakeLists.txtfiles contained in each version subfolder of thesrcfolder defines the components available for each version of the external package that is known into PID. They also reference a cmake deploy script that will be used to download, build and install the given version of the external project.
In the context of PID, components are software artefacts generated and installed by a package or a wrapper. Each component may require other components from another external package and so may explicitly define dependencies between wrappers. The primary role of PID is to manage these dependencies.
General description
The wrapper general description takes place in the root CMakeLists.txt. This file contains the description of meta-information of the wrapper.
Let’s suppose we define a wrapper for yaml-cpp, its root CMakeLists.txt could look like:
cmake_minimum_required(VERSION 3.0.2) #minimum version of CMake to use PID
set(WORKSPACE_DIR ${CMAKE_SOURCE_DIR}/../.. CACHE PATH "root of the packages workspace directory")
list(APPEND CMAKE_MODULE_PATH ${WORKSPACE_DIR}/cmake) #registereing PID CMake scripts
include(Wrapper_Definition NO_POLICY_SCOPE) # use the PID API
project(yaml-cpp)
PID_Wrapper(
AUTHOR Robin Passama
MAIL passama@lirmm.fr
INSTITUTION CNRS / LIRMM: Laboratoire d'Informatique de Robotique et de Microélectronique de Montpellier, www.lirmm.fr
ADDRESS git@gite.lirmm.fr:pid/yaml-cpp.git
PUBLIC_ADDRESS https://gite.lirmm.fr/pid/yaml-cpp.git
YEAR 2018
LICENSE MIT
DESCRIPTION "a YAML parser library for C++"
)
PID_Original_Project(
AUTHORS "yaml-cpp authors"
LICENSES "MIT License"
URL https://code.google.com/p/yaml-cpp/)Explanations:
-
Exactly as in any CMake project, the CMake project is defined with the
PROJECTkeyword. The CMake project’s name must be the same as the name of the git repository and must be the same as the project root folder name. The previous lines must be let unchanged (initialization of the PID specific cmake API). -
Then comes the
PID_Wrapper(equivalent todeclare_PID_Wrapper) macro, which is mandatory. This macro defines general meta-information on the wrapper, and should not change a lot during wrapper life cycle:- the main author (
AUTHORkeyword) and its institution (INSTITUTIONoptional keyword), as well as his/her email (MAILkeyword), considered as the maintainer of the wrapper . - the
YEARfield helps defining the wrapper’s life cycle range. - the
LICENSEfield is used to specify the license that applies to the wrapper code. This license must be defined in the workspace in the form of a license file. - the
ADDRESSfield is used to specify the address of the official GIT repository of the wrapper. - the
PUBLIC_ADDRESSfield is used to specify the public address of the same official GIT repository of the wrapper, public meaning that the clone can be done without any identification. - the
DESCRIPTIONfield must be filled with a short description of the wrapper usage/utility.
- the main author (
-
Then the user fill meta-information about original external project by using
PID_Original_Project(or equivalent long signaturedefine_PID_Wrapper_Original_Project_Info):- the
AUTHORSkeyword help defining who are the authors of the original project, in order to clearly identify them. - the
LICENSESkeywork is used to list licenses applying to the original project code. - the
URLkeyword document where to find online documentation about original project.
- the
Managed versions description
Then you have to create version subfolders in the src folder of the wrapper. Each of these folders contains the the deployment procedure of the external project and the description of the content resulting from its build process. The whole version is described in a CMakeLists.txt that looks like this:
#declaring a new known version
PID_Wrapper_Version(VERSION 0.5.1 DEPLOY deploy.cmake
SONAME 0.5 #define the extension name to use for shared objects
)
#now describe the content
PID_Wrapper_Configuration(CONFIGURATION posix) #depends only on posix
PID_Wrapper_Dependency(boost)#any version of boost can be used as we use only headers
#component for shared library version
PID_Wrapper_Component(libyaml
INCLUDES include SHARED_LINKS lib/libyaml-cpp
EXPORT boost/boost-headers
posix
)
#component for static library version
PID_Wrapper_Component(libyaml-st
INCLUDES include STATIC_LINKS lib/libyaml-cpp.a
EXPORT boost/boost-headers
posix
)Explanations:
PID_Wrapper_Version(equivalent signature isadd_PID_Wrapper_Known_Version) is used to declare the new version (usingVERSIONkeyword) and general information about it:- The
VERSIONmust be the same as the name of the containing folder. - The
DEPLOYis mandatory used to define the path to the cmake script file that implements the deploy procedure. The path is relative to the current version folder. - The
SONAMEmay be used to define which version number is used in SONAME of shared objects generated by the external project.
- The
- Then platform configurations constraints are defined using
PID_Wrapper_Configuration(equivalent todeclare_PID_Wrapper_Platform_Configuration), exactly the same way as for native packages. - Then dependencies to other external packages wrappers are defined using
PID_Wrapper_Dependency(equivalent todeclare_PID_Wrapper_External_Dependency), the same way as for native packages. - Then components generated by the external project are described using
PID_Wrapper_Component:COMPONENTkeyword defines the PID name of the componentINCLUDESkeyword lists the include folders (if any) containing public headers of the component.SHARED_LINKSand/orSTATIC_LINKSlist the binaries and linker options of a library. Many binaries can be listed. All path used are either absolute (starting with a /) or relative to the external package version install folder.- dependencies for these components are defined with
DEPENDorEXPORTkeywords. In this exampleyaml-cpprequiresboost-headerscomponent provided by theboostwrapper as well as libraries provided by theposixconfiguration.
Then you need at least to define a deployment script, as explained in PID_Wrapper_Version. These deployment script are really scpecific to the considered external project being wrapped in PID. Here is an example of such a script for yaml-cpp wrapper version 0.5.1:
# all platform related variables are passed to the script
# TARGET_BUILD_DIR is provided by default (where the build of the external package takes place)
# TARGET_INSTALL_DIR is also provided by default (where the external package is installed after build)
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)
get_External_Dependencies_Info(PACKAGE boost ROOT root_folder INCLUDES boost_include)
build_CMake_External_Project( PROJECT yaml-cpp FOLDER yaml-cpp-release-0.5.1 MODE Release
DEFINITIONS BUILD_SHARED_LIBS=ON Boost_INCLUDE_DIR=${boost_include} BOOST_INCLUDEDIR=${boost_include} BOOST_ROOT=${root_folder} BOOSTROOT=${root_folder}
COMMENT "shared libraries")
build_CMake_External_Project( PROJECT yaml-cpp FOLDER yaml-cpp-release-0.5.1 MODE Release
DEFINITIONS BUILD_SHARED_LIBS=OFF Boost_INCLUDE_DIR=${boost_include} BOOST_INCLUDEDIR=${boost_include} BOOST_ROOT=${root_folder} BOOSTROOT=${root_folder}
"CMAKE_CXX_FLAGS=-fPIC -fvisibility=hidden"
COMMENT "static libraries")
if(EXISTS ${TARGET_INSTALL_DIR}/lib64)
execute_process(COMMAND ${CMAKE_COMMAND} -E rename ${TARGET_INSTALL_DIR}/lib64 ${TARGET_INSTALL_DIR}/lib)
endif()
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.")
return_External_Project_Error()
endif()Explanations:
The build process consists in first downloading and extracting the archive containing source files (using install_External_Project function). Then since yaml-cpp requires boost we need to get the information where to find boost in local workspace using function get_External_Dependencies_Info. Path to boost root folder and include dirs are put into CMake variable that we need to pass to the yaml-cpp project build process to compile it with adequate build options.
Finally yaml-cpp is configured and build using build_CMake_External_Project function. There are different function for the different build system already managed into PID. We do this two times: one time for building the static library and another one for shared library. One nice thing is taht we can pass CMake variables definitions (using DEFINITIONS), for instance to configure the Boost_INCLUDE_DIR folder variable of the original yaml-cpp CMake project.
For other external package wrapper one needs to follow at best the same procedure.
Wrapper build process control
PID wrappers provide a set of cache variables that are used to control their build process. The configuration of these CMake cache variables is basically made using ccmake .. command in the build directory of the wrapper or by using other Cmake configuration GUI. Contrarily to native packages, wrapper do not provide default options, but they can define user specific options using dedicated API in the root CMakeLists.txt:
PID_Wrapper_Option(
OPTION BUILD_WITH_CUDA_SUPPORT
TYPE BOOL
DEFAULT OFF
DESCRIPTION "set to ON to enable CUDA support during build")Explanations:
The wrapper defines an option called BUILD_WITH_CUDA_SUPPORT that is a boolean that default to off.
This user option can be consulted in deploy script of versions in order to control the build process, this way:
... # cmake code used to download/get the external project source files
get_User_Option_Info(OPTION BUILD_WITH_CUDA_SUPPORT RESULT using_cuda) #using_cuda variable is set with the value of the option
if(using_cuda)
... # build using CUDA
else()
... # build without CUDA
endif()Understanding the result
From the complete build/install process an “installed version” of the wrapper is generated and put into the adequate folder of the workspace. The install process is managed by PID system so developer should not worry about how it takes place.

The figure provides an example of the workspace’s install folder containing yaml-cpp installed external packages wrappers for version 0.5.1 for a given platform (x86_64_linux_stdc++11). There can be many versions of the same wrapper installed in the workspace for the same platform.
Each binary package version folder content is specific to the external project, but there is always a cmake use file (a file generated and used by PID to get information about the installed wrapper) is the share folder.
Generally speaking users should never change the content of any of those folders “by hand”, otherwise it could cause troubles.