[cmake-developers] conditionals in generator expressions
Stephen Kelly
steveire at gmail.com
Thu Jun 7 13:25:30 EDT 2012
Brad King wrote:
> On 06/07/2012 11:41 AM, Stephen Kelly wrote:
>> Brad King wrote:
>>> On 06/05/2012 04:59 PM, Stephen Kelly wrote:
>>>> generator_expression(myGenExpr)
>>>
>>> That is an interesting idea to keep in mind for the future but I think
>>> the implementation of it is too tricky to be in scope now.
>>
>> That's a little unfortunate. I'm not familiar enough with the existing
>> implementation to understand why yet that is so.
>
> Allowing arbitrary code to run during generate time will be very
> difficult to get right.
Although the questions are rhetorical, I'll answer them anyway to clarify
the intention of the proposal. I'm not trying to push my proposal, but I
want to make sure it's understood :).
> In what initial state does it run?
This is for sure the toughest question.
Assuming that a copy of relevant state can be created by simply copying the
cmMakefile at the appropriate point, possibilities one might consider are:
1) The state of the makefile after project() was invoked for the first time
(so the language, platform and compiler variables are set)
2) The state when the add_{library,executable} was invoked.
3) The state when the property containing $<GENERATOR:foo> was set on the
target.
4) The final state of the makefile before generating started.
(1) is not very useful, because no targets are defined at that point.
(2) is non-optimal because (I think) a copy of the cmMakefile would have to
be made for each target when it was created, regardless of whether generator
expressions were used on it or not.
I also don't think any useful variables are set() between project() and
add_{library,executable} anyway, so it's probably not useful. Any useful
information that the buildsystem would want to set for use by a generator
expression could be set as a property on the target itself, or perhaps a
global property.
(3) 3 might allow more control of how many cmMakefiles need to be copied,
but it otherwise no more useful than (2).
(4) I'm not certain this is possible, it is not more useful than (2).
So the best state to make available would probably be a combination of
* A copy of cmMakefile just after the Platform and Compiler files are
loaded, language is set etc
* The final read only cmTargets
* Possibly global properties
Directory properties would introduce ambiguity about whether to use the
directory where the generator_expression appeared, where the target was
created or where the generator expression was added to the target, so it is
best avoided entirely.
> Can any command be invoked?
I would say only a minimum is needed. Access to everything should be 'read-
only'.
* generate_resolved("foo/bar") would "return" "foo/bar" for the position
specified, and it is the only thing that has any outside effect
* get_property() would be needed
* if() would be needed
* list() would be needed, partly at least, for APPEND
* local(), which works the same as set(), but named to make it clear that
its use has no outside effect.
I think that would be the minimum subset of commands and sub-commands to
make the feature useful.
get_target_property() could be added too for convenience, and foreach() if
it is deemed useful at some point (currently I don't see why it would be).
> What side effects can it have?
None. The only effect of the generator_expression is to "return" a value
through generate_resolved(). It has no effect on any properties or variables
outside of its scope.
> In what order do they run in case side effects interact?
As there are no side effects, the order doesn't matter. The order is
sequential as they appear in the property. So if the property contains the
value:
"/foo/bar;$<GENERATOR:g1>;/bar/bat;$<GENERATOR:g2>"
and g1 happened to return "/bing/bang;/mig/mug" and g2 "/mog/mag", then the
end result written to the makefile would be:
"/foo/bar;/bing/bang;/mig/mug;/bar/bat;/mog/mag"
No properties on the target or globally would be modified. The
generator_expression is simply defined and documented to have no side
effects.
Can you think of a reason to allow side-effects?
> Does
> it run separately for every combination of possible input
> (language, config, etc.)?
Personally I've only ever used Makefile and Ninja generators which only
generate for one config, so I can't give a good answer to this. I just
assumed it was solvable :).
I'll assume that a generator for an ide generates all configurations in one
Generate() step. Assuming it populates one 'generic' field, and one field
for each configuration, what would happen would then be something like
(semi-pseudo-code):
* For the generic field, process the INCLUDE_DIRECTORIES property without
this_Config set and write out the result
* foreach configuration, process the INCLUDE_DIRECTORIES property again,
ignoring non-generator expressions, and execute the generator_expression
with this_Config set as appropriate. The result is a set of (possibly empty)
includes which are unique for each configuration in a configuration specific
field, and a "generic" set.
Or, a generator might not have a "generic" set of includes, and only
populate config-specific fields. I assume there must be a partial solution
to this kind of thing already in place, and I think the generator_expression
would fit into the concept without too much hassle. As you can see, I don't
really know about multi-config generators though.
I don't know if languages would add further complications either. How do
current generators handle that?
> How do names/variables/functions in
> the generator_expression body bind?
They don't. They are local to the generator_expression and are discarded at
the end of it or when generate_resolved is invoked, assuming I understood
the question.
> The above questions are rhetorical to illustrate the difficulty of
> the proposal.
I think the only difficult part is deciding how the state of it should be
defined.
Handling 'public' ones also shouldn't be too difficult. The ExportImport
generator would just have to write the expressions out verbatim (probably
with a namespaced name).
> I'm quite happy with the alternative now proposed.
Yes, it seems reasonable.
>>> $<TARGET_PROPERTY_BOOL:WIN32_EXECUTABLE,tgt> = 0 or 1
>>
>> Are you sure about this one? Given your further explanation below, and
>> assuming the intention is 'link to tgt if *this has a WIN32_EXECUTABLE
>> property which is True', it should rather be:
>>
>> $<$<TARGET_PROPERTY_BOOL:WIN32_EXECUTABLE>:tgt>
>
> The TARGET_PROPERTY_BOOL condition needs to know in what target
> to check the property. Therefore it needs both the target name
> and the property name:
>
> $<$<TARGET_PROPERTY_BOOL:WIN32_EXECUTABLE,exe-getting-linked>:lib-to-
link>
>
> I suppose the "exe-getting-linked" can be implied when the
> expression appears inside the list of link libraries for a target,
Yes. I had assumed this was the only possibility actually. We could simply
define that to be the way it will work, for simplicity.
I'd even define these declarative expressions to only be valid on target
properties, so that, eg, they can't be used in an include_directories()
call.
I can't think of any reason to need to check the value of a property on a
target foo when generating for a target bar, assuming at least that
transitive (PUBLIC) properties have already been processed to enough of an
extent by the time this expression is evaluated.
> just as the $<LANGUAGE:...> condition tests the language implied by
> the context.
>
>>> $<$<CONFIG:Debug>:/path/for/Debug>
>>> $<$<NOT:$<CONFIG:Debug>>:/path/for/non-Debug>
>>> $<$<AND:$<CONFIG:Debug>,$<LANGUAGE:CXX>>:/path/for/Debug/Cxx>
>>
>> There's a lot of repetition, at least in these examples. If there's a lot
>> also in the real world, it might be more maintenance burden than
>> convenience.
>
> The goal is to provide a minimal workable interface. If it becomes
> too verbose we can enhance it later. The above examples are contrived
> to demonstrate capabilities. A more realistic example might be:
>
> set(FOO_INCLUDE_DIRS
> $<$<CONFIG:Debug>:${FOO_INCLUDE_DIRS_DEBUG}>
> $<$<CONFIG:Release>:${FOO_INCLUDE_DIRS_RELEASE}>
> )
>
> Even if a given condition needs to be repeated it can be stored in
> a normal variable "cond" and referenced later "${cond}".
Yes, true. I guess a larger real-world use would have to be mocked up to get
a full idea of the (multiple) syntaxes which would no doubt appear in the
wild and in mailing list queries appear such as:
* $<$<CONFIG:Debug>:${FOO_INCLUDE_DIRS_DEBUG}>
* $<${debug_cond}:${FOO_INCLUDE_DIRS_DEBUG}>
* $<${debugCond}:${FOO_INCLUDE_DIRS_DEBUG}>
* $<${debugCond}${FOO_INCLUDE_DIRS_DEBUG}> # An error if cond is empty.
> The
> final verbose generator expressions that the evaluator sees do not
> have to appear verbatim in source.
Yes.
I don't think yours is a bad idea, but I'm trying to tease out some of the
consequences of it. We'll still need to plan the implementation of it anyway
:)
Thanks,
Steve.
More information about the cmake-developers
mailing list