Skip to content

Commit dc54a42

Browse files
committed
Using MSBuild task to run the XmlSerializer directly
1 parent b02b5a0 commit dc54a42

File tree

5 files changed

+217
-40
lines changed

5 files changed

+217
-40
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.Build.Framework;
5+
using Microsoft.Build.Utilities;
6+
using System;
7+
using System.Collections.Generic;
8+
9+
namespace Microsoft.XmlSerializer.Generator
10+
{
11+
/// <summary>
12+
/// MSBuild task to run the Microsoft.XmlSerializer.Generator directly,
13+
/// without spawning a new process.
14+
/// </summary>
15+
public class GenerateXmlSerializers : Task
16+
{
17+
/// <summary>
18+
/// Path to the target assembly (usually the output DLL).
19+
/// </summary>
20+
[Required]
21+
public string AssemblyPath { get; set; }
22+
23+
/// <summary>
24+
/// Optional path to a response file containing additional generator arguments.
25+
/// </summary>
26+
public string RspFilePath { get; set; }
27+
28+
/// <summary>
29+
/// Force regeneration of serializers even if they exist.
30+
/// </summary>
31+
public bool Force { get; set; }
32+
33+
/// <summary>
34+
/// Run in quiet mode with minimal output.
35+
/// </summary>
36+
public bool Quiet { get; set; }
37+
38+
/// <summary>
39+
/// If true, errors during serializer generation will be suppressed.
40+
/// </summary>
41+
public bool IgnoreErrors { get; set; }
42+
43+
/// <summary>
44+
/// Executes the XmlSerializer generator.
45+
/// </summary>
46+
public override bool Execute()
47+
{
48+
var args = new List<string>();
49+
50+
if (!string.IsNullOrEmpty(AssemblyPath))
51+
args.Add(AssemblyPath);
52+
53+
if (Force)
54+
args.Add("--force");
55+
56+
if (Quiet)
57+
args.Add("--quiet");
58+
59+
if (!string.IsNullOrEmpty(RspFilePath))
60+
args.Add(RspFilePath);
61+
62+
Sgen sgen = new Sgen();
63+
Sgen.InfoWriter = new InfoTextWriter(Log);
64+
Sgen.WarningWriter = new WarningTextWriter(Log);
65+
Sgen.ErrorWriter = new ErrorTextWriter(Log);
66+
67+
int exitCode = sgen.Run(args.ToArray());
68+
69+
if (exitCode != 0)
70+
{
71+
Log.LogError("XmlSerializer failed with exit code {0}.", exitCode);
72+
}
73+
74+
return IgnoreErrors || exitCode == 0;
75+
}
76+
}
77+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.IO;
6+
using System.Text;
7+
using Microsoft.Build.Framework;
8+
using Microsoft.Build.Utilities;
9+
10+
namespace Microsoft.XmlSerializer.Generator
11+
{
12+
internal sealed class InfoTextWriter : TextWriter
13+
{
14+
private readonly TaskLoggingHelper _log;
15+
16+
public InfoTextWriter(TaskLoggingHelper log)
17+
{
18+
_log = log ?? throw new ArgumentNullException(nameof(log));
19+
}
20+
21+
public override Encoding Encoding => Encoding.UTF8;
22+
23+
public override void Write(string? value)
24+
{
25+
if (!string.IsNullOrEmpty(value))
26+
{
27+
_log.LogMessage(MessageImportance.High, value);
28+
}
29+
}
30+
31+
public override void WriteLine(string? value)
32+
{
33+
if (!string.IsNullOrEmpty(value))
34+
{
35+
_log.LogMessage(MessageImportance.High, value);
36+
}
37+
}
38+
}
39+
40+
internal sealed class WarningTextWriter : TextWriter
41+
{
42+
private readonly TaskLoggingHelper _log;
43+
44+
public WarningTextWriter(TaskLoggingHelper log)
45+
{
46+
_log = log ?? throw new ArgumentNullException(nameof(log));
47+
}
48+
49+
public override Encoding Encoding => Encoding.UTF8;
50+
51+
public override void Write(string? value)
52+
{
53+
if (!string.IsNullOrEmpty(value))
54+
{
55+
_log.LogWarning(value);
56+
}
57+
}
58+
59+
public override void WriteLine(string? value)
60+
{
61+
if (!string.IsNullOrEmpty(value))
62+
{
63+
_log.LogWarning(value);
64+
}
65+
}
66+
}
67+
68+
internal sealed class ErrorTextWriter : TextWriter
69+
{
70+
private readonly TaskLoggingHelper _log;
71+
72+
public ErrorTextWriter(TaskLoggingHelper log)
73+
{
74+
_log = log ?? throw new ArgumentNullException(nameof(log));
75+
}
76+
77+
public override Encoding Encoding => Encoding.UTF8;
78+
79+
public override void Write(string? value)
80+
{
81+
if (!string.IsNullOrEmpty(value))
82+
{
83+
_log.LogError(value);
84+
}
85+
}
86+
87+
public override void WriteLine(string? value)
88+
{
89+
if (!string.IsNullOrEmpty(value))
90+
{
91+
_log.LogError(value);
92+
}
93+
}
94+
}
95+
}

src/libraries/Microsoft.XmlSerializer.Generator/src/Microsoft.XmlSerializer.Generator.csproj

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
@@ -22,22 +22,24 @@
2222
</PropertyGroup>
2323

2424
<ItemGroup>
25+
<Compile Include="GenerateXmlSerializers.cs" />
26+
<Compile Include="LogWriter.cs" />
2527
<Compile Include="Sgen.cs" />
2628
</ItemGroup>
2729

2830
<ItemGroup>
2931
<!-- Set an empty PackagePath to place this file in the root of the package -->
30-
<None Include="build\prefercliruntime"
31-
PackagePath=""
32-
Pack="true" />
33-
<None Include="build\Microsoft.XmlSerializer.Generator.targets"
34-
PackagePath="build"
35-
Pack="true" />
36-
<None Include="build\dotnet-Microsoft.XmlSerializer.Generator.runtimeconfig.json"
37-
PackagePath="lib\netstandard2.0"
38-
Pack="true" />
32+
<None Include="build\prefercliruntime" PackagePath="" Pack="true" />
33+
<None Include="build\Microsoft.XmlSerializer.Generator.targets" PackagePath="build" Pack="true" />
34+
<None Include="build\dotnet-Microsoft.XmlSerializer.Generator.runtimeconfig.json" PackagePath="lib\netstandard2.0" Pack="true" />
3935
</ItemGroup>
4036

37+
<ItemGroup>
38+
<PackageDownloadAndReference Include="Microsoft.Build.Framework" Version="$(MicrosoftBuildFrameworkVersion)" Folder="ref/netstandard2.0" />
39+
<PackageDownloadAndReference Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildUtilitiesCoreVersion)" Folder="ref/netstandard2.0" />
40+
</ItemGroup>
41+
42+
<Import Project="$(RepositoryEngineeringDir)PackageDownloadAndReference.targets" />
4143
<Import Project="GenerateThisAssemblyCs.targets" />
4244
<Import Project="GenerateNupkgProps.targets" />
4345

