Cross compilation

This section will explain how to cross build an application for a specific target architecture. All steps will use ARM64 target’s architecture as an example and will assume that your localbuilder is running on a x86_64 host machine.

Warning!!

ALL THE FOLLOWING INSTRUCTIONS ASSUME THAT YOU ARE IN YOUR CONTAINER UNDER THE DEVEL USER !!! refer to this section to enter your container.

Cross build environment

Your home directory comes with some precreated directories:

  • gitsources: where your project/application sources git clone resides.
  • gitpkgs: where is the repository holding your packaged project is. This holds a tarball of your project source as well as its associated spec files and patches.
  • rpmbuild: this directory will hold all your RPM package build results, after the rpmbuild command has run

The container comes with a generic aarch64 ready cross build environment. To be in a cross-compiling environment you have to source a script to setup your environment for cross-compilation (cross-compiler and libraries paths). Execute the following:

[devel@redpesk-builder rpmbuild]$ . /usr/aarch64-linux-gnu/bin/cross-profile-setup-sdk-aarch64.sh

After sourcing the generic cross environment, in your specific case, you can export or source your project specific environment. As an example, cross-building a kernel will need to export this :

[devel@redpesk-builder rpmbuild]$ export CROSS_COMPILE="aarch64-linux-gnu-"
[devel@redpesk-builder rpmbuild]$ export ARCH="arm64"

But you can export everything else in the environment, an other example :

[devel@redpesk-builder rpmbuild]$ export GIT_SSH_COMMAND='ssh -v'

Cross build your project

Here is an example of how to cross build an AGL binding for an aarch64 target:

cd gitsources
git clone https://github.com/redpesk-samples/helloworld-binding.git
cd helloworld-binding
dnf-aarch64 -y builddep conf.d/packaging/helloworld-binding.spec
mkdir rpmbuild
cd rpmbuild
. /usr/aarch64-linux-gnu/bin/cross-profile-setup-sdk-aarch64.sh
# Project specific environment can be sourced/exported here if needed
cmake ..
# if there a "no version found" error, specify the version, for example:
cmake .. -DVERSION=8.99.6

If everything goes as planned, you will have an output like that:

Distribution detected (separated by ';' choose one of them) redpesk
Include: /usr/share/cmake/Modules/CMakeAfbTemplates/cmake/cmake.d/01-build_options.cmake
Include: /usr/share/cmake/Modules/CMakeAfbTemplates/cmake/cmake.d/02-variables.cmake
-- Check gcc_minimal_version (found gcc version 8.1.1) 	(found g++ version 8.1.1)
Include: /usr/share/cmake/Modules/CMakeAfbTemplates/cmake/cmake.d/03-macros.cmake
Include: /usr/share/cmake/Modules/CMakeAfbTemplates/cmake/cmake.d/04-extra_targets.cmake
.. Warning: RSYNC_TARGET RSYNC_PREFIX not defined 'make remote-target-populate' not instanciated
.. Warning: RSYNC_TARGET not defined 'make widget-target-install' not instanciated
-- Configuring done
-- Generating done
-- Build files have been written to: /home/devel/gitsources/helloworld-binding/build

Then you can continue with a make:

