Regenerate/recompile at install time

Hello,

I am writing a build system for an old project currently using hand-written Make files. In the sourcefiles, there are numerous hard-coded paths to files in the absolute form. (Mostly to the configuration files and as a back-up for the situation when some environment variable is not available, but not all.) The prerequisite for this project is that it should run both in the BINARY tree and installed, thus these hard-coded paths will differ in these situations.

As it is very similar to the RPATH problem, I am thinking if there is any solution in CMake which would allow me to recompile/relink/regenerate objects (objects in the general form) at install time?

So far what I was doing is to collect them all in one STATIC library (so I can track them all in simple manner) - in the original Makefile they were passed as simple DEFINEs, so I created a config file where these are defined as extern symbol. Then created this symbol in library.

As a hack, I could change this STATIC library to SHARED one and use the symbol interposition hack to create two libraries during build time, one for the BINARY tree and the other for the installed location. In BINARY tree, the dependencies would have the RPATH set to the build library and during installation only the second, install one would be installed and RPATH set.

However, this feels ugly. Is there a better way?

CMake does not provide such functionality.

Yes. Use APIs such as dlsym and dladdr to get the location of the shared library[1] containing a given symbol on-disk (Windows has analogous APIs). Then, use the CMake-configured information about where the library lives in the install prefix and then compute where the data is relative to that prefix. Things to note:

  • it requires the install and build trees be laid out in the same way
  • it requires that the prefix not be symlinked into through an incompatible view (can give more details if it matters; really only visible on things like Nix or Homebrew)
  • setting up the OUTPUT_DIRECTORY variables to match the install tree destinations is manual
  • I’m unsure how easy this is to do in practice with multi-config generators; so far I’ve been lucky to just ignore them, but there are likely details about that which will need figured out.

[1] Note that this does not work well for static builds; the final executable likely needs to provide some information about where the data lives next to it for this case.

Thank you for the reply, however I don’t think I completely understand your proposal. The software is based on plug-in extensibility heavily using the DLSYM functionality, so I was already trying to consolidate OUTPUTs from builds to tree similar to the install one. (Lets say I am going to use the GNUInstallDirs module to create build tree like:

$<CONFIG>/lib
$<CONFIG>/libexec
$<CONFIG>/bin
$<CONFIG>/etc
$<CONFIG>/python-index
...(etc)...

In other words the output artifacts would be grouped under $<CONFIG> mount-point.)

Problem is, I need to point to the default location of config files. If I understand correctly, you are saying to use relative paths from the executable/library to the configure file instead of the current absolute ones. That would certainly make the software more relocatable. However, it would also mean that the output directory structure in the CMAKE_BINARY_TREE would not be stable, as with CMAKE_INSTALL_PREFIX set to /usr, the tree would be:

$<CONFIG>/usr/lib
$<CONFIG>/usr/libexec
$<CONFIG>/usr/bin
$<CONFIG>/etc
$<CONFIG>/python-index

Right? (As the configuration files will mostly live in etc, it is important for installed version. Because it can be /etc or /usr/local/etc or /opt/<org_name>/etc and similar.)

(Just to be clear, the libconfig is just a header and ELF file with very few symbols pointing to .rodata section, where the absolute paths to some other locations are stored. It will live in the same */lib as any other library from this project.)

BTW, this is a Linux only project.

Ah, yeah, /etc is a pain. Because if you’re under /usr/local, I’ve seen some things (e.g., BSD Ports) use /usr/local/etc where everything does just work, but then distros tend to stuff everything under /usr and /etc is “in the wind”. For this, I would just hard-code /etc as a search location since it is relative to the entire system in such situations. A flag for whether that is absolute or prefix-relative is probably warranted as well.

Yesterday, I happened to discover that when you set the CMAKE_INSTALL_PREFIX on Debian system to /usr, it will set the CMAKE_INSTALL_LIBEXECDIR to lib/<compiler-triplet> instead of libexec as one would presume, so for example the output artifacts are in ${CMAKE_BINARY_DIR}/Debug/lib/x86_64-linux-gnu and not in ${CMAKE_BINARY_DIR}/Debug/libexec.

So, given that the CMAKE_INSTALL_PREFIX is already influencing the GNUInstallDirs based on OS and such, it probably makes sense to prepend the *_OUTPUT directories with the usr/ for all but etc. (Not sure how it would be implemented in CMake, I haven’t studied the source much yet.)


I have been playing with Ninja Multi-Config in connection with configure_file() followed by file(GENERATE) and I think that most of the configuration file generation issues could be solved by some clever application of generator expressions. (Are you generating the file for the BINARY tree build? Then use this string. Are you generating this file for installation? Then use this other string.)