What is a module ?

A module consists of a shared library embedding a set of SolAR components as well as the corresponding factory parameters to instantiate them. The management of modules is based on the XPCF third party providing a lightweight cross platform 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 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 introspect a module without the need to load the corresponding shared library.

Create a module with the wizard

Download QTCreator wizards for XPCF

Creating a new module from scratch can be a little bit tricky. To help you, a QTCreator wizard is available and will make the task much easier.
Start by installing this wizard by launching the install.bat on Windows or install.sh on Linux (in your ${XPCF_MODULE_ROOT}/xpcf/[version]/wizards/qtcreator).

Create a SolAR module in QTCreator

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

Select XPCF projects and XPCF Module template and click on Choose button.

create an SolAR Module in QT
Figure 1. Create an SolAR Module in QT

Then, set the name of your module and its location (The creation of a dedicated folder regrouping all module projects is recommended).

Set module project name in QT
Figure 2. Set module project name in QT

Next, provide the details concerning your module. You can set a different name to the package if you want to embed several modules in the same package. But, by default, keep the same one. Also, you can define the version and if you want to produce a static or a shared library. We highly recommend to use the shared library for modules. Also, for installation, all dependencies of your module can be copied in a recursive mode in your package folder. Thus, when you will deploy your module, you will be sure that all required third parties will be also deployed. For the link step, you can let the SolAR build pipeline find automatically the dependencies recursively. Finally, if you want to load your project in visual studio, check the box QTVS. Thus, you will be able to load the QT project in visual studio via the plugin QT Visual Studio Tools.

set module details in QT
Figure 3. Set module details in QT

Then, define your build system with qmake.

For the next step, you have to enter the directory where the XPCF binaries are located. Normally, you will find it in your <USER_HOME>, in the folder .remaken\packages\. You have also to set the namespace of your module. In order to have an easy access to all your modules by completion when coding, we recommend to use the namespace SolAR::MODULES::MyModule for any module.

set XPCF version directory and namespace
Figure 4. Set XPCF version directory and namespace

Then, choose your development kit that is used to configure your project in QTCreator. We recommend to use MSVC 2017 64bit on Windows or Clang on Linux.

remaken rules selection
Figure 5. Development kits selection

Finally, no subproject to add here. If you want to add a version control, select it and click on the Finish button.

Now, six files have been created: the project file MyModule.pro, a C++ file MyModule_main.cpp, a header file MyModuleAPI.h defining the macro to export your interfaces, a packagedependencies.txt file to manage the dependencies of your module, a bcom_MyModule.pc.in used by pkg_config to describe your module, and finally findremakenrules.pri to link to remaken rules.

But let’s take a closer look at these files.

QTCreator project file

MyModule.pro
QT       -= core gui
CONFIG -= app_bundle qt

TARGET = MyModule
FRAMEWORK = $${TARGET}
VERSION=0.11.0
DEFINES +=  $${TARGET}VERSION=\"$${VERSION}\"

# Uncomment following line to add more verbose information from builddefs-qmake rules
# CONFIG += verbose
# Uncomment following line to prepare remaken package
# CONFIG += package_remaken

CONFIG += with_qtvs

with_qtvs {
    PROJECTCONFIG = QTVS
}

CONFIG += c++1z
CONFIG += shared

staticlib {
    DEPENDENCIESCONFIG = staticlib
    REMAKEN_PKGSUBDIR=static
} else {
    DEPENDENCIESCONFIG = sharedlib
    REMAKEN_PKGSUBDIR=shared
}

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

CONFIG(release,debug|release) { (1)
    DEFINES += NDEBUG=1
    REMAKEN_PKGSUBDIR=$${REMAKEN_PKGSUBDIR}/release
}

package_remaken {
    message("Preparing remaken package installation in $${REMAKEN_PKGSUBDIR}")
    INSTALLSUBDIR=$${REMAKEN_PKGSUBDIR}
}

include(findremakenrules.pri) (2)

DEPENDENCIESCONFIG = sharedlib
DEPENDENCIESCONFIG += install_recurse (3)

## Configuration for Visual Studio to install binaries and dependencies. Work also for QT Creator by replacing QMAKE_INSTALL
PROJECTCONFIG = QTVS (4)

#NOTE : CONFIG as staticlib or sharedlib, DEPENDENCIESCONFIG as staticlib or sharedlib and PROJECTDEPLOYDIR MUST BE DEFINED BEFORE templatelibbundle.pri inclusion
include ($${QMAKE_REMAKEN_RULES_ROOT}/templatelibconfig.pri)


DEFINES += BOOST_ALL_NO_LIB
DEFINES += BOOST_ALL_DYN_LINK

SOURCES +=     MyModule_main.cpp

HEADERS +=     MyModuleAPI.h
unix {
}

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

win32 {
    DEFINES += _X86_VC12_TARGET_
    DEFINES += MBCS _MBCS
 }

INCLUDEPATH += $${PWD}

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

INSTALLS += header_files
DISTFILES +=     Makefile

OTHER_FILES +=     packagedependencies.txt

