Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 2 src/msbuild/.vsts-dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ variables:
- name: SkipApplyOptimizationData
value: true
- name: EnableReleaseOneLocBuild
value: true # Enable loc for vs17.14
value: true # Enable loc for vs18.0
- name: Codeql.Enabled
value: true
- group: DotNet-MSBuild-SDLValidation-Params
Expand Down
2 changes: 1 addition & 1 deletion 2 src/msbuild/azure-pipelines/vs-insertion.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ variables:
- name: AutoInsertTargetBranch
${{ if eq(variables['Build.SourceBranchName'], 'vs18.0') }}:
value: 'rel/d18.0'
${{ if eq(variables['Build.SourceBranchName'], 'vs17.14') }}:
${{ elseif eq(variables['Build.SourceBranchName'], 'vs17.14') }}:
value: 'rel/d17.14'
${{ elseif eq(variables['Build.SourceBranchName'], 'vs17.13') }}:
value: 'rel/d17.13'
Expand Down
2 changes: 1 addition & 1 deletion 2 src/msbuild/eng/Version.Details.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Dependencies>
<Source Uri="https://github.com/dotnet/dotnet" Mapping="msbuild" Sha="e72b5bbe719d747036ce9c36582a205df9f1c361" BarId="285185" />
<Source Uri="https://github.com/dotnet/dotnet" Mapping="msbuild" Sha="d499f9cdfd5b7b7bb291f95a3c14417d5edc969f" BarId="287756" />
<ProductDependencies>
<!-- Necessary for source-build. This allows the live version of the package to be used by source-build. -->
<Dependency Name="System.CodeDom" Version="9.0.0">
Expand Down
3 changes: 1 addition & 2 deletions 3 src/msbuild/eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
<Project>
<Import Project="Version.Details.props" Condition="Exists('Version.Details.props')" />
<PropertyGroup>
<VersionPrefix>18.0.0</VersionPrefix><DotNetFinalVersionKind>release</DotNetFinalVersionKind>
<VersionPrefix>18.0.2</VersionPrefix><DotNetFinalVersionKind>release</DotNetFinalVersionKind>
<PackageValidationBaselineVersion>17.14.8</PackageValidationBaselineVersion>
<AssemblyVersion>15.1.0.0</AssemblyVersion>
<PreReleaseVersionLabel>servicing</PreReleaseVersionLabel>

