The Linux Page

one cmake with multiple projects and accessing "setup" files at compile time

So many locations, you need many variables... or instead think of using one common location and find yourselves quickly every time

We have a large project named Snap! C++ which uses cmake to compile everything. It is clear that cmake is the right solution, I don't question that part even one minute. It would be a killer to try to use any other system.

Our environment is pretty large with many libraries, daemons, tools, plugins, scripts... all organized in sub-projects of one large project named snapwebsites. For example, we have the libsnapwebsites library and the snapmanager sub-projects. However, as far as cmake is concerned, this is but one project.


Well... to give you a concrete example, we have a file in snapmanager called bundles.xsd which is used in many places in the project. Other sub-projects are not really expected to know where that file resides in the snapmanager project (the location could change over time, too.)

As a result we use the common directory.

When you run cmake the first time, it creates a directory named common in the BUILD directory. Under that directory we create one sub-direct per type of files we share. At this time this is limited to DTD and XSD files.

At first, we used the file(INSTALL ...) cmake command to copy those files to the common directory. The problem with that command is that it won't copy updates automatically. Rebuild all will work from scratch, but an intermediate change fails to get applied immediately. It will wait for the install command to run.

If you were to update an XSD file, run a make install and then update your XML files using the new available fields as just added to the XSD file, the file(INSTALL ...) would work. The file would get installed at the time the install command is run.

So instead we had to use the configure_file(...) cmake command. This is an interesting side effect of the configure_file() command.

In our case, we just want to copy the file and not apply any pattern modification so we can use the COPYONLY option along the settings.


This way changes to bundles.xsd are applied before other commands happen in future sub-projects.

So the order in which you include sub-projects is still very important. If sub-project A needs some file from sub-project B then you need to make sure that sub-project B happens first. In our case this means having:


B can then install a file (such as bundles.xsd) and A can make use of the file from the corresponding BUILD/common directory.

The other solution, which works, is to use a full path. The problem with such is that if the file moves then you have to edit your full paths. Part of which are being generated, but it is not unlikely that you'd have to do an edit.

In our case, we use XSD and DTD files to verify our XML data. This means our XML data is likely valid before you even try to start our server (at least the structure of the XML is going to be valid, the data itself may still be somewhat off.) The verification is done using a command that looks like this:

validate_xml(my.xml verify.xsd)

This is a cmake function which we created to verify XML files (we have similar commands for verifying JavaScript and CSS files.)

The function searches for the verify.xsd file in the current folder. If not there, it tries in various other places (like xsd/verify.xsd.) At some point, it checks the folder ${SNAP_COMMON_DIR}/xsd/verify.xsd. Since this is where our configure_file() copies the files and modifications whenever an update is performed, it works like a charm.

Of course, that was not selve explanatory. It tooks us some time to get this to work in Snap! C++. This is why I make a post about it here. I think it is useful information.

If you'd like to learn more, I suggest you check out the cmake directory and search for XSD and DTD files in all our CMakeLists.txt files. That way you'll see a working example in our environment.