7 March 2025
Developing C++ code simultaneously for desktop and web with cmake
In this technical entry I give a summary on my current software projects that
aimed to support multiple platforms (Windows, macOS, Linux) and the web.
The mentioned code has been developed mostly in C++.
I present two projects: the
XaoS project
(which is a fractal visualization software,
created many years ago by Jan Hubička and Thomas Marsh), and
bibref
(which is
a Bible study tool). In both cases, the program is written in C++. For XaoS,
the user interface uses the
Qt
framework. For bibref, there is a Qt interface for
the desktop version, but for the web version, a terminal based user interface is provided.
The cmake utility has
growing
popularity in the software development process.
During the last years, it has been significantly improved. For example, the Qt
developers
worked together
with
kitware, the company behind cmake.
In fact, configuring cmake is not always
trivial, the documentation is sometimes too verbose, but at the end of the day,
it is possible to solve most of the challenges without introducing non-standard
solutions or workarounds.
Short and maintainable config files
One of the most important questions is how a configuration file looks like.
Is it possible to cover all situations with a short config file, or do you need to
tinker the settings for the special cases manually? In the case of XaoS,
special options have been defined with the keyword
option on
rows 97-98:
the user can set if the deep zoom feature or the OpenGL build will be enabled.
These settings are identified and handled later on
rows 100-108.
In the case of bibref, the settings are strings, they need to be handled a
bit
differently and with more work.
Detecting platforms is very crucial. Luckily, each platform defines some environment
variables to make the detection possible. For example, identifying a Mac is possible
like
this
or
this.
Unfortunately, sometimes there are multiple ways to achieve the same task,
and this leads to a wide variety of implementation techniques and different cmake configurations.
Why
unfortunately? Because future maintenance of this diversity can be laborous.
Writing cmake configs seem to allow the maintainer some freedom. For example,
if-commands may be closed with endif
with
or
without
an argument which corresponds to the argument of the if-command (and it makes the code
easier to understand). On the other hand, in some cases, cmake is case sensitive:
for example, if the library
sword had been written in uppercase
here,
it would not be found during the build process. According to the cmake documentation, variable names
are case sensitive, but commands are case insensitive, and this can make
a kind of confusion for newcomers.
Finding packages
For XaoS, the primary prerequisite is Qt. Qt comes with a large amount
of internal components like Widgets and PrintSupport, or OpenGL and OpenGLWidgets
(the latter ones are important for the OpenGL build). For bibref,
the
Sword library,
the
Boost C++ extension, and for the command line version,
the readline library are the dependencies, and, also, the flex/bison machinery
is required to parse the BRST files.
Finding these dependencies are sometimes straightforward via the
find_package
command, but sometimes not. In some cases, a
pkg_check_modules command
is also required to set some extra variables automatically that could be important during the build.
Interestingly, for the web build (via
emscripten), it is not possible to detect
the Boost library automatically, but there is a CXX flag,
-sUSE_BOOST_HEADERS,
it has to be
set separately.
These minor (but annoying) details will hopefully be unified in the future.
Linking libraries (statically or dynamically to the executable) is another important detail.
Luckily, cmake comes with a sophisticated configuration system that provides the command
target_link_libraries, and to fine-tune the linking process, the command
target_link_options. Depending on the target platform, these settings could be
completely different, for example, in the bibref project, a
simple line
is enough to link the final executable; on the other hand, for the emscripten build
one may need
something more complex.
Web builds
For both projects, it was crucial to use a virtual file system when running the program
in a web browser. Since web applications have no access to the local filesystem,
the external files that may be important for the program (for instance, the
fractal examples for XaoS, or the Bible databases for bibref) should be stored in
a different way. The emscripten tool comes with a sophisticated solution that can
be well configured via the
--preload-file linker option.
The cmake utility was originally designed for supporting C/C++ builds,
but here we use it for an HTML/Javascript/WebAssembly compilation and installation.
At the moment, the final steps like copying the executables to the installation folder,
need to be done
manually.
Hopefully, there will be a simplified way to do this transparently in the future.
Simplicity
What I really like in cmake is its simplicity for those user who just want to compile the
code and that is it. For example, a native build can be performed with a
mkdir build && cd build && cmake .. && make command line.
The same for an HTML/Javascript/WebAssembly build is the same but changing
cmake to emcmake cmake (sic!). (Formerly, make had to be changed
to emmake make, but this is no longer required.) This magic works
on all major platforms: cmake is there and waiting for orders.
Continue reading…
See also a filtered list of the entries on topics
GeoGebra,
technical developments or
internal references in the Bible.
|
Zoltán Kovács
Linz School of Education
Johannes Kepler University
Altenberger Strasse 69
A-4040 Linz
|