<!-- differentiate experimental insertions to avoid package id conflicts,
it has to be alphabetically after "preview" to avoid downgrade errors in VS -->
<PreReleaseVersionLabel Condition="'$(IsExperimental)' == 'true'">test</PreReleaseVersionLabel>
Expand Down
2 changes: 1 addition & 1 deletion 2 src/msbuild/eng/common/post-build/nuget-verification.ps1

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@
<EmbeddedResource Include="TestAssets\ExampleNetTask\TestMSBuildTaskInNet\global.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="TestAssets\ExampleNetTask\TestNetTaskWithImplicitParams\global.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="TestAssets\ExampleNetTask\TestNetTask\global.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
Expand Down
90 changes: 85 additions & 5 deletions 90 src/msbuild/src/Build.UnitTests/NetTaskHost_E2E_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public NetTaskHost_E2E_Tests(ITestOutputHelper output)
[WindowsFullFrameworkOnlyFact]
public void NetTaskHostTest()
{
using TestEnvironment env = TestEnvironment.Create(_output, setupDotnetEnvVars: true);
var bootstrapCorePath = Path.Combine(RunnerUtilities.BootstrapRootPath, "core", Constants.DotnetProcessName);
using TestEnvironment env = TestEnvironment.Create(_output);
var dotnetPath = env.GetEnvironmentVariable("DOTNET_ROOT");

string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestNetTask", "TestNetTask.csproj");

Expand All @@ -45,7 +45,7 @@ public void NetTaskHostTest()

successTestTask.ShouldBeTrue();
testTaskOutput.ShouldContain($"The task is executed in process: dotnet");
testTaskOutput.ShouldContain($"Process path: {bootstrapCorePath}", customMessage: testTaskOutput);
testTaskOutput.ShouldContain($"Process path: {dotnetPath}", customMessage: testTaskOutput);

var customTaskAssemblyLocation = Path.GetFullPath(Path.Combine(AssemblyLocation, "..", LatestDotNetCoreForMSBuild, "ExampleTask.dll"));
var resource = ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword(
Expand All @@ -57,11 +57,70 @@ public void NetTaskHostTest()
testTaskOutput.ShouldNotContain(resource);
}

[WindowsFullFrameworkOnlyFact]
public void NetTaskHost_CorrectPathsEscapingTest()
{
using TestEnvironment env = TestEnvironment.Create(_output);
var dotnetPath = env.GetEnvironmentVariable("DOTNET_ROOT");
string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestNetTask", "TestNetTask.csproj");
string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n", out bool successTestTask);

if (!successTestTask)
{
_output.WriteLine(testTaskOutput);
}

successTestTask.ShouldBeTrue();
testTaskOutput.ShouldContain($"Process path: {dotnetPath}", customMessage: testTaskOutput);

// Explicitly validate escaping for paths with spaces
var msBuildDllPathMatch = System.Text.RegularExpressions.Regex.Match(
testTaskOutput,
@"Arg\[0\]:\s*(.+)",
System.Text.RegularExpressions.RegexOptions.CultureInvariant);

msBuildDllPathMatch.Success.ShouldBeTrue();
string msBuildDllPath = msBuildDllPathMatch.Groups[1].Value.Trim();

if (msBuildDllPath.Contains(" "))
{
// Extract all space-delimited parts of the path and verify none are in subsequent args
var pathParts = msBuildDllPath.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (pathParts.Length > 1)
{
// Check that later path segments don't appear as separate command line arguments
var arg1Match = System.Text.RegularExpressions.Regex.Match(
testTaskOutput,
@"Arg\[1\]:\s*(.+)",
System.Text.RegularExpressions.RegexOptions.CultureInvariant);

if (arg1Match.Success)
{
string arg1Value = arg1Match.Groups[1].Value.Trim();
// Arg[1] should be a flag (starts with /), not a path fragment
arg1Value.ShouldMatch(@"^[/-]", "Arg[1] should be a flag, not a continuation of the path");
}
}
}

// Validate if path is in quotes in command line
var cmdLineMatch = System.Text.RegularExpressions.Regex.Match(
testTaskOutput,
@"Command line:\s*(.+)",
System.Text.RegularExpressions.RegexOptions.CultureInvariant);

if (cmdLineMatch.Success)
{
string cmdLine = cmdLineMatch.Groups[1].Value.Trim();
string quotedArg0 = $"\"{msBuildDllPath}\"";
cmdLine.ShouldContain(quotedArg0);
}
}

[WindowsFullFrameworkOnlyFact]
public void MSBuildTaskInNetHostTest()
{
using TestEnvironment env = TestEnvironment.Create(_output, setupDotnetEnvVars: true);
var bootstrapCorePath = Path.Combine(RunnerUtilities.BootstrapRootPath, "core", Constants.DotnetProcessName);
using TestEnvironment env = TestEnvironment.Create(_output);

string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestMSBuildTaskInNet", "TestMSBuildTaskInNet.csproj");

Expand All @@ -75,5 +134,26 @@ public void MSBuildTaskInNetHostTest()
successTestTask.ShouldBeTrue();
testTaskOutput.ShouldContain($"Hello TEST");
}

[WindowsFullFrameworkOnlyFact]
public void NetTaskWithImplicitHostParamsTest()
{
using TestEnvironment env = TestEnvironment.Create(_output);
var dotnetPath = env.GetEnvironmentVariable("DOTNET_ROOT");

string testProjectPath = Path.Combine(TestAssetsRootPath, "ExampleNetTask", "TestNetTaskWithImplicitParams", "TestNetTaskWithImplicitParams.csproj");

string testTaskOutput = RunnerUtilities.ExecBootstrapedMSBuild($"{testProjectPath} -restore -v:n", out bool successTestTask);

if (!successTestTask)
{
_output.WriteLine(testTaskOutput);
}

successTestTask.ShouldBeTrue();
testTaskOutput.ShouldContain($"The task is executed in process: dotnet");
testTaskOutput.ShouldContain($"Process path: {dotnetPath}", customMessage: testTaskOutput);
testTaskOutput.ShouldContain("/nodereuse:False");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Diagnostics;
using Microsoft.Build.Framework;

Expand All @@ -22,9 +23,15 @@ public override bool Execute()
Log.LogMessage(MessageImportance.High, $"The task is executed in process: {executingProcess}");
Log.LogMessage(MessageImportance.High, $"Process path: {processPath}");

string[] args = Environment.GetCommandLineArgs();
for (int i = 0; i < args.Length; i++)
{
Log.LogMessage(MessageImportance.High, $"Arg[{i}]: {args[i]}");
}

return true;
}
catch (System.Exception ex)
catch (Exception ex)
{
Log.LogError($"Failed to determine executing process: {ex.Message}");
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

<PropertyGroup>
<TestProjectFolder>$([System.IO.Path]::GetFullPath('$([System.IO.Path]::Combine('$(AssemblyLocation)', '..'))'))</TestProjectFolder>
<ExampleTaskPath>$([System.IO.Path]::Combine('$(TestProjectFolder)', '$(TargetFramework)', 'ExampleTask.dll'))</ExampleTaskPath>
</PropertyGroup>

<UsingTask TaskName="ExampleTask" AssemblyFile="$(ExampleTaskPath)" Runtime="NET"/>

<Target Name="TestTask" BeforeTargets="Build">
<ExampleTask />
</Target>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"sdk": {
// This global.json is needed to prevent builds running in tests using the bootstrap layout from walking
// up the repo tree and resolving our sdk.paths, instead of the bootstrap layout's SDK.
// See https://github.com/dotnet/runtime/issues/118488 for details.
"allowPrerelease": true,
"rollForward": "latestMajor"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -644,8 +644,8 @@ internal bool CreateNode(HandshakeOptions hostContext, INodePacketFactory factor
return false;
}

// if runtime host path is null it means we don't have MSBuild.dll path resolved and there is no need to include it in the command line arguments.
string commandLineArgsPlaceholder = "{0} /nologo /nodemode:2 /nodereuse:{1} /low:{2} ";
// If runtime host path is null it means we don't have MSBuild.dll path resolved and there is no need to include it in the command line arguments.
string commandLineArgsPlaceholder = "\"{0}\" /nologo /nodemode:2 /nodereuse:{1} /low:{2} ";

IList<NodeContext> nodeContexts;
int nodeId = (int)hostContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,16 +274,19 @@ internal LoadedType InitializeFactory(
ErrorUtilities.VerifyThrowArgumentNull(loadInfo);
VerifyThrowIdentityParametersValid(taskFactoryIdentityParameters, elementLocation, taskName, "Runtime", "Architecture");

bool taskHostParamsMatchCurrentProc = true;
if (taskFactoryIdentityParameters != null)
{
taskHostParamsMatchCurrentProc = TaskHostParametersMatchCurrentProcess(taskFactoryIdentityParameters);
_factoryIdentityParameters = new Dictionary<string, string>(taskFactoryIdentityParameters, StringComparer.OrdinalIgnoreCase);
}

_taskHostFactoryExplicitlyRequested = taskHostExplicitlyRequested;

_isTaskHostFactory = (taskFactoryIdentityParameters != null
&& taskFactoryIdentityParameters.TryGetValue(Constants.TaskHostExplicitlyRequested, out string isTaskHostFactory)
&& isTaskHostFactory.Equals("true", StringComparison.OrdinalIgnoreCase));
&& isTaskHostFactory.Equals("true", StringComparison.OrdinalIgnoreCase))
|| !taskHostParamsMatchCurrentProc;

try
{
Expand All @@ -293,7 +296,7 @@ internal LoadedType InitializeFactory(
string assemblyName = loadInfo.AssemblyName ?? Path.GetFileName(loadInfo.AssemblyFile);
using var assemblyLoadsTracker = AssemblyLoadsTracker.StartTracking(targetLoggingContext, AssemblyLoadingContext.TaskRun, assemblyName);

_loadedType = _typeLoader.Load(taskName, loadInfo, _taskHostFactoryExplicitlyRequested);
_loadedType = _typeLoader.Load(taskName, loadInfo, _taskHostFactoryExplicitlyRequested, taskHostParamsMatchCurrentProc);
ProjectErrorUtilities.VerifyThrowInvalidProject(_loadedType != null, elementLocation, "TaskLoadFailure", taskName, loadInfo.AssemblyLocation, String.Empty);
}
catch (TargetInvocationException e)
Expand Down
8 changes: 6 additions & 2 deletions 8 src/msbuild/src/Build/Instance/TaskRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1489,8 +1489,12 @@ private bool GetTaskFactory(TargetLoggingContext targetLoggingContext, ElementLo

bool isAssemblyTaskFactory = String.Equals(TaskFactoryAttributeName, AssemblyTaskFactory, StringComparison.OrdinalIgnoreCase);
bool isTaskHostFactory = String.Equals(TaskFactoryAttributeName, TaskHostFactory, StringComparison.OrdinalIgnoreCase);
_taskFactoryParameters ??= new();
TaskFactoryParameters.Add(Constants.TaskHostExplicitlyRequested, isTaskHostFactory.ToString());
_taskFactoryParameters ??= [];

if (isTaskHostFactory)
{
TaskFactoryParameters.Add(Constants.TaskHostExplicitlyRequested, isTaskHostFactory.ToString());
}

if (isAssemblyTaskFactory || isTaskHostFactory)
{
Expand Down
28 changes: 21 additions & 7 deletions 28 src/msbuild/src/Shared/TypeLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,10 @@ private static Assembly LoadAssemblyUsingMetadataLoadContext(AssemblyLoadInfo as
internal LoadedType Load(
string typeName,
AssemblyLoadInfo assembly,
bool useTaskHost = false)
bool useTaskHost = false,
bool taskHostParamsMatchCurrentProc = true)
{
return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly, useTaskHost);
return GetLoadedType(s_cacheOfLoadedTypesByFilter, typeName, assembly, useTaskHost, taskHostParamsMatchCurrentProc);
}

/// <summary>
Expand All @@ -227,15 +228,20 @@ internal LoadedType ReflectionOnlyLoad(
string typeName,
AssemblyLoadInfo assembly)
{
return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly, useTaskHost: false);
return GetLoadedType(s_cacheOfReflectionOnlyLoadedTypesByFilter, typeName, assembly, useTaskHost: false, taskHostParamsMatchCurrentProc: true);
}

/// <summary>
/// Loads the specified type if it exists in the given assembly. If the type name is fully qualified, then a match (if
/// any) is unambiguous; otherwise, if there are multiple types with the same name in different namespaces, the first type
/// found will be returned.
/// </summary>
private LoadedType GetLoadedType(ConcurrentDictionary<Func<Type, object, bool>, ConcurrentDictionary<AssemblyLoadInfo, AssemblyInfoToLoadedTypes>> cache, string typeName, AssemblyLoadInfo assembly, bool useTaskHost)
private LoadedType GetLoadedType(
ConcurrentDictionary<Func<Type, object, bool>, ConcurrentDictionary<AssemblyLoadInfo, AssemblyInfoToLoadedTypes>> cache,
string typeName,
AssemblyLoadInfo assembly,
bool useTaskHost,
bool taskHostParamsMatchCurrentProc)
{
// A given type filter have been used on a number of assemblies, Based on the type filter we will get another dictionary which
// will map a specific AssemblyLoadInfo to a AssemblyInfoToLoadedTypes class which knows how to find a typeName in a given assembly.
Expand All @@ -246,7 +252,7 @@ private LoadedType GetLoadedType(ConcurrentDictionary<Func<Type, object, bool>,
AssemblyInfoToLoadedTypes typeNameToType =
loadInfoToType.GetOrAdd(assembly, (_) => new AssemblyInfoToLoadedTypes(_isDesiredType, _));

return typeNameToType.GetLoadedTypeByTypeName(typeName, useTaskHost);
return typeNameToType.GetLoadedTypeByTypeName(typeName, useTaskHost, taskHostParamsMatchCurrentProc);
}

/// <summary>
Expand Down Expand Up @@ -316,11 +322,11 @@ internal AssemblyInfoToLoadedTypes(Func<Type, object, bool> typeFilter, Assembly
/// <summary>
/// Determine if a given type name is in the assembly or not. Return null if the type is not in the assembly
/// </summary>
internal LoadedType GetLoadedTypeByTypeName(string typeName, bool useTaskHost)
internal LoadedType GetLoadedTypeByTypeName(string typeName, bool useTaskHost, bool taskHostParamsMatchCurrentProc)
{
ErrorUtilities.VerifyThrowArgumentNull(typeName);

if (useTaskHost && _assemblyLoadInfo.AssemblyFile is not null)
if (ShouldUseMetadataLoadContext(useTaskHost, taskHostParamsMatchCurrentProc))
{
return GetLoadedTypeFromTypeNameUsingMetadataLoadContext(typeName);
}
Expand Down Expand Up @@ -374,6 +380,14 @@ internal LoadedType GetLoadedTypeByTypeName(string typeName, bool useTaskHost)
return type != null ? new LoadedType(type, _assemblyLoadInfo, _loadedAssembly ?? type.Assembly, typeof(ITaskItem), loadedViaMetadataLoadContext: false) : null;
}

/// <summary>
/// Determine whether an assembly is likely to be used out of process and thus loaded with a <see cref="MetadataLoadContext"/>.
/// </summary>
/// <param name="useTaskHost">Task Host Parameter was specified explicitly in XML or through environment variable.</param>
/// <param name="taskHostParamsMatchCurrentProc">The parameter defines if Runtime/Architecture explicitly defined in XML match current process.</param>
private bool ShouldUseMetadataLoadContext(bool useTaskHost, bool taskHostParamsMatchCurrentProc) =>
(useTaskHost || !taskHostParamsMatchCurrentProc) && _assemblyLoadInfo.AssemblyFile is not null;

private LoadedType GetLoadedTypeFromTypeNameUsingMetadataLoadContext(string typeName)
{
return _publicTypeNameToLoadedType.GetOrAdd(typeName, typeName =>
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.