Skip to content

Commit 71bcd22

Browse files
JanProvaznikCopilot
andcommitted
Pin net472 test projects to servicing assemblies instead of rewriting exe.config
Replace the previous approach (rewriting the bundled MSBuild.exe.config binding redirects down to the compiled-against versions) with deploying the servicing assemblies into the test bins, as preferred in review. Test projects that bundle MSBuild.exe and spawn it out-of-proc (Engine, CommandLine, Tasks, Utilities UnitTests) now set DeployRuntimeServicingAssemblies=true, just like MSBuild.csproj and the bootstrap. Their net472 output then carries the servicing runtime assemblies that the copied servicing app.config binding redirects point at, so the bundled MSBuild.exe starts correctly instead of failing with a FileLoadException. Removes the AlignBindingRedirectsToOutputAssemblies task/target added previously. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e68114a commit 71bcd22

5 files changed

Lines changed: 29 additions & 110 deletions

File tree

src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
<TargetFrameworks>$(RuntimeOutputTargetFrameworks)</TargetFrameworks>
55
<PlatformTarget>$(RuntimeOutputPlatformTarget)</PlatformTarget>
66
<IsPackable>false</IsPackable>
7+
8+
<!-- This project bundles MSBuild.exe (via the MSBuild.csproj reference) and spawns it
9+
out-of-proc, so it is a net472 servicing-assembly redistribution surface just like
10+
MSBuild.csproj: deploy the servicing assemblies so the bundled MSBuild.exe and its
11+
servicing app.config binding redirects agree. See the
12+
ReplaceRuntimeAssembliesWithServicingVersions target in src/Directory.Build.targets. -->
13+
<DeployRuntimeServicingAssemblies>true</DeployRuntimeServicingAssemblies>
14+
715
<AssemblyName>Microsoft.Build.Engine.UnitTests</AssemblyName>
816

917
<DefineConstants>$(DefineConstants);MICROSOFT_BUILD_ENGINE_UNITTESTS</DefineConstants>

src/Directory.Build.targets

Lines changed: 0 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -296,116 +296,6 @@
296296
</ItemGroup>
297297
</Target>
298298

299-
<!--
300-
The net472 servicing redistribution above swaps DLLs and updates the binding redirects only in
301-
the projects that opt in ($(DeployRuntimeServicingAssemblies) == 'true': MSBuild.exe and the
302-
bootstrap). Test projects that re-bundle MSBuild.exe (they spawn it out-of-proc, e.g. via
303-
ProjectReference to MSBuild.csproj) copy MSBuild.exe.config (which now carries the servicing
304-
redirects) but resolve their own copy-local runtime assemblies at the compiled-against
305-
versions. That leaves MSBuild.exe.config pointing at servicing versions (e.g. System.Memory
306-
4.0.5.0) while the assembly on disk is the compiled-against one (4.0.2.0), so the copied
307-
MSBuild.exe fails to start with a FileLoadException, breaking every out-of-proc test (and
308-
hanging tests that wait on a spawned node/server).
309-
310-
These test bins are not a VS redistribution surface, so the fix is to keep them self-consistent
311-
at the compiled-against versions: realign the copied MSBuild.exe.config binding redirects to the
312-
runtime assemblies actually next to it.
313-
314-
This is scoped to test projects only ($(RealignBundledMSBuildExeConfig), defaulted on for
315-
$(IsTestProject) == 'true') and deliberately NOT keyed on the absence of
316-
$(DeployRuntimeServicingAssemblies): non-test projects that bundle MSBuild.exe for shipping
317-
(e.g. the VS.ExternalAPIs.MSBuild insertion package) must never have their copied config
318-
rewritten to the compiled-against versions, even though they happen not to ship MSBuild.exe.config
319-
today.
320-
-->
321-
<PropertyGroup>
322-
<RealignBundledMSBuildExeConfig Condition="'$(RealignBundledMSBuildExeConfig)' == '' and '$(IsTestProject)' == 'true'">true</RealignBundledMSBuildExeConfig>
323-
</PropertyGroup>
324-
325-
<UsingTask TaskName="AlignBindingRedirectsToOutputAssemblies"
326-
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll"
327-
TaskFactory="RoslynCodeTaskFactory">
328-
<ParameterGroup>
329-
<ConfigFiles ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
330-
</ParameterGroup>
331-
<Task>
332-
<Code Type="Fragment" Language="cs"><![CDATA[
333-
System.Xml.Linq.XNamespace ns = "urn:schemas-microsoft-com:asm.v1";
334-
foreach (Microsoft.Build.Framework.ITaskItem item in ConfigFiles)
335-
{
336-
string configPath = item.ItemSpec;
337-
if (!System.IO.File.Exists(configPath))
338-
{
339-
continue;
340-
}
341-
342-
string directory = System.IO.Path.GetDirectoryName(configPath);
343-
System.Xml.Linq.XDocument document = System.Xml.Linq.XDocument.Load(configPath);
344-
bool changed = false;
345-
346-
foreach (System.Xml.Linq.XElement dependentAssembly in document.Descendants(ns + "dependentAssembly"))
347-
{
348-
System.Xml.Linq.XElement identity = dependentAssembly.Element(ns + "assemblyIdentity");
349-
System.Xml.Linq.XElement redirect = dependentAssembly.Element(ns + "bindingRedirect");
350-
if (identity == null || redirect == null)
351-
{
352-
continue;
353-
}
354-
355-
string name = (string)identity.Attribute("name");
356-
if (string.IsNullOrEmpty(name))
357-
{
358-
continue;
359-
}
360-
361-
string assemblyPath = System.IO.Path.Combine(directory, name + ".dll");
362-
if (!System.IO.File.Exists(assemblyPath))
363-
{
364-
continue;
365-
}
366-
367-
string version;
368-
try
369-
{
370-
version = System.Reflection.AssemblyName.GetAssemblyName(assemblyPath).Version.ToString();
371-
}
372-
catch (System.Exception)
373-
{
374-
continue;
375-
}
376-
377-
redirect.SetAttributeValue("oldVersion", "0.0.0.0-" + version);
378-
redirect.SetAttributeValue("newVersion", version);
379-
380-
System.Xml.Linq.XElement codeBase = dependentAssembly.Element(ns + "codeBase");
381-
if (codeBase != null)
382-
{
383-
codeBase.SetAttributeValue("version", version);
384-
}
385-
386-
changed = true;
387-
}
388-
389-
if (changed)
390-
{
391-
document.Save(configPath);
392-
Log.LogMessage(Microsoft.Build.Framework.MessageImportance.Normal, "Aligned binding redirects in '{0}' to the runtime assemblies in its output directory.", configPath);
393-
}
394-
}
395-
]]></Code>
396-
</Task>
397-
</UsingTask>
398-
399-
<Target Name="AlignCopiedMSBuildExeConfigBindingRedirects"
400-
AfterTargets="Build"
401-
Condition="'$(TargetFramework)' == 'net472' and '$(RealignBundledMSBuildExeConfig)' == 'true'">
402-
<ItemGroup>
403-
<_CopiedMSBuildExeConfig Include="$(TargetDir)**\MSBuild.exe.config" />
404-
</ItemGroup>
405-
<AlignBindingRedirectsToOutputAssemblies ConfigFiles="@(_CopiedMSBuildExeConfig)"
406-
Condition="'@(_CopiedMSBuildExeConfig)' != ''" />
407-
</Target>
408-
409299
<!-- Import parent targets -->
410300
<Import Project="..\Directory.Build.targets"/>
411301

