66using System . Collections . Generic ;
77using System . IO ;
88using System . Reflection ;
9+ using System . Text ;
910using Microsoft . Build . Framework ;
1011using Microsoft . Build . Utilities ;
1112
@@ -31,11 +32,8 @@ public sealed class LottieGen : Microsoft.Build.Utilities.ToolTask
3132
3233 protected override string ToolName => "LottieGen.exe" ;
3334
34- /// <summary>
35- /// Optional path to LottieGen.exe. If not specified, LottieGen.exe
36- /// is expected to be in the same directory as the task's DLL.
37- /// </summary>
38- public string ? LottieGenExePath { get ; set ; }
35+ // WARNING: Nullable value types are not supported by MSBuild and will result in an error
36+ // when passed into the task through an MSBuild project file.
3937
4038 /// <summary>
4139 /// The Lottie file to process.
@@ -82,12 +80,12 @@ public sealed class LottieGen : Microsoft.Build.Utilities.ToolTask
8280
8381 /// <summary>
8482 /// The lowest UAP version on which the result must run.Defaults
85- /// to 7. Must be 7 or higher.Code will be generated that will
83+ /// to 7. Must be 7 or higher. Code will be generated that will
8684 /// run down to this version.If less than TargetUapVersion,
8785 /// extra code will be generated if necessary to support the
8886 /// lower versions.
8987 /// </summary>
90- public uint ? MinimumUapVersion { get ; set ; }
88+ public uint MinimumUapVersion { get ; set ; }
9189
9290 /// <summary>
9391 /// Specifies the namespace for the generated code. Defaults to
@@ -134,7 +132,7 @@ public sealed class LottieGen : Microsoft.Build.Utilities.ToolTask
134132 /// minimum SDK version required to compile the generated code.
135133 /// If not specified, defaults to the latest UAP version.
136134 /// </summary>
137- public uint ? TargetUapVersion { get ; set ; }
135+ public uint TargetUapVersion { get ; set ; }
138136
139137 /// <summary>
140138 /// Prevents any information from being included that could change
@@ -147,7 +145,7 @@ public sealed class LottieGen : Microsoft.Build.Utilities.ToolTask
147145 /// <summary>
148146 /// Generates code for a particular WinUI version. Defaults to 2.4.
149147 /// </summary>
150- public Version ? WinUIVersion { get ; set ; }
148+ public string ? WinUIVersion { get ; set ; }
151149
152150 public override bool Execute ( )
153151 {
@@ -223,13 +221,13 @@ protected override string GenerateCommandLineCommands()
223221 AddOptionalBool ( nameof ( DisableTranslationOptimizer ) , DisableTranslationOptimizer ) ;
224222 AddOptionalBool ( nameof ( GenerateColorBindings ) , GenerateColorBindings ) ;
225223 AddOptionalBool ( nameof ( GenerateDependencyObject ) , GenerateDependencyObject ) ;
226- AddOptional ( nameof ( MinimumUapVersion ) , MinimumUapVersion ) ;
224+ AddOptional ( nameof ( MinimumUapVersion ) , MinimumUapVersion > 0 ? MinimumUapVersion : null ) ;
227225 AddOptional ( nameof ( Namespace ) , Namespace ) ;
228226 AddArg ( nameof ( OutputFolder ) , OutputFolder ! ) ;
229227 AddOptionalBool ( nameof ( Public ) , Public ) ;
230228 AddOptional ( nameof ( RootNamespace ) , RootNamespace ) ;
231229 AddOptionalBool ( nameof ( StrictMode ) , StrictMode ) ;
232- AddOptional ( nameof ( TargetUapVersion ) , TargetUapVersion ) ;
230+ AddOptional ( nameof ( TargetUapVersion ) , TargetUapVersion > 0 ? TargetUapVersion : null ) ;
233231 AddOptional ( nameof ( WinUIVersion ) , WinUIVersion ) ;
234232
235233 var result = string . Join ( " " , args ) ;
@@ -253,15 +251,73 @@ void AddOptionalBool(string parameterName, bool value)
253251 }
254252
255253 void AddArg ( string parameterName , string value )
256- => args . Add ( $ "-{ parameterName } { value } ") ;
254+ {
255+ args . Add ( $ "-{ parameterName } ") ;
256+ args . Add ( EscapeCmdLineArg ( value ) ) ;
257+ }
258+ }
259+
260+ /// <summary>
261+ /// Escapes a command-line argument to ensure that it can be passed through to another application without mangling.
262+ /// </summary>
263+ /// <remarks>
264+ /// The string is wrapped in quotes to ensure that any spaces are considered part of the value. Backslashes and quotes
265+ /// are escaped to ensure they are not interpreted as metacharacters.
266+ /// </remarks>
267+ /// <param name="arg">The argument to escape.</param>
268+ /// <returns>The escaped argument.</returns>
269+ string EscapeCmdLineArg ( string arg )
270+ {
271+ // From MSDN "Everyone quotes command line arguments the wrong way"
272+ StringBuilder result = new StringBuilder ( ) ;
273+
274+ // Wrap the arg in quotes
275+ result . Append ( '\" ' ) ;
276+
277+ for ( int i = 0 ; ; ++ i )
278+ {
279+ int numBackslashes = 0 ;
280+ while ( i < arg . Length && arg [ i ] == '\\ ' )
281+ {
282+ ++ i ;
283+ ++ numBackslashes ;
284+ }
285+
286+ if ( i >= arg . Length )
287+ {
288+ // End of string.
289+ // Doubling any trailing backslashes gives us an even number followed by our final double-quote.
290+ // Cmdline will replace each pair with a single backslash (back to where we started), but treat
291+ // the quote as a metacharacter and remove it.
292+ result . Append ( '\\ ' , numBackslashes * 2 ) ;
293+ break ;
294+ }
295+ else if ( arg [ i ] == '"' )
296+ {
297+ // Found a double-quote in the string.
298+ // Doubling any preceding backslashes and adding one gives us an odd number followed by the double-quote.
299+ // Cmdline will replace each pair with a single backslash and convert the ending \" to a literal quote.
300+ result . Append ( '\\ ' , ( numBackslashes * 2 ) + 1 ) ;
301+ result . Append ( arg [ i ] ) ;
302+ }
303+ else
304+ {
305+ // In all other cases, just pass through the characters as-is.
306+ result . Append ( '\\ ' , numBackslashes ) ;
307+ result . Append ( arg [ i ] ) ;
308+ }
309+ }
310+
311+ result . Append ( '\" ' ) ;
312+ return result . ToString ( ) ;
257313 }
258314
259315 // Provides the default path to the tool. Ignored if
260316 // the <ToolPath/> property is set.
261317 // By default we expect the tool to be in the same directory
262318 // as the assembly that this class is in.
263319 protected override string GenerateFullPathToTool ( )
264- => Path . Combine ( Assembly . GetExecutingAssembly ( ) . Location , ToolName ) ;
320+ => Path . Combine ( Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) , ToolName ) ;
265321
266322 protected override bool ValidateParameters ( )
267323 {
@@ -293,7 +349,17 @@ protected override bool ValidateParameters()
293349 break ;
294350 }
295351
296- return hasErrors ;
352+ if ( WinUIVersion is not null )
353+ {
354+ Version version ;
355+ if ( ! Version . TryParse ( WinUIVersion , out version ) )
356+ {
357+ Log . LogError ( $ "Invalid version string \" { WinUIVersion ! } \" .") ;
358+ hasErrors = true ;
359+ }
360+ }
361+
362+ return ! hasErrors ;
297363 }
298364
299365 protected override void LogEventsFromTextOutput ( string singleLine , MessageImportance messageImportance )
0 commit comments