make -j
Scanning dependencies of target test-files
Scanning dependencies of target prepare_package
Scanning dependencies of target helloworld-skeleton
Scanning dependencies of target htdocs
Scanning dependencies of target prepare_package_test
Scanning dependencies of target helloworld-subscribe-event
Scanning dependencies of target fixture-files
Scanning dependencies of target helloworld-config
[  7%] Generating package
[  7%] Building C object helloworld-skeleton/CMakeFiles/helloworld-skeleton.dir/helloworld-service-binding.c.o
[ 11%] Generating htdocs
[ 14%] Generating package-test
Scanning dependencies of target autobuild
[ 18%] Generating test-files
[ 22%] Generating fixture-files
[ 25%] Generating package-test/htdocs
[ 29%] Generating helloworld-config
[ 33%] Building C object helloworld-subscribe-event/CMakeFiles/helloworld-subscribe-event.dir/helloworld-event-service-binding.c.o
[...]
[ 96%] Generating package/lib/afb-helloworld-skeleton.so
[ 96%] Built target project_populate_helloworld-skeleton
[ 96%] Built target helloworld-subscribe-event
Scanning dependencies of target project_populate_helloworld-subscribe-event
[100%] Generating package/lib/afb-helloworld-subscribe-event.so
[100%] Built target project_populate_helloworld-subscribe-event
Scanning dependencies of target populate
[100%] Built target populate
Scanning dependencies of target helloworld-binding_build_done
++ Debug from afb-binder --port=1234  --ldpaths=package --workdir=. --roothttp=../htdocs --token= --verbose
[100%] Built target helloworld-binding_build_done

Cross build the RPM packages

Warning!!

ALL THE FOLLOWING INSTRUCTIONS ASSUME THAT YOU ARE IN YOUR CONTAINER UNDER THE DEVEL USER !!! refer to this section to enter your container.

# prerequisite: install needed libraries
sudo dnf install redpesk-utils

In this section, you can see how to cross build RPM packages. It is possible to use the previous example agl-service-helloworld but we choose another example: Mustach We are going to use an intermediate git repository meant for packaging, we will name it gitpkg.

Inside the gitpkg, there are 3 types of files: the spec file, one or several patches (if needed) and a source archive tarball.

First step: populate package sources repository (gitpkg)

# create your project gitpkg and go inside
mkdir ~/gitpkgs/mustach && cd ~/gitpkgs/mustach
# add the specfile in the mustach gitpkg
cat << EOF > mustach.spec
%global _privatelibs libmustach.*[.]so\(\).*
%global __requires_exclude ^(%{_privatelibs})$

Name:           mustach
Version:        0.0.1
Release:        0%{?dist}
Summary:        mustach is a C implementation of the mustache template specification

License:        APL2.0
URL:            https://gitlab.com/jobol/mustach.git
Source0:        %{name}-%{version}.tar.gz

BuildRequires:  valgrind

%global debug_package %{nil}

%description
C implementation of mustache templating

%package devel
Summary:      Devel poackage for mustach

Requires:    %{name} = %{version}

%description devel
Devel package of mustach

%prep
%autosetup -p1

%build
make PREFIX=%{_prefix} LIBDIR=%{_libdir}

%install
%make_install PREFIX=%{_prefix} LIBDIR=%{_libdir}

%check
%if "x%{?_crossroot}" == "x"
make test
%endif

%files
%doc
%{_bindir}/%{name}
%{_libdir}/libmustach-core.so.*
%{_libdir}/libmustach-json-c.so.*
%{_libdir}/libmustach.so.*
%{_datadir}/man/man1/mustach.1.gz

%files devel
%{_includedir}/%{name}/mustach-json-c.h
%{_includedir}/%{name}/mustach.h
%{_includedir}/mustach/mustach-wrap.h
%{_libdir}/libmustach-core.so
%{_libdir}/libmustach-json-c.so
%{_libdir}/libmustach.so
%{_libdir}/pkgconfig/libmustach-core.pc
%{_libdir}/pkgconfig/libmustach-json-c.pc
%{_libdir}/pkgconfig/libmustach.pc

%license

%changelog
EOF
# init the git repository
git init
# stage spec file changes
git add *.spec

NB: You can refer to the RPM packaging guide to know more about creating a spec file.

Second step: apply your modifications

In general, you have typically 2 usecases you want to support: an internal project OR an existing upstream project.

This second step differs depending on the usecase. We are gonna see how, with our Mustach example.

1st case : Internal project

In this case, you have access to directly modify the project source code by cloning it, and creating the archive from the path where you cloned the sources.

