CMake add prefix to each element of a list - cmake

In GNU Make I would have written this:
DEF=FOO BAR BAZ
CMAKE_REQUIRED_DEFINIITIONS=$(addprefix -D,$(FOO))
Is there an equivalent in CMake other than doing this:
macro(addprefix prefix list_name)
SET(${list_name}_TMP)
foreach(l ${list_name})
list(APPEND ${list_name}_TMP ${prefix}${l} )
endforeach()
SET(${list_name} ${list_name}_TMP)
UNSET(${list_name}_TMP)
endmacro(addprefix)
set(APPEND required_definitions FOO BAR BAZ)
set(CMAKE_REQUIRED_DEFINITIONS addprefix(-D required_definitions))
CMake is supposed to be easier to use than GNU Make...

Reformulating my previous comment as answer:
target_compile_definitions(your_target PUBLIC ${your_list})
is probably the CMake command you are looking for.

Related

CMake: pass library name to grandparent, but only if grandparent exists

A library foo is to be built either as a project of its own, or as part of a larger project bar. For the latter, I found no better solution than line (*):
$ cat foo/lib/CMakeLists.txt
...
set(foo_LIBRARY foo PARENT_SCOPE)
...
$ cat foo/CMakeLists.txt
...
add_subdirectory(lib)
set(cerf_LIBRARY ${cerf_LIBRARY} PARENT_SCOPE) # (*)
...
$ cat bar/CMakeLists.txt
...
add_subdirectory(link-to-foo)
...
Now building bar works. But when building only foo, I get
CMake Warning (dev) at CMakeLists.txt:30 (set):
Cannot set "foo_LIBRARY": current scope has no parent.
Aiming for zero warnings in my projects, I'd need a better solution.

Dealing with the separator in CMake

I'm trying to compile some Java code with CMake (I'm aware that Java is not really the use-case for CMake) and I want to provide the class paths for the files. The compilation should work on both Unix and Windows systems. The problem I have is with separating the different class paths. Using:
set(CLASS_PATH ${PATH1} ${PATH2})
message(STATUS "${CLASS_PATH}")
prints
<PATH1>;<PATH2>
But this happens on both Unix and Windows. So I have to manually add separators. The way I'm doing it is
if(${CMAKE_HOST_WIN32})
set(SEP "\;")
elseif(${CMAKE_HOST_UNIX})
set(SEP ":")
endif(${CMAKE_HOST_WIN32})
Is this really the best way to deal with separators? I feel like I'm missing something.
Update - MCVE
To describe my thought: FILE_LIST would be contain all the java files that I want to compile. I defined a custom function which I can call on this FILE_LIST and compile the files. Maybe I'm doing something wrong with the function parameters?
cmake_minimum_required(VERSION 3.11)
set(CLASS_PATH E:/tmp/cmake/separator C:/tmp/)
set(FILE_LIST 1.txt 2.txt 3.txt)
add_custom_target(war ALL)
function(compile_java clp)
foreach(java_file ${ARGN})
add_custom_command(
TARGET war
PRE_BUILD
COMMAND echo "${clp}" ${java_file}
)
endforeach(java_file)
endfunction()
compile_java("${CLASS_PATH}" ${FILE_LIST}) # I have to pass CLASS_PATH in quotes
So, based on comments, you want the path list as a single command-line argument, with a platform-specific separator. You can achieve this using string operations:
function(compile_java clp)
if(NOT CMAKE_HOST_WIN32)
string(REPLACE ";" ":" clp "${clp}")
endif()
foreach(java_file ${ARGN})
add_custom_command(
TARGET war
PRE_BUILD
COMMAND echo "${clp}" ${java_file}
)
endforeach(java_file)
endfunction()

Inline conditionals in CMake in argument list?