src/MSBuild.UnitTests/Microsoft.Build.CommandLine.UnitTests.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44
<TargetFrameworks>$(RuntimeOutputTargetFrameworks)</TargetFrameworks>
55
<PlatformTarget>$(RuntimeOutputPlatformTarget)</PlatformTarget>
66
<IsPackable>false</IsPackable>
7+
8+
<!-- This project bundles MSBuild.exe (via the MSBuild.csproj reference) and spawns it
9+
out-of-proc, so it is a net472 servicing-assembly redistribution surface just like
10+
MSBuild.csproj: deploy the servicing assemblies so the bundled MSBuild.exe and its
11+
servicing app.config binding redirects agree. See the
12+
ReplaceRuntimeAssembliesWithServicingVersions target in src/Directory.Build.targets. -->
13+
<DeployRuntimeServicingAssemblies>true</DeployRuntimeServicingAssemblies>
714
</PropertyGroup>
815

916
<ItemGroup>

src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
<IsPackable>false</IsPackable>
77
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
88

9+
<!-- This project bundles MSBuild.exe (via the MSBuild.csproj reference) and spawns it
10+
out-of-proc, so it is a net472 servicing-assembly redistribution surface just like
11+
MSBuild.csproj: deploy the servicing assemblies so the bundled MSBuild.exe and its
12+
servicing app.config binding redirects agree. See the
13+
ReplaceRuntimeAssembliesWithServicingVersions target in src/Directory.Build.targets. -->
14+
<DeployRuntimeServicingAssemblies>true</DeployRuntimeServicingAssemblies>
15+
916
<AssemblyName>Microsoft.Build.Tasks.UnitTests</AssemblyName>
1017
<IsTestProject>true</IsTestProject>
1118
<DefineConstants>$(DefineConstants);MICROSOFT_BUILD_TASKS_UNITTESTS</DefineConstants>

src/Utilities.UnitTests/Microsoft.Build.Utilities.UnitTests.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
<AssemblyName>Microsoft.Build.Utilities.UnitTests</AssemblyName>
77
<IsTestProject>true</IsTestProject>
88
<RootNamespace>Microsoft.Build.Utilities.UnitTests</RootNamespace>
9+
10+
<!-- This project bundles MSBuild.exe (via the MSBuild.csproj reference) and spawns it
11+
out-of-proc, so it is a net472 servicing-assembly redistribution surface just like
12+
MSBuild.csproj: deploy the servicing assemblies so the bundled MSBuild.exe and its
13+
servicing app.config binding redirects agree. See the
14+
ReplaceRuntimeAssembliesWithServicingVersions target in src/Directory.Build.targets. -->
15+
<DeployRuntimeServicingAssemblies>true</DeployRuntimeServicingAssemblies>
916
</PropertyGroup>
1017

1118
<ItemGroup>

0 commit comments

Comments
 (0)