How to prepend all filenames on the list with common path?
Asked Answered
F

6

64

How can I prepend all filenames on the list with a common path prefix automatically? For instance having a list of files in CMakeLists.txt:

SET(SRC_FILES foo1.cpp foo2.cpp)

I'd like to get a list that is equivalent to this:

${CMAKE_CURRENT_SOURCE_DIR}/foo1.cpp ${CMAKE_CURRENT_SOURCE_DIR}/foo2.cpp

I need this to use filenames in a PARENT_SCOPE context, e.g.

SET(FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${SRC_FILES} PARENT_SCOPE)

so, that a CMakeFiles.txt in another directory can still find these files.

In essence, I'd expect something like this (pseudo-code):

SET(FILES_TO_TRANSLATE PREPEND_ALL_NAMES(${CMAKE_CURRENT_SOURCE_DIR} ${SRC_FILES}) PARENT_SCOPE)

Is this is easily doable, or do I have to user "foreach" loop to create new list of files?

Flown answered 3/12, 2010 at 14:20 Comment(0)
T
68

CMake 3.12 added list transformers - one of these transformers is PREPEND. Thus, the following can be used inline to prepend all entries in a list:

list(TRANSFORM FILES_TO_TRANSLATE PREPEND ${CMAKE_CURRENT_SOURCE_DIR})

...where FILES_TO_TRANSLATE is the variable name of the list.

More information can be found in the CMake documentation.

Tinney answered 3/7, 2018 at 22:35 Comment(6)
If you use this, make sure you add cmake_minimum_required( VERSION 3.12 ) in your CMakeLists.txt file.Hexaemeron
I can't get this working: with set(A "thing"), list(TRANSFORM ${A} PREPEND "some") the content of A is still "thing". What am I doing wrong?Emancipated
You need to reference the list by its name only (e.g. just plain A instead of ${A})Isfahan
And now version 3.15 even added the list(PREPEND ...) command directly.Alter
@Alter list(PREPEND ...), according to the doc you linked, inserts elements to the 0th position in the list. This does not achieve prepending something to each item.Theurich
@Theurich Oh, you're right, of course. My bad, sorry for the confusion.Alter
N
34

Following function may be what you want.

FUNCTION(PREPEND var prefix)
   SET(listVar "")
   FOREACH(f ${ARGN})
      LIST(APPEND listVar "${prefix}/${f}")
   ENDFOREACH(f)
   SET(${var} "${listVar}" PARENT_SCOPE)
ENDFUNCTION(PREPEND)

To use it,

PREPEND(FILES_TO_TRANSLATE ${CMAKE_CURRENT_SOURCE_DIR} ${SRC_FILES})
Near answered 24/12, 2014 at 0:52 Comment(6)
Even though this works, the function declaration is misleading since it only lists 2 function arguments var, prefix. However in practice the user has to pass ${CMAKE_CURRENT_SOURCE_DIR} ${SRC_FILES} as the prefix argument.Lanell
It is true that it requires the third parameter. However, you need to add the third argument to the front of the FOREACH list.Near
What is "FILES_TO_TRANSLATE"?Freshman
FILES_TO_TRANSLATE is just a CMake variable that stores the list of the file, same with what OP used.Near
Does new versions of cmake have a similar (but official) function ?Schizophyceous
Can I update the list inplace without creating a new copy?Graycegrayheaded
N
17
string(REGEX REPLACE "([^;]+)" "ANYPREFIX/\\1.cpp" outputlist "${inputlist}")

Replace ANYPREFIX with any prefix and '.cpp' with any suffix you need.

Naval answered 30/7, 2015 at 14:18 Comment(9)
This is a great answer actually. You may want to preprocess your list to remove the original extension (if that's what you're doing): string(REPLACE ".json" "" inputlist "${inputlist}")Elmer
what does ; means in your regular expression.Eyrir
in cmake list is just a string with semicolon-separated strings, so you can just apply regex or any other string operation to it as a string and be done with it. ([^;]+) mean to "take all characters till next ';'"Naval
"Huh, a regex. Bet it's wrong." Yep. Files containing ; won't work properly. Better to let CMake look through the list - it handles things correctly.Jugendstil
My bet that most regex haters just don't know how to use them properly. Creating file names with semicolon in cmake is shooting yourself in the leg. And cmake doesn't support such file names. Run cmake --help-policy CMP0037 for more information.Naval
Why "1.cpp"? What does that mean?Freshman
It is not 1.cpp, it is \\1 AND .cpp.Naval
Why are you adding an extra .cpp?Cubism
That just an example of how to add both prefix and postfix. Edited answer to make that clear.Naval
D
3

You need to use a foreach loop. But if you use that in several parts of your project, you might want to create a function or a macro.

Demott answered 5/12, 2010 at 14:0 Comment(0)
C
3

As an improvement to Ding-Yi Chen's answer, if you still need to support pre-3.12 CMake as I do, you can use following code:

function(list_transform_prepend var prefix)
    set(temp "")
    foreach(f ${${var}})
        list(APPEND temp "${prefix}${f}")
    endforeach()
    set(${var} "${temp}" PARENT_SCOPE)
endfunction()

Advantage of this solution is, that interface is closer to one is CMake 3.12 list(TRANSFORM ...) and the change is done in-place.

Used like this

list_transform_prepend(FILES_TO_TRANSLATE "${CMAKE_CURRENT_SOURCE_DIR}/")
Cinnabar answered 3/12, 2019 at 10:47 Comment(0)
B
2

I assume that you want an absolute filename as you are prepending ${CMAKE_CURRENT_SOURCE_DIR}. If you are using FILE(GLOB <VAR> <PATTER>), all the files will have an absolute path already:

file(GLOB_RECURSE SOURCE_FILES src/*.cpp)

See the comments CMake in documentation on why not to use GLOB to add source files.

Betsybetta answered 21/3, 2012 at 0:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.