Is it possible in CMake to have inline conditonals within argument lists somehow?
Example of what I want (the whole IF line is not parsed but handled like a string, but I want it parsed):
LIST(APPEND myList
foo
bar
IF(MINGW) hello ENDIF()
cool
)
instead of what I have now
LIST(APPEND myList
foo
bar
)
IF(MINGW)
LIST(APPEND myList hello)
ENDIF(MINGW)
LIST(APPEND myList cool)
Something similar to the example would make my CMakeLists.txt files way easier to read at many places! Especially if there's a specific order that needs be kept the CMake code gets very big without inline conditionals sometimes, because one needs to repeat the same call everytime.
Note: I took LIST as an example here, the question should be seen as general for other functions, too!
There is currently no such feature in CMake, although I agree it would be quite useful in certain situations.
I usually rely on the fact that CMake has no problem with ignoring empty values in most contexts:
if(MINGW)
set(ADDITIONAL_ITEMS hello)
endif()
list(APPEND mylist
foo
bar
${ADDITIONAL_ITEMS}
cool
)
It's not perfect, but IMHO at least cleaner than appending to the same list twice. The same technique also works for conditionally passing function parameters.
Note that depending on the context where this is needed, CMake generator expressions might be an option:
target_link_libraries(t foo bar $<$<BOOL:${SOME_CONDITION}>:hello> cool)
You can use the PLATFORM_ID generator expression, depending on what you're doing with the list you're creating:
http://www.cmake.org/cmake/help/v3.0/manual/cmake-generator-expressions.7.html
target_link_libraries(t foo bar $<$<PLATFORM_ID:MINGW>:hello> cool)
Apart from target_link_libraries, such expressions work with target_include_directories, target_compile_definitions, target_compile_options, target_compile_features (CMake 3.1), target_sources (CMake 3.1), file(GENERATE), install(FILES), add_custom_target etc. You get the idea :).
Unfortunately, no.
The only way to conditionally build lists without repetition is with list APPEND (BTW unless ordering matters, you can simplify it by adding cool in the list definition).
CMake syntax is quite verbose and limited and it is often hard not to repeat yourself. That is why I have sometimes ended up generating parts of the CMake code in another language.

Best way to check with CMake whether list containts a specific entry

I want to check whether a lists contains a specific entry like in the following code snipplet:
macro(foo)
if ($(ARGN} contains "bar")
...
endif
endmacro()
CMake does not offer a contains. What is best / easiest way to get the desired result?
In CMake's wiki I found a LIST_CONTAINS macro, but the wiki page is outdated. Is this still the best way to go or has CMake gained new capabilities?
With CMake 3.3 or later, the if command supports an IN_LIST operator, e.g.:
if ("bar" IN_LIST _list)
...
endif()
For older versions of CMake, you can use the built-in list(FIND) function:
list (FIND _list "bar" _index)
if (${_index} GREATER -1)
...
endif()
Fewer lines:
if (";${ARGN};" MATCHES ";bar;")
# ...
endif()
But see the IN_LIST syntax from #sakra for a more-modern syntax.
I have been using one liner like if ("${PLATFORM}" MATCHES "^(os|ios|android|linux|win32)$") to check if PLATFORM is in the list
If the intention here is to add a value to a list but only if it's not already in the list, then an alternative approach is to just add it to the list and immediately remove possible duplicates again:
list(APPEND SOME_LIST "value")
list(REMOVE_DUPLICATES SOME_LIST)

cmake list append for compiler flags yields bogus results?

I need to add various flags to my C and C++ compile lines in my CMake files (CMake 2.8.10.2). I see some people use add_definitions but from what I can see that is intended for preprocessor flags (-D). I have some flags that I don't want passed to the preprocessor.
So I've been trying to modify CMAKE_C_FLAGS and CMAKE_CXX_FLAGS. I see that some people were using something like:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -new -flags -here")
but then I read in the cmake docs that this is less efficient, and the right way to do it is to use list(APPEND ...), like this:
list(APPEND CMAKE_C_FLAGS -new -flags -here)
However, when I do this my compile line contains the flags separated by semicolons and is a syntax error. I read that this is now lists are stored internally, but I figured this would be taken care of by cmake when I used the variable. This seems so basic; am I doing something wrong? I mean, what the heck good are these lists if they can't be used unless you happen to want a semicolon-separated list of values (and who wants that, other than I guess Windows %PATH% settings or something)? Should I be using the quoted version even though the docs suggest it's less efficient/appropriate?
In CMake, a "list" is a string of items separated by semi-colons. For example:
set(FOO "a")
list(APPEND FOO "b") # now FOO="a;b"
list(APPEND FOO "c") # now FOO="a;b;c"
In CMake, a string of space-seperated items is just a string, not a list. Use the string(APPEND) command to append to it. For example:
set(FOO "a")
string(APPEND FOO " b") # now FOO="a b"
string(APPEND FOO " c") # now FOO="a b c"
On old versions of CMake that lack the string(APPEND) command, you should fallback to the set command. For example:
set(FOO "a")
set(FOO "${FOO} b")
set(FOO "${FOO} c")
In this case, you would indeed normally use the set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -new -flags -here") technique.
You're right in that in most other contexts CMake can "translate" a semi-colon separated list into something meaningful for the compiler (e.g. the list of source files in an executable), but in this case, CMake takes the flags as a single, complete string to pass to the compiler/linker.
You could if you really wanted keep the list of flags as a CMake list, but then before exiting the CMakeLists.txt, you could yourself "translate" the list into a single string value of CMAKE_C_FLAGS, but it's unusual to see this.