[cmake-developers] conditionals in generator expressions

Brad King brad.king at kitware.com
Mon Aug 20 09:11:19 EDT 2012


On 08/19/2012 05:43 PM, Stephen Kelly wrote:
> Thanks, here are my findings so far, which relate to my extension of that 
> branch at git at gitorious.org:~steveire/cmake/steveires-cmake.git:

Thanks for working on this.

> 1) The result string of the $<CONFIG:...> expression does not allow lists. 
> That means we can't do anything like this:
> 
> include_directories("$<$<CONFIG:Release>:/foo;/bar/bat>")
> include_directories("$<$<CONFIG:Release>:${somedirs}>") 
> # somedirs might contain a list
> 
> I fixed that in my branch by modifying cmSystemTools::ExpandListArgument. 

It's not the generator expression parsing that can't handle ';'.  It's
that the division on ';' occurs before generator expressions are processed.
Try evaluating them in the original INCLUDE_DIRECTORIES property string
*before* parsing it out as a list.  In this hunk:

-    cmSystemTools::ExpandListArgument(prop, includes);
+    std::vector<std::string> unProcessedIncludes;
+    cmSystemTools::ExpandListArgument(prop, unProcessedIncludes);

put the ge.Process() in the first argument of ExpandListArgument.

> 2) Because of the way generator expressions are parsed, the > character 
> can't be part of the result string. This is an unlikely but possible 
> character to appear in include directories for example. 

We could create a generator expression such as "$<RANGLE>" that expands
to right-angle.  The expanded values are not re-processed by the parser
so if it expands to '>' it would be accepted literally.

> This seems to be because the generator expression can start and end anywhere 
> in the input string to the Process method, instead of assuming that the 
> start of the string is the start of the generator expression, and the end is 
> the end of it. The latter probably makes more sense in the context of target 
> property generator expressions. Eg, I'd prefer this to be illegal:
> 
> include_directories("/tmp/$<$<CONFIG:Release>:foo/>bar") 
> # Could include /tmp/bar or /tmp/foo/bar, but not very readable.
> 
> Can this be changed?

I consider this a feature, and with my answer to (1) you would not have
boundaries to test anyway.

> 3) I can add something like $<TARGET_INTERFACE_INCLUDE_DIRS:tgt> in the 
> future to expand to the interface include directories of a target (currently 
> cmake has no such concept, but I'm thinking in the future).
> 
> However, I don't think something like 
> 
> $<$<CONFIG:Release>:$<TARGET_INTERFACE_INCLUDE_DIRS:tgt>> 
> 
> would work, would it? The 'right hand side' of the expressions must be the 
> final string?

It would work just fine.  The first "$<" token will be kept as a boundary
on the stack of opening tokens by the parser until after the other two
are expanded.  When the last ">" is reached then the expression will be
either "$<0:/include/dirs>" or "$<1:/include/dirs>" and will be evaluated
as one of those expressions.

> 4) I'm really not certain if I've implemented the patch for conditional link 
> libraries in the right place. I tried a few different locations. I'm also 
> not sure if there is logic at configure time which can't be implemented in 
> terms of the generator expressions which are not evaluated by then. I tried 
> using it with imported targets which had entries in their 
> IMPORTED_LINK_INTERFACE_LIBRARIES, which seemed to work, so that's 
> encouraging.

It should be in cmTarget::ComputeLinkImplementation at this line:

      // The entry is meant for this configuration.
      impl.Libraries.push_back(item);

Process the value of the item string and use ExpandListArgument on the
result to append to impl.Libraries.

Also in cmTarget::ComputeLinkInterface in the "if(explicitLibraries)"
block between these two lines:

    // The interface libraries have been explicitly set.
    cmSystemTools::ExpandListArgument(explicitLibraries, iface.Libraries);

Process the value of explicitLibraries before expanding as a list.

Generator expressions should be processed directly after reading the
original string value from the user interface that provides it.

> 5) I think it's quite cool that I can add something like 
> 
> set(_WIN32_EXE "$<AND:$<TARGET_PROPERTY_STREQUAL:TYPE,EXECUTABLE>,
> $<TARGET_PROPERTY_BOOL:WIN32_EXECUTABLE>>")
> target_link_libraries(Qt5::Core
>   LINK_INTERFACE_LIBRARIES
>    "$<${_WIN32_EXE}:Qt5::WinMain>"
> )
> 
> and it will lie dormant and not have any effect on any shared libraries 
> until it is finally linked to an executable :). Several features come 
> together nicely there.

Nice.  BTW, I think the TARGET_PROPERTY_STREQUAL expression can use
"==" instead of "," as the separator between property name and value.

> 6) In my new implementation of cmTarget::GetIncludeDirectories, it is now 
> necessary for that method to only be called at generate-time. As far as I 
> can see, that is already true for that method, but for maintenance it is a 
> bit difficult. Are there other examples of similar requirements of when an 
> API can be meaningfully called? It seems to be something that could benefit 
> from refactoring throughout cmake if so.

This has been pretty messy in the past.  Recently I created the
cmGeneratorTarget class to hold information/interfaces for a target
that only make sense at generate time.  You could move the method
over there.

-Brad



More information about the cmake-developers mailing list