Awesome
E-MSBuild
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:
$(name = $( … ))
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
))'
)
)]
= 10000
Min / 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 runtime
Global properties
To use local scoped variables as part of other msbuild properties and so on.
$(+name = …)
$(+MyProperty = "val1")
To unset:
$(-name =)
$(-name = …)
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.