Skip to content

Draft: Make custom target exporting more convenient

When a target properties need to be exported in a custom format, for example to generate a pkg-config .pc file used to be simple back in the days, but today, with various generator expression used in target properties and multi-config generators this is no longer a simple task.

A straightforward method using file(GENERATE ...) at the generation step does not really work because it exports the $<BUILD_INTERFACE: (one can inter-exchange $<INSTALL_INTERFACE: and $<BUILD_INTERFACE: to workaround that, but that is tedious for nested in target properties generator expressions).

Then multiple and mapped configurations do not add to simplicity of the solution. An obvious way to overcome that complication is to use generator expression, but there is no simple way to work with targets lists and lists in general with generator expressions.

This MR attempts to address all the above listed problems, greatly simplifying custom code for exporting targets, and these new tools perhaps can be of use in other cases as well.

What's included?

  1. A new ability for file(GENERATE ...) to specify target interface kind to operate on. This is implemented via adding a new member InterfaceMode to the cmGeneratorExpressionContext class, which is an enumerator with values Build and Install. cmGeneratorExpressionNode::GetNode() receives new parameter, const cmGeneratorExpressionContext& context, and basing on that it returns OneNode or ZeroNode for BUILD_INTERFACE and INSTALL_INTERFACE. The file(GENERATE ... command receives a corresponding parameter TARGET_INTERFACE with possible values BUILD_INTERFACE or INSTALL_INTERFACE, which defaults to BUILD_INTERFACE and passes this value to the file generation. This allows to select the INSTALL_INTERFACE part for generating.

  2. The $<TARGET_PROPERTY:tgt,prop> expression is changed to allow lists in the tgt argument, and it returns a list as well. This is breaking change as empty list is allowed, i.e. no target.

  3. New generator expression $<TARGET_PROPERTY_RECURSIVE:tgt_list,prop[,collect_targets]> is added, which works as $<TARGET_PROPERTY:...> but also scans its result and expands each target found recursively. When collect_targets is 1 it changes behavior and instead of collecting target properties collects encountered targets. The purpose of this mode is to get all the targets required for linking from properties like INTERFACE_LINK_LIBRARIES.

  4. New generator expression $<TRANSFORM:list,action,[action args...]> is added. The action is the name of a generator expression. A placeholder arg (_1) can be given in any of the optional parameters, which will be replaced with the list element value when executing the action. Example: $<TRANSFORM:$<TARGET_PROPERTY:target,INTERFACE_LINK_LIBRARIES>,TARGET_LINKER_FILE,_1>

  5. A new generator expression $<TARGET_LINKER_ARTIFACT:tgt> is added, which is just a shortcut to conditional querying different properties for the linker argument (-lname or linking library file path). It returns INTERFACE_LINK_LIBRARIES for targets of INTERFACE_LIBRARY type and fallbacks to $<TARGET_LINKER_FILE:tgt> otherwise.

The following decisions need to be made:

  • 1. file(GENERATE ...) and the cmGeneratorExpressionContext can refer to either build or install interface. Maybe there should be flags instead, allowing specify "none" and "both"?
  • 2. Should every $<TARGET_...:tgt,...> generator expression accept list of targets?
  • 3. $<TRANSFORM:list,action> accepts action as a name of other expression. Is there a better way to pass action? Maybe we can add a special argument notation to change expression expansion order, or add another expression for it (like TeX does with \expandafter)? For example: $<TRANSFORM:target_list,($<TARGET_PROPERTY:_,LOCATION)> to map targets or $<TRANSFORM:property_list,($<TARGET_PROPERTY:tgt,_)> to map properties. Here brackets would delay expansion of the $<TRANSFORM:...> arguments.
  • 4. The $<TARGET_LINKER_ARTIFACT:...> expression is added because with the current implementation I can't put the following equivalent expression in $<TRANSFORM:...> action: $<IF:$<STREQUAL:$<TARGET_PROPERTY:tgt,TYPE,INTERFACE_LIBRARY>:$<TARGET_PROPERTY:tgt,INTERFACE_LINK_LIBRARIES>:$<TARGET_LINKER_FILE:tgt>>. But should that become possible maybe $<TARGET_LINKER_ARTIFACT:tgt> is unneded.

Todos:

  • Investigate stripping cmGeneratorExpression::Preprocess() of interface mode manipulations.
  • Investigate converting other target-dependent generator expressions to target list processing.
  • Update and add tests for new and changed generator expressions.
  • Manual and changelog entries.
Edited by Eugene Shalygin

Merge request reports