From 9ad09362c760838a00da7a11c23f49c5713ee0ff Mon Sep 17 00:00:00 2001 From: Jean-Jacques Lafay Date: Wed, 13 Oct 2021 15:39:01 +0200 Subject: [PATCH] Fix files kept in use in XslTransformation Following #6863, the created XmlReader is no longer responsible for its underlying stream. This can cause the build process to hold on to the processed file, preventing its removal. This can especially be a problem when the transformation is in fact aimed at the input file itself, where we want to create the transformed file, then move it to the original. --- .../XslTransformation_Tests.cs | 36 ++++++++++++++++++- src/Tasks/XslTransformation.cs | 9 +++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/Tasks.UnitTests/XslTransformation_Tests.cs b/src/Tasks.UnitTests/XslTransformation_Tests.cs index 5fc54d6dfcf..e0d8e222449 100644 --- a/src/Tasks.UnitTests/XslTransformation_Tests.cs +++ b/src/Tasks.UnitTests/XslTransformation_Tests.cs @@ -13,6 +13,7 @@ using System.Text.RegularExpressions; using System.Xml.Xsl; using System.Xml; +using Shouldly; using Xunit; namespace Microsoft.Build.UnitTests @@ -386,7 +387,7 @@ public void OutputTest() /// Setting correct "Parameter" parameters for Xsl. /// [Fact] - public void XsltParamatersCorrect() + public void XsltParametersCorrect() { string dir; TaskItem[] xmlPaths; @@ -780,6 +781,39 @@ public void OutputFileCannotBeWritten() CleanUp(dir); } + /// + /// The files are not kept locked by the task + /// + [Fact] + public void InputFilesDontLock() + { + string dir; + TaskItem[] xmlPaths; + TaskItem xslPath; + TaskItem[] outputPaths; + MockEngine engine; + Prepare(out dir, out xmlPaths, out xslPath, out _, out outputPaths, out _, out _, out engine); + + // Test with files + { + XslTransformation t = new XslTransformation(); + t.BuildEngine = engine; + t.XmlInputPaths = xmlPaths; + t.XslInputPath = xslPath; + t.OutputPaths = outputPaths; + + Assert.True(t.Execute()); + string xmlInputPath = xmlPaths[0].ItemSpec; + File.Delete(xmlInputPath); // this should succeed (file not locked by task) + File.Exists(xmlInputPath).ShouldBeFalse(); + string xslInputPath = xslPath.ItemSpec; + File.Delete(xslInputPath); // this should succeed (file not locked by task) + File.Exists(xslInputPath).ShouldBeFalse(); + } + + CleanUp(dir); + } + /// /// XslDocument that throws runtime exception. /// diff --git a/src/Tasks/XslTransformation.cs b/src/Tasks/XslTransformation.cs index 3b8810be561..0f47a2edfe2 100644 --- a/src/Tasks/XslTransformation.cs +++ b/src/Tasks/XslTransformation.cs @@ -334,9 +334,9 @@ public XmlReader CreateReader(int itemPos) { if (XmlMode == XmlModes.XmlFile) { - return XmlReader.Create(new StreamReader(_data[itemPos]), null, _data[itemPos]); + return XmlReader.Create(new StreamReader(_data[itemPos]), new XmlReaderSettings { CloseInput = true }, _data[itemPos]); } - else // xmlModes.Xml + else // xmlModes.Xml { return XmlReader.Create(new StringReader(_data[itemPos])); } @@ -459,7 +459,10 @@ public XslCompiledTransform LoadXslt(bool useTrustedSettings) _log.LogMessageFromResources(MessageImportance.Low, "XslTransform.UseTrustedSettings", _data); } - xslct.Load(new XPathDocument(XmlReader.Create(new StreamReader(_data), null, _data)), settings, new XmlUrlResolver()); + using (XmlReader reader = XmlReader.Create(new StreamReader(_data), new XmlReaderSettings { CloseInput = true }, _data)) + { + xslct.Load(new XPathDocument(reader), settings, new XmlUrlResolver()); + } break; case XslModes.XsltCompiledDll: #if FEATURE_COMPILED_XSL