-
Notifications
You must be signed in to change notification settings - Fork 373
Conditions
Conditions are used to drive dynamic content genarating or replacing.
Conditions use C++ style of conditional preprocessor expressions. Expressions are composed from constant literals (strings, numbers, true
, false
), operators, symbols, brackets and whitespaces. Only single line expressions are supported. Boolean and numerical expressions are supported (nonzero value is interpreted as true
)
Sample conditions in source code
Unlike C++ preprocessor conditions, template engine allows ability for using conditional expressions that are based on results of other expressions. Specifically Evaluate and Computed symbols can be leveraged for this purpose.
(other related sample in GeneratorTest.json):
template.json
:
"symbols":{
"langVersion": {
"type": "parameter",
"datatype": "text",
"description": "Sets the LangVersion property in the created project file",
"defaultValue": "",
"replaces": "$(ProjectLanguageVersion)",
"displayName": "Language version"
},
"csharp10orLater": {
"type": "generated",
"generator": "regexMatch",
"datatype": "bool",
"parameters": {
"pattern": "^(|10\\.0|10|preview|latest|default|latestMajor)$",
"source": "langVersion"
}
},
"csharpFeature_ImplicitUsings": {
"type": "computed",
"value": "csharp10orLater == \"true\""
},
}
Program.cs
:
#if (!csharpFeature_ImplicitUsings)
using System;
#endif
Choice Symbol can have one of N predefined values. Those predefined values can be referenced in the conditions as quoted literals. Unquoted literals are as well supported as opt-in feature via enableQuotelessLiterals
. Following 2 expressions are equivalent when opted in:
#if (PLATFORM == "Windows")
#if (PLATFORM == Windows)
This allows for easier authoring of nested generated conditions.
Information about multi-choice symbols can be found in Reference for template.json
Comparison to multi-choice symbol results in operation checking of a presence of any value of a multi-choice parameter - meaning ==
operator behaves as contains()
operation. Example (sourced from integration test):
template.json
:
"symbols": {
"Platform": {
"type": "parameter",
"description": "The target platform for the project.",
"datatype": "choice",
"allowMultipleValues": true,
"enableQuotelessLiterals": true,
"choices": [
{
"choice": "Windows",
"description": "Windows Desktop"
},
{
"choice": "WindowsPhone",
"description": "Windows Phone"
},
{
"choice": "MacOS",
"description": "Macintosh computers"
},
{
"choice": "iOS",
"description": "iOS mobile"
},
{
"choice": "android",
"description": "android mobile"
},
{
"choice": "nix",
"description": "Linux distributions"
}
],
"defaultValue": "MacOS|iOS"
}
}
Program.cs
:
#if (Platform == MacOS)
// MacOS choice flag specified here
#endif
In above example if Platform
has it's default value (MacOS
and iOS
) or if those 2 values are passed to the engine (e.g. via command line: dotnet new MyTemplate --Platform MacOS --Platform iOS
), the condition in Program.cs
file will be evaluated as true.
Order of operands doesn't matter - PLATFORM == Windows
evaluates identical as Windows == PLATFORM
. Comparing 2 multi-choice symbols leads to standard equality check
Cases that needs evaluation of different type of condition over multi-choice symbols than 'contains' (e.g. exclusive equality or membership in subset of possible values) can be achieved with slightly more involved condition - so we recommend definition of aliases via computed conditions.
Lets consider following multi-choice symbol:
template.json
:
"symbols": {
"PLATFORM": {
"type": "parameter",
"description": "The target platform for the project.",
"datatype": "choice",
"allowMultipleValues": true,
"enableQuotelessLiterals": true,
"choices": [
{
"choice": "Windows",
"description": "Windows Desktop"
},
{
"choice": "WindowsPhone",
"description": "Windows Phone"
},
{
"choice": "MacOS",
"description": "Macintosh computers"
},
{
"choice": "iOS",
"description": "iOS mobile"
},
{
"choice": "android",
"description": "android mobile"
},
{
"choice": "nix",
"description": "Linux distributions"
}
],
"defaultValue": "WindowsPhone|iOS|android"
}
}
Then Checking whether platform is a mobile platform can be performed with following condition: (PLATFORM == android || PLATFORM == iOS || PLATFORM == WindowsPhone) && PLATFORM != Windows && PLATFORM != MacOS && PLATFORM != nix
Checking for one and only one platform needs similarly involved condition: PLATFORM == android && PLATFORM != iOS && PLATFORM != WindowsPhone && PLATFORM != Windows && PLATFORM != MacOS
This is given by the fact that we do not support exclusive equality operator (in the future, if needed, we can introduce dedicated operator for that - e.g. ===
).
To simplify templates and make them more readable - following computed conditions can be defined:
template.json
:
"symbols": {
"IsMobile": {
"type": "computed",
"value": "(PLATFORM == android || PLATFORM == iOS || PLATFORM == WindowsPhone) && PLATFORM != Windows && PLATFORM != MacOS && PLATFORM != nix"
},
"IsAndroidOnly": {
"type": "computed",
"value": "PLATFORM == android && PLATFORM != iOS && PLATFORM != WindowsPhone && PLATFORM != Windows && PLATFORM != MacOS && PLATFORM != nix"
},
}
Usage can then look as following:
Program.cs
#if IsAndroidOnly
// This renders for android only
#elseif IsMobile
// This renders for rest of mobile platforms
#else
// This renders for desktop platforms
#endif
Parameter symbols in template can be specified together with optional conditions:
-
IsEnabled Condition
- Used to determine when (or if) this symbol should be used. If this condition is specified and evaluates tofalse
(or afalse
constant is passed), then this parameter is treated as if it does not exist. This applies to the use of conditional processing of sources and replacements. Verification of mandatory parameters does not consider disabled parameters (even if marked as required). -
IsRequired Condition
- defines if parameter is required or optional.
Input - currently only other parameter symbols from the template configuration are supported within the parameter conditions. Any other variables are not bound and replaced (they are considered part of literal string).
Evaluation order - Dependencies between parameters are detected and evaluation is performed in order that guarantees that all dependencies are evaluated prior their dependant (see Topological Sorting for details).
In case of cyclic dependency the evaluation proceeds only if current input values of parameters do not lead to nondeterministic result (and the cycle is indicated in warning log message). That means order of evaluation or number of reevaluations should not have impact on the result of evaluation. Otherwise an error is reported, indicating the cycle.
Example template.json
with cyclic dependency:
"symbols": {
"A": {
"type": "parameter",
"datatype": "bool",
"isEnabled": "B != false",
"defaultValue": false
},
"B": {
"type": "parameter",
"datatype": "bool",
"isEnabled": "A != true",
"defaultValue": true
}
}
The following input parameter values can (and will) be evaluated deterministically: --A false --B true
The following input parameter values cannot be evaluated deterministically (and will lead to error): --A true --B false
Applying user, host and default values - All user-provided, host-provided and default values are applied before the conditions are evaluated. After the evaluation default values are reapplied to parameters that were evaluated as optional and that do not have user-/host-provided values. After this an evaluation of presence of required values takes place.
Users of Edge API can supply evaluation results of parameters conditions when instantiating template via TemplateCreator
. More details are in Inside the Template Engine document.