Overview#
PKI uses CMake to generate Makefiles that will be used to build RPM packages. The RPM build process consists of the following steps:
Generating Makefiles from CMake scripts
Build the binaries
Run unit tests
Install the binaries
CMake#
Directory structure#
The CMake scripts are stored in files called CMakeLists.txt. The main script is located in the top of the source folder. It may include other scripts in other subfolders.
PKI_SOURCE_DIR
+ CMakeLists.txt -- defines variables
+ base/
+ CMakeLists.txt -- determines components to include
+ common/
+ CMakeLists.txt -- installs component
+ python/
+ shared/
+ src/
+ CMakeLists.txt -- builds and installs binaries
+ test/
+ CMakeLists.txt -- builds and runs test cases
Modules#
CMake standard modules are included in PKI’s source repository to avoid build compatibility issues. The modules are stored in the cmake/Modules folder.
PKI_SOURCE_DIR
+ cmake/
+ Modules/
+ Java.cmake
+ JavaFileList.cmake
+ JUnit.cmake
...
It includes custom modules that provide the following CMake commands:
javac: compile Java code
jar: build JAR file
javadoc: generate Javadoc
link: create symbolic link
add_junit_test: run unit tests
Variables#
The main CMake script defines the following variables:
APPLICATION_NAME: pki
VERSION: 10.0.1 – actual release version
APPLICATION_VERSION: 10.0.1 – compatibility version
APPLICATION_VERSION_MAJOR: 10
APPLICATION_VERSION_MINOR: 0
APPLICATION_VERSION_PATCH: 1
Some variables are defined in DefineInstallationPaths module:
JAVA_LIB_INSTALL_DIR: /usr/share/java
JAVA_JAR_INSTALL_DIR: /usr/lib64/java
SYSCONF_INSTALL_DIR: /etc
VAR_INSTALL_DIR: /var
SYSTEMD_LIB_INSTALL_DIR: /lib/systemd/system
SYSTEMD_ETC_INSTALL_DIR: /etc/systemd/system
They can be overriden via command line arguments.
See also:
Commands#
The usage of the custom commands are shown below.
See also CMake built-in commands.
Build Process#
Generating Makefiles#
Prepare a build directory, then run CMake as follows:
$ mkdir $BUILD_DIR
$ cd $BUILD_DIR
$ cmake <args> $SRC_DIR
Some variables are defined via command-line argument:
VERSION: project version
VAR_INSTALL_DIR: location of /var directory
JAVA_LIB_INSTALL_DIR: JNI installation directory
SYSTEMD_LIB_INSTALL_DIR: systemd scripts installation directory
RESTEASY_LIB: RESTEasy installation directory
WITH_JAVADOC: option to build Javadoc
To build a specific package, at least one of the following variables should be defined via command-line argument:
BUILD_IPA_PKI_THEME
BUILD_DOGTAG_PKI_THEME
BUILD_REDHAT_PKI_THEME
BUILD_PKI_CORE
BUILD_PKI_RA
BUILD_PKI_TPS
BUILD_PKI_CONSOLE
BUILD_PKI_MIGRATE
BUILD_PKI_SELINUX
For example, on Fedora 26 the pki-core.spec generates the following commands:
$ /usr/bin/mkdir -p build
$ cd build
$ CFLAGS="${CFLAGS:--O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic}"
$ export CFLAGS
$ CXXFLAGS="${CXXFLAGS:--O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic}"
$ export CXXFLAGS
$ FFLAGS="${FFLAGS:--O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic -I/usr/lib64/gfortran/modules}"
$ export FFLAGS
$ FCFLAGS="${FCFLAGS:--O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic -I/usr/lib64/gfortran/modules}"
$ export FCFLAGS
$ LDFLAGS="${LDFLAGS:--Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld}"
$ export LDFLAGS
$ /usr/bin/cmake \
-DCMAKE_C_FLAGS_RELEASE:STRING="-DNDEBUG" \
-DCMAKE_CXX_FLAGS_RELEASE:STRING="-DNDEBUG" \
-DCMAKE_Fortran_FLAGS_RELEASE:STRING="-DNDEBUG" \
-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \
-DCMAKE_INSTALL_PREFIX:PATH=/usr \
-DINCLUDE_INSTALL_DIR:PATH=/usr/include \
-DLIB_INSTALL_DIR:PATH=/usr/lib64 \
-DSYSCONF_INSTALL_DIR:PATH=/etc \
-DSHARE_INSTALL_PREFIX:PATH=/usr/share \
-DLIB_SUFFIX=64 \
-DBUILD_SHARED_LIBS:BOOL=ON \
-DVERSION=10.6.0-0.fc26 \
-DVAR_INSTALL_DIR:PATH=/var \
-DBUILD_PKI_CORE:BOOL=ON \
-DJAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk \
-DJAVA_LIB_INSTALL_DIR=/usr/lib/java \
-DSYSTEMD_LIB_INSTALL_DIR=/usr/lib/systemd/system \
-DAPP_SERVER=tomcat8 \
-DJAXRS_API_JAR=/usr/share/java/jboss-jaxrs-2.0-api.jar \
-DRESTEASY_LIB=/usr/share/java/resteasy \
..
$ /usr/bin/make VERBOSE=1 -j8 all
For each source folder that contains CMakeLists.txt, CMake will create a corresponding folder in the build directory and generate the Makefile there.
PKI_SOURCE_DIR
+ CMakeLists.txt
+ base/
+ CMakeLists.txt
+ common/
+ CMakeLists.txt
+ src/
+ CMakeLists.txt
+ test/
+ CMakeLists.txt
+ build/
+ Makefile
+ base/
+ Makefile
+ common/
+ Makefile
+ src/
+ Makefile
+ test/
+ Makefile
There are variables that can be used by the scripts to refer to these folders:
CMAKE_SOURCE_DIR: always points to PKI_SOURCE_DIR
CMAKE_BINARY_DIR: always points to PKI_SOURCE_DIR/build
CMAKE_CURRENT_SOURCE_DIR: points to current source dir, e.g. PKI_SOURCE_DIR/base/common/src
CMAKE_CURRENT_BINARY_DIR: points to current binary dir, e.g. PKI_BINARY_DIR/base/common/src
Build Dependency#
symkey-jar
pki-nsutil-classes
pki-nsutil-jar
pki-cmsutil-classes
pki-cmsutil-jar
pki-certsrv-classes
pki-certsrv-jar
pki-examples-classes
tkstool
pki-tools-classes
pki-tools-jar
pki-javadoc
pki-tomcat-classes
pki-tomcat7-classes/pki-tomcat8-classes
pki-tomcat-jar
pki-tomcat
pki-cms-classes
pki-cms-jar
pki-cmsbundle-jar
pki-cmscore-classes
pki-cmscore-jar
pki-ca-classes
pki-ca-jar
pki-kra-classes
pki-kra-jar
pki-ocsp-classes
pki-ocsp-jar
pki-tks-classes
pki-tks-jar
pki-tps-classes
pki-tps-jar
pki-console-classes
pki-console-jar
pki-test-classes
pki-util-test-classes
pki-server-test-classes
Target |
Dependencies |
---|---|
pki-nsutil-jar |
pki-nsutil-classes |
pki-cmsutil-classes |
pki-nsutil-jar |
pki-cmsutil-jar |
pki-cmsutil-classes |
pki-certsrv-classes |
pki-cmsutil-jar |
pki-certsrv-jar |
pki-certsrv-classes |
pki-tools-classes |
pki-certsrv-jar |
pki-tools-jar |
pki-tools-classes |
tkstool |
pki-certsrv-jar |
pki-tomcat-classes |
pki-certsrv-jar, pki-tomcat7-classes or pki-tomcat8-classes |
pki-tomcat-jar |
pki-tomcat-classes |
pki-cms-classes |
pki-tomcat-jar |
pki-cms-jar |
pki-cms-classes |
pki-cmscore-classes |
pki-cms-jar |
pki-cms-jar |
pki-cmscore-classes |
tps_library |
pki-tps-jar |
ldapauth_library |
pki-tps-jar |
tokendb_module |
pki-tps-jar |
tps_module |
pki-tps-jar |
tokendb_library |
pki-tps-jar |
Building the Binaries#
To build the binaries the RPM builder will execute the following command:
make all
It will execute build targets in Makefiles generated by the CMake scripts. For example, the following CMake command will compile Java source code into Java binaries:
javac(pki-certsrv-classes
SOURCE_DIR
...
SOURCES
com/netscape/certsrv/*.java
EXCLUDE
...
CLASSPATH
${PKI_NSUTIL_JAR} ${PKI_CMSUTIL_JAR}
...
OUTPUT_DIR
${CMAKE_BINARY_DIR}/classes
DEPENDS
pki-nsutil-jar pki-cmsutil-jar
)
See also javac tool documentation.
Then the binaries will be packaged into a JAR file using the following command:
jar(pki-certsrv-jar
CREATE
${CMAKE_BINARY_DIR}/dist/pki-certsrv.jar
OPTIONS
m
PARAMS
${CMAKE_CURRENT_BINARY_DIR}/pki-certsrv.mf
INPUT_DIR
${CMAKE_BINARY_DIR}/classes
FILES
com/netscape/certsrv/*.class
EXCLUDE
...
DEPENDS
pki-certsrv-classes
)
See also jar tool documentation.
If the Javadoc option is enabled, it will also generate Javadoc files:
javadoc(pki-javadoc
SOURCEPATH
${CMAKE_SOURCE_DIR}/base/util/src
${CMAKE_SOURCE_DIR}/base/common/src
${CMAKE_SOURCE_DIR}/base/java-tools/src
DEST
${CMAKE_CURRENT_BINARY_DIR}/javadoc/pki-${APPLICATION_VERSION}
SUBPACKAGES
com.netscape.certsrv
com.netscape.cms
com.netscape.cmstools
com.netscape.cmsutil
CLASSPATH
${PKI_CMSUTIL_JAR} ${PKI_CERTSRV_JAR} ${PKI_CMS_JAR} ${PKI_TOOLS_JAR}
...
OPTIONS
-windowtitle 'pki-javadoc'
-doctitle '<h1>PKI Javadoc</h1>'
-author
-use
-version
DEPENDS
pki-cmsutil-jar pki-certsrv-jar pki-cms-jar pki-tools-jar
)
See also javadoc tool documentation.
Running Unit Tests#
To run the unit tests the RPM builder will execute the following command:
make test
It will execute test targets in Makefiles generated by the CMake scripts. If the test code needs to be compiled, there should be a separate CMake command to compile it, for example:
javac(pki-common-test-classes
SOURCES
com/netscape/certsrv/*.java
com/netscape/cmscore/*.java
CLASSPATH
${PKI_NSUTIL_JAR} ${PKI_CMSUTIL_JAR}
...
${CMAKE_BINARY_DIR}/test/classes
OUTPUT_DIR
${CMAKE_BINARY_DIR}/test/classes
DEPENDS
pki-test-classes
pki-nsutil-jar pki-cmsutil-jar
pki-certsrv-jar pki-cms-jar pki-cmscore-jar pki-cmsbundle-jar
)
The test itself is defined as follows:
add_junit_test(test-pki-common
CLASSPATH
${PKI_NSUTIL_JAR} ${PKI_CMSUTIL_JAR}
...
${CMAKE_BINARY_DIR}/test/classes
TESTS
com.netscape.certsrv.authentication.AuthTokenTest
com.netscape.certsrv.request.AgentApprovalsTest
...
REPORTS_DIR
reports
)
add_dependencies(test test-pki-common)
Installing the Binaries#
To install the binaries the RPM builder will execute the following command:
make install
It will execute install targets in Makefiles generated by the CMake scripts, for example:
install(
FILES
${CMAKE_BINARY_DIR}/dist/pki-certsrv.jar
DESTINATION
${JAVA_JAR_INSTALL_DIR}/pki
)
Writing CMake Scripts#
Locating dependencies#
Dependencies such as Java libraries can be located as follows:
find_file(JSS_JAR
NAMES
jss4.jar
PATHS
${JAVA_LIB_INSTALL_DIR}
/usr/share/java
)
Customizing templates#
Some source files are templates that contain CMake variables, for example:
Name: pki-certsrv
Specification-Version: ${APPLICATION_VERSION}
Implementation-Version: ${VERSION}
The variables can be replaced with the actual value using the following command:
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/pki-certsrv.mf
${CMAKE_CURRENT_BINARY_DIR}/pki-certsrv.mf
)
Writing a function#
Function can be defined as follows:
function(javac target)
...
endfunction(javac)
The arguments can be parsed as follows:
foreach (arg ${ARGN})
if (arg MATCHES "(SOURCE_DIR|SOURCES|EXCLUDE|CLASSPATH|OUTPUT_DIR|DEPENDS)")
set(param ${arg})
else ()
if (param STREQUAL "SOURCE_DIR")
set(source_dir ${arg})
elseif (param STREQUAL "SOURCES")
list(APPEND sources ${arg})
...
endif(param STREQUAL "SOURCE_DIR")
endif(arg MATCHES "(SOURCE_DIR|SOURCES|EXCLUDE|CLASSPATH|OUTPUT_DIR|DEPENDS)")
endforeach(arg)
Paths can be merged as follows:
if (UNIX)
set(separator ":")
else (UNIX)
set(separator ";")
endif(UNIX)
foreach (path ${classpath})
set(native_classpath "${native_classpath}${separator}${path}")
endforeach(path)
To add the target into the build step and to specify the dependency:
add_custom_target(${target} ALL DEPENDS ${depends})
To add commands to be executed by this function:
add_custom_command(
TARGET ${target}
COMMAND
...
WORKING_DIRECTORY
...
)
Example#
Assuming PKI source code is already checked out in PKI_SRC folder, run the build as follows:
$ mkdir -p ~/build
$ cd ~/build
$ cmake\
-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON\
-DCMAKE_INSTALL_PREFIX:PATH=/usr\
-DINCLUDE_INSTALL_DIR:PATH=/usr/include\
-DLIB_INSTALL_DIR:PATH=/usr/lib64\
-DSYSCONF_INSTALL_DIR:PATH=/etc\
-DSHARE_INSTALL_PREFIX:PATH=/usr/share\
-DLIB_SUFFIX=64\
-DBUILD_SHARED_LIBS:BOOL=ON\
-DVERSION=10.4.0-0.1.fc24\
-DVAR_INSTALL_DIR:PATH=/var\
-DBUILD_PKI_CORE:BOOL=ON\
-DJAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk\
-DJAVA_LIB_INSTALL_DIR=/usr/lib/java\
-DSYSTEMD_LIB_INSTALL_DIR=/usr/lib/systemd/system\
-DWITH_TOMCAT7:BOOL=OFF\
-DJAXRS_API_JAR=/usr/share/java/jboss-jaxrs-2.0-api.jar\
-DRESTEASY_LIB=/usr/share/java/resteasy\
$PKI_SRC
$ make
Future Enhancements#
RPM spec dependency#
Currently the build process has to be done using RPM which is quite slow because it needs to build the entire package. For development it would be better to build using CMake (without RPM) because it could compile only the code that is changed, and then install the binaries directly on the development machine.
cd build
cmake ..
make ca
make install
However, this is currently not possible because the RPM spec file contains some additional build operations. These operations should be converted into CMake scripts.
JUnit command improvement#
Currently the JUnit test cases need to be specified explicitly. The CMake command should be modified to find all test classes automatically, so the CMake script may look like the following:
junit(pki-common-test
SOURCES
**/*Test.java
CLASSPATH
${PKI_NSUTIL_JAR} ${PKI_CMSUTIL_JAR}
...
REPORTS_DIR
reports
)