I still have't found the detailed documentation but I have done a ton of experimentation and have concluded the following...
(Note: my examples use some concepts from Sencha GXT which I develop with)
<define-property> is used to define the property and its possible values but is not the last word in terms of which values will be actually used. There should be exactly one such definition per property in the hierarchy of the *.gwt.xml files.
<set-property> does NOT set the property to A value but creates a sort of a rule with the COLLECTION of POSSIBLE values for that property (ideally to a subset of what is defined in <define-property>). The last declaration of <set-property> for a given property wins. Be careful! If you use a <set-property> after <inherits> (inheriting a module), you may/will be overriding any rules that you inherited. Note that there are conditional rules and unconditional rules. For example,
<set-property name="gxt.user.agent" value="ie10, ie11, gecko1_9, safari5, chrome"/>
... will unconditionally state that, when determining permutations, the property 'gxt.user.agent' can have any one of the listed values. Conversely,
<set-property name="user.agent" value="safari">
<any>
<when-property-is name="gxt.user.agent" value="safari5" />
<when-property-is name="gxt.user.agent" value="chrome" />
</any>
</set-property>
Will state that user.agent can only be "safari" when "gxt.user.agent" is either "safari5" or "chrome". The order seems important, so you would want rules for dependent properties declared after the rules for their dependencies. Cyclic dependencies will fail the compilation. You can create many conditional rules as long as they don't contradict one another. I do not know yet what would happen if they do, but I guess that the last declared would win.
Note that the conditional rules can also specify multiple possible values. For example (illustration only, may not match your needs):
<!-- On desktops we do all browsers including IE -->
<set-property name="gxt.user.agent" value="safari5, chrome, gecko1_9, ie11">
<when-property-is name="gxt.device" value="desktop" />
</set-property>
<!-- ... but on tablets and phones we exclude IE -->
<set-property name="gxt.user.agent" value="safari5, chrome, gecko1_9">
<any>
<when-property-is name="gxt.device" value="tablet" />
<when-property-is name="gxt.device" value="phone" />
</any>
</set-property>
You can use <any> and <all> to create complex/compound criteria. These can be nested inside one another.
How can this be used to get precise control of permutations? You may not need this but it helped me to begin by defining two properties like these:
<define-property name="custom.use.case" values="case1, case2, ..."/>
<property-provider name="helix.product.mode"><![CDATA[
var useCase = ...; // JavaScript code to determine the use case
return useCase;
]]>
</property-provider>
<define-property name="custom.permutation" values="perm1, perm2, ..."/>
The first property defines the use case. Above I have a way to determine it at runtime. You may not and may skip it. It also serves as a starting point to define everything else as we can define all the other properties based on the use case. For example:
<!-- Case 1 restrictions -->
<set-property name="gxt.device" value="desktop">
<when-property-is name="custom.use.case" value="case1" />
</set-property>
<set-property name="gxt.user.agent" value="chrome, safari5, gecko1_9, ie11">
<when-property-is name="custom.use.case" value="case1" />
</set-property>
...
<!-- Case 2 restrictions -->
<set-property name="gxt.device" value="tablet, phone">
<when-property-is name="custom.use.case" value="case2" />
</set-property>
<set-property name="gxt.user.agent" value="chrome, safari5, gecko1_9">
<when-property-is name="custom.use.case" value="case2" />
</set-property>
...
<!-- Case 3 restrictions -->
<set-property name="gxt.device" value="tablet, phone">
<when-property-is name="custom.use.case" value="case3" />
</set-property>
<set-property name="gxt.user.agent" value="safari5">
<when-property-is name="custom.use.case" value="case3" />
</set-property>
...
etc.
Next thing to do is to collapse all the properties except for the custom.permutation, because we only want custom.permutation to drive the permutations:
<collapse-property name="custom.use.case" values="*" />
<collapse-property name="gxt.device" values="*" />
<collapse-property name="gxt.user.agent" values="*" />
<collapse-property name="user.agent" values="*" />
<collapse-property name="user.agent.os" values="*" />
This approach allows extremely fine grain control over "permutations" and their internal complexity, the "soft permutations" as some call them. There will be exactly one permutation for each possible "custom.permutation" property value. Each permutation will only have the needed "soft permutations" in it if you do a good job of setting up the rules above.
Note that both actual and soft permutations cost. Having many soft permutations (grouped into actual permutations or not) costs compile performance and runtime code size of the permutation they are grouped in. Do not ignore this, especially if you have many properties. Actual permutations have even higher compile and linking time cost (but having more of them as opposed to combining many soft permutations into grouped actual permutations reduces the size of each permutation).
If using GXT, starting with version 4, you will notice that it adds gxt.device property having the values such as desktop, tablet and phone. This caused our compilation time to increase about 6-7 times after upgrading to GXT 4 because our permutations went out of control. Some ended up being, quite literally, made for Internet Explorer running on a Mac OS tablet. You can appreciate what a waste of time permutations for non-existent use cases are. By implementing the above we were able to reduce the GWT compilation time down to about half of the original time or about 10-12 times faster than what they were right after GXT 4 upgrade.