EvMSBuild
Advanced Evaluator of MSBuild scripts aka Advanced MSBuild with user-variables support through Varhead and more.
It is actively used in projects such as vsSolutionBuildEvent, SobaScript, vsCommandEvent,
License
Licensed under the MIT License
Copyright (c) 2013-2020 Denis Kuzmin < x-3F@outlook.com > GitHub/3F
[ ☕ Donate ]
E-MSBuild contributors https://github.com/3F/E-MSBuild/graphs/contributors
Syntax and features
| evaluation region (container) | optional scope | escaping |
|---|---|---|
$( … ) |
$( …:project ) |
$$( … ) |
User Variables:
Strings:
"…"- Allows any evaluation inside string. Escaping is a\"'…'- All containers $() should not be evaluated for this type string. Escaping is a\'
'' and "" used 'as is' for compatibility with MSBuild
$(name = " - Platform is a $(Platform) ")
$(sPoint = $([System.DateTime]::Parse("2019/08/01").ToBinary()))
$(pdir = $(ProjectDir.Replace('\', '/'):project))
Increment & Decrement operators for numbers and strings
| Numbers | Strings |
|---|---|
$(i += 1) |
$(name += "str") |
$(i -= 1) |
+= and -= for numbers also initializes variable to 0 if it's not defined before.
$(n = 0) $(n += 3.14) $(n += $(n))
$(desc = "Hello ") $(desc += "world !")
Math operations
$(numYmod = $([MSBuild]::Modulo($(numY), 12))) $([MSBuild]::BitwiseAnd($(mask), $(v))) != 0
Difference between quotes. Exponential notation problem
All data inside double quotes ".." will be evaluated manually (standard moving: upward from deepest).
All data inside single quotes '..' is not processed and will be entirely sent into engine for a single final evaluation.
What does this mean, for example:
$([MSBuild]::Multiply("$([System.Math]::Log(2))", 16)) -> 1,10903548889591E+16 \ \_(1) 0,693147180559945_/ \_______________(2)__________________________________/ $([MSBuild]::Multiply('$([System.Math]::Log(2))', 16)) -> 11,0903548889591 \______________________(1)___________________________/ $([System.Math]::Exp(1.10903548889591E+16)) = ∞ $([System.Math]::Exp(11.0903548889591)) = 65535,9999999983
Other samples:
| Expression | Evaluated value |
|---|---|
| $([System.Math]::Log(2)) | 0,693147180559945 |
| $([MSBuild]::Multiply('$([System.Math]::Log(2))', 16)) | 11,0903548889591 |
| $([System.Math]::Exp('$([MSBuild]::Multiply($([System.Math]::Log(2)), 16))')) | 65536 |
$([System.Math]::Exp($([MSBuild]::Multiply('$([System.Math]::Log(10))', 4)))) = 9999.99999999997 $([System.Math]::Exp($([MSBuild]::Multiply($([System.Math]::Log(10)), 4)))) = 10000.0000000002 $([System.Math]::Exp('$([MSBuild]::Multiply($([System.Math]::Log(10)), 4))')) = 10000
#[$(
[System.Math]::Exp('$(
[MSBuild]::Multiply(
$([System.Math]::Log(10)),
4
))'
)
)]
= 10000Min / Max
0 - n & n - 18:
$([System.Math]::Max(0, $(n))) $([System.Math]::Min($(n), 18))
n - m (min(max($(n), $(val)), $(m))):
$([System.Math]::Min( $([System.Math]::Max( $(n), $(val) )), $(m) ))
Bit mask
Set
$(mask = 0) $(mask = $([MSBuild]::BitwiseOr($(mask), 1))) $(mask = $([MSBuild]::BitwiseOr($(mask), 4))) $(mask = $([MSBuild]::BitwiseOr($(mask), 8))) $(maskString = $([System.Convert]::ToString('$([System.Convert]::ToInt32($(mask)))', 2)))
Result: 1101
Check
#[$(v = 2)] #[( $([MSBuild]::BitwiseAnd($(mask), $(v))) != 0 ){ "$(v)" is defined in the mask($(maskString)) } else{ "$(v)" is not defined in the mask($(maskString)) }]
"2" is not defined in the mask(1101)
The numbers of modulo
0 - 99:
$([MSBuild]::Modulo($(num), 100)) 0, 1, 2, 3, 4 ... 98, 99, 0, 1, 2 ...
n - m (e.g. 10 - 99):
Same as above, only use limit like:
= (val % (max - min)) + min
#[$(
[MSBuild]::Add(
$(minrev),
$([MSBuild]::Modulo(
$(num),
$([MSBuild]::Subtract(
$(maxrev),
$(minrev)
))
))
)
)]
10, 11, 12, ... 98, 99, 10, 11, 12 ...Raise number to the specified power
$([System.Math]::Pow(10, 4)) = 10000
or via exp:
$([System.Math]::Exp('$([MSBuild]::Multiply($([System.Math]::Log(10)), 4))')) = 10000
Nested levels. Recursive evaluation
…
Useful for any dynamic references to your data or additional evaluation. For example:
$(ProjectDir:$(ProjectName)) - ProjectDir value scoped by project name at runtimeGlobal properties
To use local scoped variables as part of other msbuild properties and so on.
To unset:
Operations with strings
$(SolutionPath.Replace('\', '/')) -> to D:/App/ConsoleApp1.sln $(SolutionPath.Replace('\', '\\')) -> to D:\\App\\ConsoleApp1.sln
$(desc = "Hello ") $(desc = $([System.String]::Concat($(desc), "world !")) )
Escape-Sequence
Implemented a strictly limited set:
- hexadecimal-escape-sequence:
\x 0-0xF [0-0xF [0-0xF [0-0xF]]] - unicode-escape-sequence:
\u 0-0xF 0-0xF 0-0xF 0-0xF\U 0-0xF 0-0xF 0-0xF 0-0xF 0-0xF 0-0xF 0-0xF 0-0xF
- basic:
\r \n \t \v \a \b \0 \f
$([System.String]::Concat("\r\n"))$(ver = "1.2.3") $([System.String]::Format("\t version is a {0}", $(ver)))
Newline characters and other problematic symbols
$(cs.Replace("\r\n", ""))
or other CR/LF combination (Newline - representations)
$(cs.Replace("\r", "").Replace("\n", ""))
Date & Time
Format & Culture:
Sortable format: ~ yyyy/MM/dd, eg.: 2016/08/21 for InvariantCulture
For specific culture use for example:
$([System.DateTime]::Parse("21.08.2016", '$([System.Globalization.CultureInfo]::GetCultureInfo("ru-RU"))')) $([System.DateTime]::Parse("08/21/2016", '$([System.Globalization.CultureInfo]::GetCultureInfo("en-US"))'))
Number of ticks:
$([System.DateTime]::Parse("2015/02/17").ToBinary()) $([System.DateTime]::Parse("2015/02/17 07:21").ToBinary())
$([System.DateTime]::UtcNow.Ticks)
Total Minutes or Hours from Ticks:
$([System.TimeSpan]::FromTicks(635618792404338780).TotalHours) $([System.TimeSpan]::FromTicks(635618792404338780).TotalMinutes)
Delta between the time:
$([System.TimeSpan]::FromTicks($([MSBuild]::Subtract(635618821282084745, 635618792404338780))).TotalMinutes.ToString("0")) $([System.TimeSpan]::FromTicks($([MSBuild]::Subtract(635618821282084745, 635618792404338780))).TotalSeconds.ToString("0"))
Custom Date and Time Format Strings:
$([System.DateTime]::UtcNow.ToString("yyyy.MM.dd_HH;mm;ss.ffff"))Result: 2016.02.07_10;56;54.8265
Standard MSBuild Property Functions support
$([System.Guid]::NewGuid()) $(SolutionDir.Substring(0,3)) $([System.DateTime]::Now.ToString("yyyy.MM.dd HH:mm:ss"))
$(registry:Hive\MyKey\MySubKey@ValueName) - gets value for ValueName from subkey. $(registry:Hive\MyKey\MySubKey) - gets the default subkey value.