cd ~/gitsources/
# clone sources
git clone https://gitlab.com/jobol/mustach.git
cd mustach
# modify the project sources (in this example, we add a version switch)
sed  -i '/help(prog);/a \\t\tif (!strcmp(*av, "-v") || !strcmp(*av, "--version")) {\n\t\t\tprintf ("Mustach v1.1.0\\n");\n\t\t\texit(0);\n\t\t}' mustach-tool.c
# this line lets us modify the Makefile automatically in this example
# but files can be modified the usual way, for instance with a text editor.

git commit mustach-tool.c -sm "Add version information switch"

cd ~/gitpkgs/mustach
# create the archive from the project sources
iotpkg archive --path-source ~/gitsources/mustach/ --commit master


# move project sources to the correct directory
mkdir -p ~/rpmbuild/SOURCES/
cp *.tar.gz ~/rpmbuild/SOURCES/
2nd case : Existing upstream project

In this case, you can’t modify the project sources directly. You thus have to create the archive from a URL, and create a patch to apply your modifications, instead of directly modifying the application/project source code.

cd ~/gitpkgs/mustach

# retrieve the archive from a URL
# The name of the archive will be in the format : mustach-${version}+${last_commit_sha}.tar.gz
# To ommit ${last_commit_sha} from the name of the archive '--version X.Y.Z' is added at the end of the command iotpkg
# where X.Y.Z is the version of package (can be also used to force the version of the archive) 
iotpkg archive --path-source https://gitlab.com/jobol/mustach.git --commit master 

