1

I'm refactoring a cmake file to use modern CMAKE practices for an personal project. However since I don't believe the operating system comes as a generalized variable, in order to test the operating system in CMAKE I have to do the following (based on this answer):

if(UNIX AND NOT APPLE)
    set(LINUX TRUE)
endif()
#...
if(LINUX)
    set(MY_PROJECT_LINK_ARGUMENTS ...)
elseif(WIN32)
    set(MY_PROJECT_LINK_ARGUMENTS ...)
else()
    message( FATAL_ERROR "Unsupported operating system")
endif()

add_library(xxx STATIC)
#...
target_link_libraries(xxx INTERFACE ${MY_PROJECT_LINK_LIBRARIES} ...)

add_library(yyy STATIC)
#...
target_link_libraries(yyy INTERFACE ${MY_PROJECT_LINK_LIBRARIES} ...)

add_library(zzz STATIC)
#...
target_link_libraries(zzz INTERFACE ${MY_PROJECT_LINK_LIBRARIES} ...)

What I'm confused about is how to accomplish this in idiomatic modern target based CMAKE. Since I don't know of a CMAKE variable that exists that generally encapsulates the operating system, I created an OS variable.

if(UNIX AND NOT APPLE)
    set(LINUX TRUE)
    set(PROJECT_OS LINUX)
elseif(APPLE)
    set(PROJECT_OS APPLE)
elseif(WIN32)
    set(PROJECT_OS WIN32)
endif()

And then I create a generator expression

target_link_libraries(xxx INTERFACE
    $<$<BOOL:$<PROJECT_OS:LINUX>>: ...> 
    $<$<BOOL:$<PROJECT_OS:WIN32>>: ...>
    ...)

But now I've already created a variable and if I were to pass the same arguments to each target (xxx, yyy, zzz) I would have duplicated the same thing several times.

The only way I see to solve this is with a variable, but modern CMAKE guidelines apparently say we should stay far away from variables (example A, B, and C), so how am I supposed to accomplish this task with out them?

Krupip
  • 4,404
  • 2
  • 32
  • 54

1 Answers1

0

Variables aren’t evil per se. They have two major potential problems:

  • They introduce global-ish state, which might become a maintenance nightmare.
  • They might lead to an incompletely exported CMake project, creating problems for everyone who needs to depend on your project in their CMake system.

Creating project-global variables for a more fine grained distinction between OSs is straight forward enough to not have these problems. Putting OS dependent link arguments into variables feels smelly from the maintenance point of view. I’d keep the OS variables and put the OS dependent linking into a function, like so:

...
function(set_os_dependent_dependencies target_name)
    if (LINUX)
        target_link_libraries(${target_name} ...)
    elseif(WIN32)
        target_link_libraries(${target_name} ...)
    endif()
endfunction()

add_library(xxx STATIC)
set_os_dependent_dependencies(xxx)
...
besc
  • 2,507
  • 13
  • 10
  • what about compiler dependent arguments? In a lot of the examples, they show this done through generator expressions, and shun other methods as harder to read. Are we meant to duplicated this stuff or use variables in that case? – Krupip Oct 01 '18 at 16:53