#NOTE : Must be placed at the end of the .pro
include ($${QMAKE_REMAKEN_RULES_ROOT}/remaken_install_target.pri) (5)
1 These CONFIG definitions must be added to your .pro file if you want to work with Visual Studio.
2 This .pri file has been installed by the wizard. It will allow to find the remaken folder depending on the OS you are using.
3 The dependencies of your module will be installed recursively. More details are available on the builddefs-qmake project on GitHub.
4 The installation of your module will also work with Visual Studio. Warning, in QTCreator, this will replace the usual QMAKE_INSTALL.
5 Place at the end the .pri file to install your module.

Package Dependencies file

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

To define the dependencies used by your pipeline, just add to your packagedependencies.txt a reference to the SolARFramework and to the third parties used by your module as shown below:

/.packagedependencies.txt

SolARFramework|0.11.0|SolARFramework|SolARBuild@github|https://github.com/SolarFramework/SolarFramework/releases/download
MyThirdParty1|x.x.x|MyThirdPaty1Folder|ThirdParty@github|https://github.com/MyGitHubProject/MyModule/releases/download (1)
MyThirdParty2#stable|x.x.x|MyThirdParty2Folder|conan|conan-center|shared|MyThirdParty2BuildOption (2)
1 Your third party is available in the release of your github project. To know how to package your third party, check the Third parties packaging page
2 Your third party can be directly downloaded using conan (for instance, from conan-center).

You will find bellow the syntax for each dependency (more information are available on the Remaken project on GitHub):

dependency_name|dependency_version|dependency_folder_name|dependency_path|dependency_download_url

XPCF and the build pipeline handle dependencies recursivity, meaning that you do not need to add the dependencies of the SolAR framework.

Your third parties, should be available in your <USER_HOME>/.remaken/packages/<yourCompiler>/ folder. To automatically download your dependencies, just run remaken where your packagedependencies.txt is located.

Numerous samples of packagedependencies.txt files can be found with the SolAR Modules you have surely installed on your machine.

MyModuleAPI.h

The MyModuleAPI.h file has been automatically created and it defines the macro MYMODULE_EXPORT_API you will have to place in front of each component interface to export them in your shared library.

MyModule_main.cpp

/MyModule_main.cpp

#include <xpcf/module/ModuleFactory.h>
#include <iostream>

namespace xpcf=org::bcom::xpcf;

/**
 *  @ingroup xpcfmodule
 */
/**
  * Declare module.
  */
 (1)
XPCF_DECLARE_MODULE("{ab4241b5-db78-4312-9e9b-f7896cddb961}","SolAR::MODULES::MyModule","MyModule module description");

/**
 * This method is the module entry point.
 * XPCF uses this method to create components available in the module.
 *
 * Each component exposed must be declared inside a xpcf::tryCreateComponent<ComponentType>() call.
 */
 (2)
extern "C" XPCF_MODULEHOOKS_API xpcf::XPCFErrorCode XPCF_getComponent(const xpcf::uuids::uuid& componentUUID,SRef<xpcf::IComponentIntrospect>& interfaceRef)
{
    xpcf::XPCFErrorCode errCode = xpcf::XPCFErrorCode::_FAIL;
    /* Sample code to declare components instanciation
    errCode = xpcf::tryCreateComponent<SolAR::MODULES::MyModule::componentType>(componentUUID,interfaceRef);
    if (errCode != xpcf::XPCFErrorCode::_SUCCESS) {
        errCode = xpcf::tryCreateComponent<SolAR::MODULES::MyModule::otherComponentType>(componentUUID,interfaceRef);
    }
    */
    return errCode;
}

/**
  * The declarations below populate list of the components available in the module (it represents the module index).
  * XPCF uses this index to introspect the components available in a module, providing the ability to generate the configuration file skeleton from the code.
  */
 (3)
XPCF_BEGIN_COMPONENTS_DECLARATION
/* sample components declarations
XPCF_ADD_COMPONENT(SolAR::MODULES::MyModule::componentType)
XPCF_ADD_COMPONENT(SolAR::MODULES::MyModule::otherComponentType)
*/
XPCF_END_COMPONENTS_DECLARATION

This .cpp file has three main goals:

1 It declares the module to XPCF with a given UUID, a given namespace and a given name
2 It allows the XPCF component factory, thanks to the XPCF_getComponent method, to retrieve and create each component from its UUID
3 it declares the components embedded in the module

Each time you will want to embed a new component into the module, you will have to add its creation in the XPCF_getComponent method and to add it in the component declaration.

bcom-MyModule.pc.in file

The SolAR build pipeline can use pkg-config that helps to insert the correct compiler options on the command line. Thus, 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). When you will install your module, this file will be directly copied to your binary folder <USER_HOME>/.remaken/packages/<yourInstallSubDir>/<yourCompiler>/<MyModule>/<MyModule_Version>.

The 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="$XPCF_MODULE_ROOT/SolARBuild/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 file.

This file is somewhat empty, but will be completed when adding components. It will be copied to the XPCF registry folder HOME_USER_/.xpcf/.