# create the patch (In this example we add a version switch)
cat << EOF > 0001-Add-version-information-switch.patch
diff --git a/mustach-tool.c b/mustach-tool.c
index 52fce50..befde90 100644
--- a/mustach-tool.c
+++ b/mustach-tool.c
@@ -127,6 +127,10 @@ int main(int ac, char **av)
        if (*++av) {
                if (!strcmp(*av, "-h") || !strcmp(*av, "--help"))
                        help(prog);
+               if (!strcmp(*av, "-v") || !strcmp(*av, "--version")) {
+                       printf ("Mustach v1.1.0\n");
+                       exit(0);
+               }
                f = (av[0][0] == '-' && !av[0][1]) ? "/dev/stdin" : av[0];
                s = load_json(f);
                if (s < 0) {
EOF

# modify the spec file to take into account the patch
sed -i '/Source0/a Patch0:         0001-Add-version-information-switch.patch' mustach.spec
# this line lets us add the `Patch` line to the spec file automatically in this example
# but a normal text editor can be used as well

# copy sources and patches to the correct directory
mkdir -p ~/rpmbuild/SOURCES/
cp *.tar.gz *patch ~/rpmbuild/SOURCES/

Third step: cross build the RPM

cd ~/gitpkgs/mustach
# install required packages before building (valgrind in this example)
sudo dnf-aarch64 -y builddep mustach.spec
# Source the environment to cross build
. /usr/aarch64-linux-gnu/bin/cross-profile-setup-sdk-aarch64.sh
# cross build the RPM with the following command
rpmbuild -bb --nodeps --target aarch64 *.spec
# output of rpmbuild command (Second case here)
Building target platforms: aarch64
Building for target aarch64
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.0OEjgt
+ umask 022
+ cd /home/devel/rpmbuild/BUILD
+ cd /home/devel/rpmbuild/BUILD
+ rm -rf mustach-1.1.0+20210531+1+gee3d9f7
+ /usr/bin/gzip -dc /home/devel/rpmbuild/SOURCES/mustach-1.1.0+20210531+1+gee3d9f7.tar.gz
+ /usr/bin/tar -xof -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd mustach-1.1.0+20210531+1+gee3d9f7
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ /usr/bin/cat /home/devel/rpmbuild/SOURCES/0001-Add-version-information-switch.patch
+ /usr/bin/patch -p1 -s --fuzz=0 --no-backup-if-mismatch
+ exit 0
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.TsMRVw
+ umask 022
+ cd /home/devel/rpmbuild/BUILD
+ cd mustach-1.1.0+20210531+1+gee3d9f7
+ make PREFIX=/usr LIBDIR=/usr/lib64
[...]
Processing files: mustach-1.1.0+20210531+1+gee3d9f7-0.rpdarz.aarch64
warning: Duplicate build-ids /home/devel/rpmbuild/BUILDROOT/mustach-1.1.0+20210531+1+gee3d9f7-0.rpdarz.aarch64/usr/lib64/libmustach-json-c.so.1.1 and /home/devel/rpmbuild/BUILDROOT/mustach-1.1.0+20210531+1+gee3d9f7-0.rpdarz.aarch64/usr/lib64/libmustach.so.1.1
Provides: libmustach-core.so.1.1()(64bit) libmustach-json-c.so.1.1()(64bit) libmustach.so.1.1()(64bit) mustach = 1.1.0+20210531+1+gee3d9f7-0.rpdarz mustach(aarch-64) = 1.1.0+20210531+1+gee3d9f7-0.rpdarz
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires: libc.so.6()(64bit) libc.so.6(GLIBC_2.17)(64bit) libjson-c.so.4()(64bit) rtld(GNU_HASH)
Processing files: mustach-devel-1.1.0+20210531+1+gee3d9f7-0.rpdarz.aarch64
Provides: mustach-devel = 1.1.0+20210531+1+gee3d9f7-0.rpdarz mustach-devel(aarch-64) = 1.1.0+20210531+1+gee3d9f7-0.rpdarz pkgconfig(libmustach) = 1.1.0 pkgconfig(libmustach-core) = 1.1.0 pkgconfig(libmustach-json-c) = 1.1.0
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires: /usr/bin/pkg-config
Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/devel/rpmbuild/BUILDROOT/mustach-1.1.0+20210531+1+gee3d9f7-0.rpdarz.aarch64
Wrote: /home/devel/rpmbuild/RPMS/aarch64/mustach-1.1.0+20210531+1+gee3d9f7-0.rpdarz.aarch64.rpm
Wrote: /home/devel/rpmbuild/RPMS/aarch64/mustach-devel-1.1.0+20210531+1+gee3d9f7-0.rpdarz.aarch64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.xN6UIL
+ umask 022
+ cd /home/devel/rpmbuild/BUILD
+ cd mustach-1.1.0+20210531+1+gee3d9f7
+ /usr/bin/rm -rf /home/devel/rpmbuild/BUILDROOT/mustach-1.1.0+20210531+1+gee3d9f7-0.rpdarz.aarch64
+ exit 0

You can see the RPM has been created in the corresponding directory for aarch64 targets:

ls ~/rpmbuild/RPMS/aarch64/
# list of rpm files for this example (Second case here)
mustach-1.1.0+20210531+1+gee3d9f7-0.rpdarz.aarch64.rpm
mustach-devel-1.1.0+20210531+1+gee3d9f7-0.rpdarz.aarch64.rpm

Fourth step: commit your modifications

The source archive (file xxx-version.tar.gz) must be part of gitpkg. It’s a bad idea to directly add this archive gitpkg git repo. This is why we will use a git extension, named Git LFS (for Large File Storage) to manage this binary. For more information on git LFS, please refer to https://git-lfs.github.com

Here are the commands to add your source archive into the gitpkg :

# Setup Git LFS (only once)
git lfs install

# Select the file types you'd like Git LFS to manage (only once)
git lfs track *.tar.gz

# Now make sure .gitattributes is tracked (only once)
git add .gitattributes

# Now, just commit and push to gitpkg as you normally would with git, the LFS
#extension will automatically work behind the scenes

# Add the specfile
git add *spec
# Add patch file(s)
git add *patch
# Add the archive
git add *tar.gz

# Commit
git commit -sam "Initial commit"

# If you have a running operational redpesk stack
git remote add origin http://<your_gitlab_url>
# Push changes into the gitpkg repository
git push