What is a module ?

A module consists in a shared library embedded a set of SolAR components as well as the corresponding factories to instantiate them. The management of modules is based on the XPCF third party providing a lightweight cross platform component framework. XPCF provides the following features to manage SolAR modules:

  • introspection to figure out which components are available in the module

  • separate implementation from interface to create the concrete implementation of a SolAR component and to bind it to an abstract SolAR and XPCF component interfaces.

  • component creation

Each module implementation is identified with a Universally Unique IDentifier (UUID) to nearly ensure the uniqueness of a component implementation when the system instantiates it.

As mentionned previoulsy, the third party XPCF can introspect the shared library of a module to obtain information about the embedded components and the interfaces they implement. Only, the introspection of a shared library requires to load it, what could become tricky when the shared library has been built on a different platform than yours (useful for authoring tools supporting cross-platform compilation such as Unity). For this reason, XPCF proposes to associate to each module a registry file in xml format that describes the module with:

  • the module UUID,

  • the components embedded in the module with their UUID,

  • the abstract component interfaces implemented by the components.

Thus, any system will be able to intropsect a module without the need to load the corresponding shared library.

Create a module with the wizard

We are aware that creating a module and adding components to it can be a little tedious. A QTCreator wizard should soon be available to automate file generation and make the module creation easier for you. But for now, follow the instructions below to create a module step-by-step.

Create a module step-by-step

Create a module project

The SolAR framework provides build pipeline tools to ease the cross platform compilation and deployment. As Qmake and CMake have both advantages and limitations, we recommend to provide for any SolAR module both a .pro to work on QTCreator as well as a CMake file to work for example on Visual Studio or other IDE.

QT Creator

Open QTCreator and create a new project (in File menu).

Choose Library and C++ Library.

create a shared C++ library in QT
Figure 1. Project creation

Then, set the name of your project with the name of your module, set the location (a dedicated folder with all module projects is recommended) and select "Shared Library" for the type of the library.

select library type
Figure 2. Project name, location and library type

Next, select your development kits. We recommend to use MSVC 2017 64bit or Clang. Then, unselect all required modules. On the next window, keep the name of the class to create with the name of your module. Unfortunately, with the QT project creation wizard, you cannot set dedicated folders for headers and sources. As we recommend to put the header files in a "interfaces" folder and the source files in a "src" folder, you will have to create these two folders manually and move the two files (MyModule.h and MyModule.cpp) in their respective folder (interfaces and src). Finally go to the summary and click on "Finish".

SolAR provide a smart and easy to use build and deployment system.

Now, you need to select the file called "MyModule.pro" in your project tree, and replace the text by the following one:

MyModule.pro
## remove Qt dependencies
QT       -= core gui
CONFIG -= qt

## global defintions : target lib name, version
TARGET = MyModule (1)
FRAMEWORK = $$TARGET
VERSION=x.x.x (2)

DEFINES += MYVERSION=$${VERSION}
DEFINES += TEMPLATE_LIBRARY
CONFIG += c++1z


CONFIG(debug,debug|release) {
    DEFINES += _DEBUG=1
    DEFINES += DEBUG=1
}

CONFIG(release,debug|release) {
    DEFINES += _NDEBUG=1
    DEFINES += NDEBUG=1
}

DEPENDENCIESCONFIG = shared recurse

include (../../builddefs/qmake/templatelibconfig.pri) (5)

## DEFINES FOR MSVC/INTEL C++ compilers
msvc {
DEFINES += "_BCOM_SHARED=__declspec(dllexport)"
}

INCLUDEPATH += interfaces/

HEADERS += interfaces/MyModule.h (3)

SOURCES += src/MyModule.cpp (4)

unix {
    QMAKE_CXXFLAGS += -Wignored-qualifiers
    QMAKE_LINK=clang++
    QMAKE_CXX = clang++
}

macx {
    DEFINES += _MACOS_TARGET_
    QMAKE_MAC_SDK= macosx
    QMAKE_CFLAGS += -mmacosx-version-min=10.7 -std=c11 #-x objective-c++
    QMAKE_CXXFLAGS += -mmacosx-version-min=10.7 -std=c11 -std=c++11 -O3 -fPIC#-x objective-c++
    QMAKE_LFLAGS += -mmacosx-version-min=10.7 -v -lstdc++
    LIBS += -lstdc++ -lc -lpthread
}

win32 {

    DEFINES += WIN64 UNICODE _UNICODE
    QMAKE_COMPILER_DEFINES += _WIN64
    QMAKE_CXXFLAGS += -wd4250 -wd4251 -wd4244 -wd4275
}