src/libraries/Microsoft.XmlSerializer.Generator/src/Sgen.cs

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ public static int Main(string[] args)
2525

2626
private static string s_references = string.Empty;
2727
private static readonly Dictionary<string, string> s_referencedic = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
28+
public static TextWriter InfoWriter { get; internal set; } = Console.Out;
29+
public static TextWriter WarningWriter { get; internal set; } = Console.Out;
30+
public static TextWriter ErrorWriter { get; internal set; } = Console.Error;
2831

29-
private int Run(string[] args)
32+
internal int Run(string[] args)
3033
{
3134
string assembly = null;
3235
var types = new List<string>();
@@ -177,15 +180,15 @@ private int Run(string[] args)
177180
{
178181
foreach (string err in errs)
179182
{
180-
Console.Error.WriteLine(FormatMessage(parsableErrors, true, SR.Format(SR.Warning, err)));
183+
ErrorWriter.WriteLine(FormatMessage(parsableErrors, true, SR.Format(SR.Warning, err)));
181184
}
182185
}
183186

184187
if (args.Length == 0 || assembly == null)
185188
{
186189
if (assembly == null)
187190
{
188-
Console.Error.WriteLine(FormatMessage(parsableErrors, false, SR.Format(SR.ErrMissingRequiredArgument, SR.Format(SR.ErrAssembly, "assembly"))));
191+
ErrorWriter.WriteLine(FormatMessage(parsableErrors, false, SR.Format(SR.ErrMissingRequiredArgument, SR.Format(SR.ErrAssembly, "assembly"))));
189192
}
190193

191194
WriteHelp();
@@ -194,8 +197,8 @@ private int Run(string[] args)
194197

195198
if (disableRun)
196199
{
197-
Console.WriteLine("This tool is not intended to be used directly.");
198-
Console.WriteLine("Please refer to https://go.microsoft.com/fwlink/?linkid=858594 on how to use it.");
200+
InfoWriter.WriteLine("This tool is not intended to be used directly.");
201+
InfoWriter.WriteLine("Please refer to https://go.microsoft.com/fwlink/?linkid=858594 on how to use it.");
199202
return 0;
200203
}
201204

@@ -254,7 +257,7 @@ private static void GenerateFile(List<string> typeNames, string defaultNamespace
254257
Type type = assembly.GetType(typeName);
255258
if (type == null)
256259
{
257-
Console.Error.WriteLine(FormatMessage(parsableerrors, false, SR.Format(SR.ErrorDetails, SR.Format(SR.ErrLoadType, typeName, assemblyName))));
260+
ErrorWriter.WriteLine(FormatMessage(parsableerrors, false, SR.Format(SR.ErrorDetails, SR.Format(SR.ErrLoadType, typeName, assemblyName))));
258261
}
259262

260263
types[typeIndex++] = type;
@@ -275,7 +278,7 @@ private static void GenerateFile(List<string> typeNames, string defaultNamespace
275278
{
276279
if (verbose)
277280
{
278-
Console.WriteLine(SR.Format(SR.ImportInfo, type.Name, i + 1, types.Length));
281+
InfoWriter.WriteLine(SR.Format(SR.ImportInfo, type.Name, i + 1, types.Length));
279282
}
280283

281284
bool isObsolete = false;
@@ -300,7 +303,7 @@ private static void GenerateFile(List<string> typeNames, string defaultNamespace
300303
{
301304
if (verbose)
302305
{
303-
Console.Out.WriteLine(FormatMessage(parsableerrors, true, SR.Format(SR.InfoIgnoreType, type.FullName)));
306+
InfoWriter.WriteLine(FormatMessage(parsableerrors, true, SR.Format(SR.InfoIgnoreType, type.FullName)));
304307
WriteWarning(e, parsableerrors);
305308
}
306309

@@ -372,7 +375,7 @@ private static void GenerateFile(List<string> typeNames, string defaultNamespace
372375

373376
if (method == null)
374377
{
375-
Console.Error.WriteLine(FormatMessage(parsableerrors: false, warning: false, message: SR.GenerateSerializerNotFound));
378+
ErrorWriter.WriteLine(FormatMessage(parsableerrors: false, warning: false, message: SR.GenerateSerializerNotFound));
376379
}
377380
else
378381
{
@@ -404,22 +407,21 @@ private static void GenerateFile(List<string> typeNames, string defaultNamespace
404407
{
405408
if (!silent)
406409
{
407-
Console.Out.WriteLine(SR.Format(SR.InfoFileName, codePath));
408-
Console.Out.WriteLine(SR.Format(SR.InfoGeneratedFile, assembly.Location, codePath));
410+
InfoWriter.WriteLine(SR.Format(SR.InfoFileName, codePath));
411+
InfoWriter.WriteLine(SR.Format(SR.InfoGeneratedFile, assembly.Location, codePath));
409412
}
410413
}
411414
else
412415
{
413-
Console.Out.WriteLine(FormatMessage(parsableerrors, false, SR.Format(SR.ErrGenerationFailed, assembly.Location)));
416+
InfoWriter.WriteLine(FormatMessage(parsableerrors, false, SR.Format(SR.ErrGenerationFailed, assembly.Location)));
414417
}
415418
}
416419
else
417420
{
418-
Console.Out.WriteLine(FormatMessage(parsableerrors, true, SR.Format(SR.InfoNoSerializableTypes, assembly.Location)));
421+
InfoWriter.WriteLine(FormatMessage(parsableerrors, true, SR.Format(SR.InfoNoSerializableTypes, assembly.Location)));
419422
}
420423
}
421424

422-
423425
private static bool ArgumentMatch(string arg, string formal)
424426
{
425427
// Full name format, eg: --assembly
@@ -459,7 +461,7 @@ private static void ImportType(Type type, string defaultNamespace, List<XmlMappi
459461

460462
if (verbose)
461463
{
462-
Console.Out.WriteLine(FormatMessage(parsableerrors, true, SR.Format(SR.InfoIgnoreType, type.FullName)));
464+
InfoWriter.WriteLine(FormatMessage(parsableerrors, true, SR.Format(SR.InfoIgnoreType, type.FullName)));
463465
WriteWarning(e, parsableerrors);
464466
}
465467

@@ -489,22 +491,22 @@ private static Assembly LoadAssembly(string assemblyName)
489491
private static void WriteHeader()
490492
{
491493
// do not localize Copyright header
492-
Console.WriteLine($".NET Xml Serialization Generation Utility, Version {ThisAssembly.InformationalVersion}]");
494+
InfoWriter.WriteLine($".NET Xml Serialization Generation Utility, Version {ThisAssembly.InformationalVersion}]");
493495
}
494496

495497
private void WriteHelp()
496498
{
497-
Console.Out.WriteLine(SR.HelpDescription);
498-
Console.Out.WriteLine(SR.Format(SR.HelpUsage, this.GetType().Assembly.GetName().Name.Substring("dotnet-".Length)));
499-
Console.Out.WriteLine(SR.HelpDevOptions);
500-
Console.Out.WriteLine(SR.Format(SR.HelpAssembly, "-a", "--assembly"));
501-
Console.Out.WriteLine(SR.Format(SR.HelpType, "--type"));
502-
Console.Out.WriteLine(SR.Format(SR.HelpProxy, "--proxytypes"));
503-
Console.Out.WriteLine(SR.Format(SR.HelpForce, "--force"));
504-
Console.Out.WriteLine(SR.Format(SR.HelpOut, "-o", "--out"));
505-
506-
Console.Out.WriteLine(SR.HelpMiscOptions);
507-
Console.Out.WriteLine(SR.Format(SR.HelpHelp, "-h", "--help"));
499+
InfoWriter.WriteLine(SR.HelpDescription);
500+
InfoWriter.WriteLine(SR.Format(SR.HelpUsage, this.GetType().Assembly.GetName().Name.Substring("dotnet-".Length)));
501+
InfoWriter.WriteLine(SR.HelpDevOptions);
502+
InfoWriter.WriteLine(SR.Format(SR.HelpAssembly, "-a", "--assembly"));
503+
InfoWriter.WriteLine(SR.Format(SR.HelpType, "--type"));
504+
InfoWriter.WriteLine(SR.Format(SR.HelpProxy, "--proxytypes"));
505+
InfoWriter.WriteLine(SR.Format(SR.HelpForce, "--force"));
506+
InfoWriter.WriteLine(SR.Format(SR.HelpOut, "-o", "--out"));
507+
508+
InfoWriter.WriteLine(SR.HelpMiscOptions);
509+
InfoWriter.WriteLine(SR.Format(SR.HelpHelp, "-h", "--help"));
508510
}
509511

510512
private static string FormatMessage(bool parsableerrors, bool warning, string message)
@@ -524,7 +526,7 @@ private static string FormatMessage(bool parsableerrors, bool warning, string co
524526

525527
private static void WriteError(Exception e, bool parsableerrors)
526528
{
527-
Console.Error.WriteLine(FormatMessage(parsableerrors, false, e.Message));
529+
ErrorWriter.WriteLine(FormatMessage(parsableerrors, false, e.Message));
528530
if (e.InnerException != null)
529531
{
530532
WriteError(e.InnerException, parsableerrors);
@@ -533,7 +535,7 @@ private static void WriteError(Exception e, bool parsableerrors)
533535

534536
private static void WriteWarning(Exception e, bool parsableerrors)
535537
{
536-
Console.Out.WriteLine(FormatMessage(parsableerrors, true, e.Message));
538+
WarningWriter.WriteLine(FormatMessage(parsableerrors, true, e.Message));
537539
if (e.InnerException != null)
538540
{
539541
WriteWarning(e.InnerException, parsableerrors);

src/libraries/Microsoft.XmlSerializer.Generator/src/build/Microsoft.XmlSerializer.Generator.targets

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
<Delete Condition="Exists('$(_SerializerPdbIntermediateFolder)') == 'true'" Files="$(_SerializerPdbIntermediateFolder)" ContinueOnError="true"/>
4949
<Delete Condition="Exists('$(_SerializerCsIntermediateFolder)') == 'true'" Files="$(_SerializerCsIntermediateFolder)" ContinueOnError="true"/>
5050
<Message Text="Running Serialization Tool" Importance="normal" />
51-
<Exec Command="dotnet Microsoft.XmlSerializer.Generator &quot;$(IntermediateOutputPath)$(AssemblyName)$(TargetExt)&quot; --force --quiet $(_SgenRspFilePath)" ContinueOnError="true"/>
51+
<UsingTask TaskName="GenerateXmlSerializers" AssemblyFile="$(MSBuildThisFileDirectory)/../lib/netstandard2.0/dotnet-Microsoft.XmlSerializer.Generator.dll" />
52+
<GenerateXmlSerializers AssemblyPath="$(IntermediateOutputPath)$(AssemblyName)$(TargetExt)" RspFilePath="$(_SgenRspFilePath)" Force="true" Quiet="true" IgnoreErrors="true" />
5253
<Warning Condition="Exists('$(_SerializerCsIntermediateFolder)') != 'true'" Text="$(_SGenWarningText)" />
5354
<Csc Condition="Exists('$(_SerializerCsIntermediateFolder)') and !Exists('$(_CscRspFilePath)')" ContinueOnError="true" OutputAssembly="$(_SerializerDllIntermediateFolder)" References="@(ReferencePath);@(IntermediateAssembly)" Optimize="$(Optimize)" EmitDebugInformation="$(DebugSymbols)" Sources="$(_SerializerCsIntermediateFolder);$(_SerializerCsAssemblyInfoIntermediateFolder)" TargetType="Library" ToolExe="$(CscToolExe)" ToolPath="$(CscToolPath)" DisabledWarnings="$(_SerializationAssemblyDisabledWarnings)"/>
5455
<Csc Condition="Exists('$(_SerializerCsIntermediateFolder)') and Exists('$(_CscRspFilePath)')" ContinueOnError="true" OutputAssembly="$(_SerializerDllIntermediateFolder)" References="@(ReferencePath);@(IntermediateAssembly)" Optimize="$(Optimize)" EmitDebugInformation="$(DebugSymbols)" Sources="$(_SerializerCsIntermediateFolder);$(_SerializerCsAssemblyInfoIntermediateFolder)" TargetType="Library" ResponseFiles="$(_CscRspFilePath)" ToolExe="$(CscToolExe)" ToolPath="$(CscToolPath)" DisabledWarnings="$(_SerializationAssemblyDisabledWarnings)"/>

0 commit comments

Comments
 (0)