Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

force set Condition="false" on Microsoft.WebApplication.targets #8946

Merged
merged 7 commits into from
Feb 6, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,175 @@ public async Task PackagesConfigUpdateCanHappenEvenWithMismatchedVersionNumbers(
""");
}

[Fact]
public async Task PackagesConfigUpdateIsNotThwartedBy_VSToolsPath_PropertyBeingSetInUserCode()
{
await TestUpdateForProject("Newtonsoft.Json", "7.0.1", "13.0.1",
projectContents: """
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>68ed3303-52a0-47b8-a687-3abbb07530da</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TestProject</RootNamespace>
<AssemblyName>TestProject</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Core" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Web.Services" />
<Reference Include="System.EnterpriseServices" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<PropertyGroup>
<!-- some project files set this property which makes the Microsoft.WebApplication.targets import a few lines down always fail -->
<VSToolsPath Condition="'$(VSToolsPath)' == ''">C:\some\path\that\does\not\exist</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
""",
packagesConfigContents: """
<packages>
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
</packages>
""",
expectedProjectContents: """
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>68ed3303-52a0-47b8-a687-3abbb07530da</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TestProject</RootNamespace>
<AssemblyName>TestProject</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
<HintPath>packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Core" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Web.Services" />
<Reference Include="System.EnterpriseServices" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<PropertyGroup>
<!-- some project files set this property which makes the Microsoft.WebApplication.targets import a few lines down always fail -->
<VSToolsPath Condition="'$(VSToolsPath)' == ''">C:\some\path\that\does\not\exist</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
""",
expectedPackagesConfigContents: """
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
</packages>
""");
}

protected static async Task TestUpdateForProject(
string dependencyName,
string oldVersion,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Xml.Linq;

using Microsoft.Language.Xml;
using NuGetUpdater.Core.Updater;

namespace NuGetUpdater.Core;

Expand Down Expand Up @@ -56,7 +57,10 @@ public static async Task UpdateDependencyAsync(string repoRootPath, string proje
args.Add(msbuildDirectory); // e.g., /usr/share/dotnet/sdk/7.0.203
}

RunNuget(args, packagesDirectory, logger);
using (new WebApplicationTargetsConditionPatcher(projectPath))
{
RunNuget(args, packagesDirectory, logger);
}

projectBuildFile = ProjectBuildFile.Open(repoRootPath, projectPath);
projectBuildFile.NormalizeDirectorySeparatorsInProject();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.IO;
using System.Linq;

using Microsoft.Language.Xml;

namespace NuGetUpdater.Core.Updater
{
internal class WebApplicationTargetsConditionPatcher : IDisposable
{
private string? _capturedCondition;
private readonly XmlFilePreAndPostProcessor _processor;

public WebApplicationTargetsConditionPatcher(string projectFilePath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this shape, I think it makes it relatively easy to create a couple of similar Patchers and group them together into an aggregator.

{
_processor = new XmlFilePreAndPostProcessor(
getContent: () => File.ReadAllText(projectFilePath),
setContent: s => File.WriteAllText(projectFilePath, s),
nodeFinder: doc => doc.Descendants()
ryanbrandenburg marked this conversation as resolved.
Show resolved Hide resolved
.FirstOrDefault(e => e.Name == "Import" && e.GetAttributeValue("Project") == @"$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets")
as XmlNodeSyntax,
preProcessor: n =>
{
var element = (IXmlElementSyntax)n;
_capturedCondition = element.GetAttributeValue("Condition");
ryanbrandenburg marked this conversation as resolved.
Show resolved Hide resolved
return (XmlNodeSyntax)element.RemoveAttributeByName("Condition").WithAttribute("Condition", "false");
},
postProcessor: n =>
{
var element = (IXmlElementSyntax)n;
var newElement = element.RemoveAttributeByName("Condition");
if (_capturedCondition is not null)
{
newElement = newElement.WithAttribute("Condition", _capturedCondition);
}

return (XmlNodeSyntax)newElement;
}
);
}

public void Dispose()
{
_processor.Dispose();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;

using Microsoft.Language.Xml;

namespace NuGetUpdater.Core.Updater
{
internal class XmlFilePreAndPostProcessor : IDisposable
{
public Func<string> GetContent { get; }
public Action<string> SetContent { get; }
public Func<XmlDocumentSyntax, XmlNodeSyntax?> NodeFinder { get; }
public Func<XmlNodeSyntax, XmlNodeSyntax> PreProcessor { get; }
public Func<XmlNodeSyntax, XmlNodeSyntax> PostProcessor { get; }

public XmlFilePreAndPostProcessor(Func<string> getContent, Action<string> setContent, Func<XmlDocumentSyntax, XmlNodeSyntax?> nodeFinder, Func<XmlNodeSyntax, XmlNodeSyntax> preProcessor, Func<XmlNodeSyntax, XmlNodeSyntax> postProcessor)
{
GetContent = getContent;
SetContent = setContent;
NodeFinder = nodeFinder;
PreProcessor = preProcessor;
PostProcessor = postProcessor;
PreProcess();
}

public void Dispose()
{
PostProcess();
}

private void PreProcess() => RunProcessor(PreProcessor);

private void PostProcess() => RunProcessor(PostProcessor);

private void RunProcessor(Func<XmlNodeSyntax, XmlNodeSyntax> processor)
{
var content = GetContent();
var xml = Parser.ParseText(content);
if (xml is null)
{
return;
}

var node = NodeFinder(xml);
if (node is null)
{
return;
}

var replacementElement = processor(node);
var replacementXml = xml.ReplaceNode(node, replacementElement);
var replacementString = replacementXml.ToFullString();
SetContent(replacementString);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ public static IEnumerable<IXmlElementSyntax> GetElements(this IXmlElementSyntax
return element.Attributes.FirstOrDefault(a => a.Name.Equals(name, comparisonType));
}

public static IXmlElementSyntax RemoveAttributeByName(this IXmlElementSyntax element, string attributeName, StringComparison comparisonType = StringComparison.Ordinal)
{
var attribute = element.GetAttribute(attributeName, comparisonType);
if (attribute is null)
{
return element;
}

return element.RemoveAttribute(attribute);
}

public static string GetAttributeValue(this IXmlElementSyntax element, string name, StringComparison comparisonType)
{
return element.Attributes.First(a => a.Name.Equals(name, comparisonType)).Value;
Expand Down