header_files.path = $${PROJECTDEPLOYDIR}/interfaces
header_files.files = $$files($${PWD}/interfaces/*.h*)

# HOME variable corresponds to C:\user\<yourUsername>
xpcf_xml_files.path = $$(HOME)/.xpcf/SolAR
xpcf_xml_files.files=$$files($${PWD}/xpcf*.xml)

INSTALLS += header_files
INSTALLS += xpcf_xml_files

Now, just update the .pro file:

1 replace the TARGET by the name of your module,
2 set the version number of your module,
3 change the name of your header file when it is added to the HEADERS variable,
4 change the name of your source file when it is added to the SOURCES_ variable.
5 check if the builddefs folder used to define the compilation pipeline is well referenced
In order to deploy your module in the binary folder ${REMAKENROOT}/MyModule/MyModuleVersion after building, in QT creator click in the left menu on Projects, then on Build, on add a build step and select Make. Set the Make Argument to the value install. Do it for both release and debug mode.

 — Using Cmake instead of .pro QT file — 

Create a CMakeLists.txt file and copy the following code to it. This file use a temporarily BCOMDEVROOT environment variable link to your SoLAR sources.

CMakeLists.txt
cmake_minimum_required(VERSION 3.7.2)

##################################################
set (VERSION_NUMBER "x.x.x") (2)
project("MyModule") (1)

set (HEADERS
    interfaces/MyModule.h
) (3)

set (SOURCES
    src/MyModule.cpp
) (4)

install (FILES "${CMAKE_CURRENT_SOURCE_DIR}/xpcf_MyModule_registry.xml" DESTINATION $ENV{BCOMDEVROOT}/.xpcf/SolAR/	) (5)

##################################################

# various macros
include("$ENV{BCOMDEVROOT}/bcomBuild/SolARFramework/solarmacros.cmake") (6)
# config setup
setup()
# process packagedependencies.txt
processPackagedependencies()
# define targets (type library)
defineTargets("library" "")

now, just update the CMakeLists.txt file:

1 set the name of the project with the name of your module,
2 set the version number of your module,
3 change the name of your header file when it is added to the HEADERS variable,
4 change the name of your source file when it is added to the SOURCES_ variable.
5 indicate the name of your registry file in your porject folder to copy it to the binary folder ${BCOMDEVROOT}/MyModule/MyModuleVersion.
6 check that the solarmacros.cmake file exists. This file must be install when you have launched the installer. If you decide to build the framework by yourself, this file will be copied in your ${BCOMDEVROOT} folder when you will build and install the SolARFramework project.

Configure your dependencies

As mentionned previously, SolAR framework provides developers with a compilation pipeline allowing among other things to easily manage dependencies (download, version management, packaging during deployment step, etc.).

To define the dependencies used by your module, create a file called packagedependencies.txt at the root of your project.

Following, an example of packagedependencies.txt file for the OpenCV module:

/.packagedependencies.txt

xpcf|2.2.0|xpcf|artifactory|https://repository.b-com.com/amc-generic
spdlog|0.14.0|spdlog|thirdParties|https://github.com/SolarFramework/binaries/releases/download
eigen|3.3.5|eigen|thirdParties|https://github.com/SolarFramework/binaries/releases/download
SolARFramework|0.6.0|SolARFramework|github|https://github.com/SolarFramework/SolarFramework/releases/download
SolARModuleTools|0.6.0|SolARModuleTools|github|https://github.com/SolarFramework/SolARModuleTools/releases/download
SolARModuleOpenCV|0.6.0|SolARModuleOpenCV|github|https://github.com/SolarFramework/SolARModuleOpenCV/releases/download
opencv|3.4.3|opencv|thirdParties|https://github.com/SolarFramework/binaries/releases/download

Here is the syntax for each dependency:

dependency_name|dependency_version|dependency_folder_name|dependency_path|dependency_download_url

artifactory refers to .remaken/packages/<yourCompiler>/ folder. You can create a REMAKENROOT variable. All process are made in this folder.

xpcf, boost, spdlog, eigen and SolARFramework are mandatory third parties for creating a module.

Create mandatory header and source FILES

2 header files and 1 source file are mandatory to create a module.

Do not forget to add a reference to the three next files required to create a module in the .pro and in the CMakeLists.txt.

MyModuleAPI.h

Create a new header file in the interfaces folder of your project named MyModuleAPI.h, where of course MyModule will be replaced by the name of your module. This file will define the macro MYMODULE_EXPORT_API you will have to place in front of each component interface to export with your shared library. So copy the following code in your new MyModuleAPI.h file:

MyModuleAPI.h
/**
 * Your header defining your copyright and your license
 */

#ifndef MYMODULE_API_H
#define MYMODULE_API_H

#if _WIN32
#ifdef MyModule_API_DLLEXPORT
#define MYMODULE_EXPORT_API __declspec(dllexport)
#else //MYMODULE_API_DLLEXPORT
#define MYMODULE_EXPORT_API __declspec(dllimport)
#endif //MYMODULE_API_DLLEXPORT
#else //_WIN32
#define MYMODULE_EXPORT_API
#endif //_WIN32
#include "MyModule_traits.h"
#endif //MYMODULE_API

Of course, replace "MyModule" by your module name in respecting the case used in this file.

MyModule_traits.h

Now, you will have to create a second header file named MyModule_traits.h in your interfaces folder (again, replace MyModule with the name of your module). This header will be the unique one to reference in order to use one or more components embedded in your module.

MyModule_traits.h
/**
 * Your header defining your copyright and your license
 */

#ifndef MYMODULE_TRAITS_H
#define MYMODULE_TRAITS_H

#include "xpcf/core/traits.h"

namespace SolAR {
namespace MODULES {
namespace MYMODULE {

}
}
}

#endif // MYMODULE_TRAITS_H

For now, this file is quite empty, but you will see in a next step that we will define in it a trait for each component you will create and add to your module (Create a component).

MyModule.cpp

Finally, create in the src folder of your project a source file named MyModule.cpp. This file has three functions: * it declares the module with a UUID, a name and a description, * it is used by the XPCF component factory to create the components of the module, * it declares the components embedded in the module.

Copy and paste the following code in it (again replace MyModule by the name of your module):

MyModule.cpp
/**
 * Your header defining your copyright and your license
 */

#include "xpcf/module/ModuleFactory.h"
#include "MyModule_traits.h"

namespace xpcf=org::bcom::xpcf;

XPCF_DECLARE_MODULE("MyModule UUID", "MyModule", "MyModule description"); (1)

extern "C" XPCF_MODULEHOOKS_API xpcf::XPCFErrorCode XPCF_getComponent(const boost::uuids::uuid& componentUUID,SRef<xpcf::IComponentIntrospect>& interfaceRef)
{
    xpcf::XPCFErrorCode errCode = xpcf::XPCFErrorCode::_FAIL;

    return errCode;
}

XPCF_BEGIN_COMPONENTS_DECLARATION

XPCF_END_COMPONENTS_DECLARATION
1 Replace MyModule UUID by a Universally Unique IDentifier. To get one, you can use an online UUID generator. Please, provide a detailed description of your module that could be introspected by authoring tools.

Create a registry file

As mentionned previoulsy, a shared library has to be loaded for introspection, what is quite tricky when it has been built on a platform different from yours. For this reason, we always associate to a module a registry file describing information about the module itself and its components.

Create an xml file, name it xpcf_MyModule_registry.xml, and copy the following code in it (again replace MyModule with the name of your module):

xpcf_MyModule_registry.xml
<xpcf-registry>
<module uuid="MyModule_UUID" name="MyModule_Name" description="MyModule_Description" path="$REMAKENROOT/MyModule/MyModule_version/lib/x86_64/shared"> (1)

</module>
</xpcf-registry>
1 Copy and paste the module UUID, name and description defined in the file MyModule.cpp. Update the path by replacing MyModule by the name of your module and MyModule_version by the version number of your module defined in your .pro and in your CMakeLists.txt files.

This file is somewhat empty, but will be completed when adding components.

Create a pkg-config file

The SolAR compilation pipeline use pkg-config that helps to insert the correct compiler options on the command line so an application can use gcc -o test test.c pkg-config --libs --cflags glib-2.0 for instance, rather than hard-coding values on where to find glib (or other libraries).

Create a file in your project root folder named bcom-MyModule.pc.in and copy the following code in it:

libname=MyModule (1)
prefix=/usr/local
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/interfaces
Name: MyModule (2)
Description: MyModule description (3)
Version: MyModuleVersion (4)
Requires:
Libs: -L${libdir} -l${libname}
Libs.private: ${libdir}/${pfx}${libname}.${lext}
Cflags: -I${includedir}
1 The shared library name of your module defined in your .pro and CMakeLists.txt files.
2 The description of the module such as the one defined in MyModule.cpp.
3 The name of your module.
4 the version number of your module defined in your .pro and CMakeLists.txt files.

When you will install your module, this file will be directly copied to your binary folder ${REMAKENROOT}/MyModule/MyModule_Version.