9

I'm trying to make another guy's research code reproducible, so that others don't have the same trouble I'm having right now. But, I'm lacking the experience with cmake for that. Here is what I could do after reading the docs:
In the same folder as my CMakeLists.txt I have a file called io_utils.h with a ROOT_PATH variable whose value is VALUE_ROOT_PATH. I want to replace that string with the value of the file's current directory. So I tried to add the following to CMakeLists.txt:

# Set ROOT_PATH in io_utils.h
FIND_PATH(BUILD_PATH CMakeLists.txt . )
FILE(READ ${BUILD_PATH}io_utils.h IO_UTILS)
STRING(REGEX REPLACE "VALUE_ROOT_PATH" "${BUILD_PATH}" MOD_IO_UTILS "${IO_UTILS}" )
FILE(WRITE ${BUILD_PATH}io_utils.h "${MOD_IO_UTILS}")

I tried to make and install that but it is not working, the file isn't changed. What's wrong with what I did?

Eder Santana
  • 439
  • 1
  • 4
  • 8

3 Answers3

21

I suggest a different approach using the configure_file cmake macro. First, you create a template file that references any variables you plan to set in cmake, then the macro substitutes the actual value in place of the variable. For example, let's say we have a template file io_utils.h.in that looks something like this:

const char* ROOT_PATH = "${BUILD_PATH}";

In your CMakeLists.txt, you can do something like this:

configure_file( io_utils.h.in io_utils.h )

When you run cmake, it will use the first file as a template (io_utils.h.in here) to generate the second file (called io_utils.h here) with the value substituted in:

const char* ROOT_PATH = "/path/to/CMakeLists.txt";

By the way, CMake has a built-in variable that references the directory with the top-level CMakeLists.txt called CMAKE_SOURCE_DIR. Try replacing BUILD_PATH above with this variable.

atsui
  • 1,008
  • 10
  • 17
  • 2
    Since not all files are headers which you may want to search/replace, it would still be interesting to have an answer to the original question, as well as this suggested alternative. – ideasman42 Mar 12 '15 at 10:25
1

In this case it seems to be a lot easier to define a macro and let the preprocessor do the job:

add_definition(ROOT_PATH ${VALUE_ROOT_PATH})

  • 2
    This has the disadvantage that the file becomes dependent on the define from the build environment. For instance, a header in a library that you want to ship to clients probably should be self-contained and not have such a dependency. But if that is of no concern, this is a valid solution. – ComicSansMS Jul 20 '16 at 11:42
0

The answer by @atsui is fine for most cases and his approach should probably be used by default. But sometimes you may already have some ini-file that you want to edit according to your needs, and you don't want to create and support a template just to be able to use configure_file command.

For such cases the approach from OP may work as well. Actually, the code in the question is almost working. I only needed to add a path separator between ${BUILD_PATH} and io_utils.h, i.g. it should be ${BUILD_PATH}/io_utils.h in both file commands.

There are several things to note about this code, though:

  1. The code edits the file in source tree. Generally, out-of-source builds should be preferred, since they have many pros over the in-source builds. For out-of-source version, CMAKE_BINARY_DIR variable may be useful.
  2. string(REGEX REPLACE ...) is used, but in this case string(REPLACE ...) may be enough.
  3. You may actually use the same variable as input and as output in string(REGEX REPLACE ...) and string(REPLACE ...).
  4. Please note, that file(WRITE ... may change line ending type, if line endings do not correspond to host system. If this is not acceptable, then file(GENERATE ... CONTENT ... NEWLINE_STYLE ...) may be used instead (but it requires at least CMake 3.20).
  5. Don't forget about the double quotes around the content variable value, otherwise you will lose your semicolons.

Having all this, below is just another example of how to edit file in CMake.

file(READ "${SOURCE_DIR}/file.ini" FILE_CONTENT)
string(REPLACE "one" "two" FILE_CONTENT "${FILE_CONTENT}" )
file(WRITE "${CMAKE_BINARY_DIR}/file.ini" "${FILE_CONTENT}")
install(FILES "${CMAKE_BINARY_DIR}/file.ini" DESTINATION etc)
Alex Che
  • 6,659
  • 4
  • 44
  • 53