Skip to content
Open
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
117 changes: 0 additions & 117 deletions src/Tasks/Common/ProcessTaskEnvironmentDriver.cs

This file was deleted.

17 changes: 9 additions & 8 deletions src/Tasks/Common/TaskEnvironmentDefaults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@
// Provides a default TaskEnvironment for single-threaded MSBuild execution.
// When MSBuild supports IMultiThreadableTask, it sets TaskEnvironment directly.
// This fallback ensures tasks work with older MSBuild versions that do not set it.
//
// Delegates to MSBuild's public TaskEnvironment.Fallback API
// (see https://github.com/dotnet/msbuild/pull/13462) so we no longer carry our
// own polyfill driver implementation.

#if NETFRAMEWORK

using System;

namespace Microsoft.Build.Framework
{
internal static class TaskEnvironmentDefaults
{
/// <summary>
/// Creates a default TaskEnvironment backed by the current process environment.
/// Uses Environment.CurrentDirectory as the project directory, which in single-threaded
/// MSBuild is set to the project directory before task execution.
/// Returns the MSBuild-provided fallback TaskEnvironment, which is backed by the
/// current process environment and uses Environment.CurrentDirectory as the project
/// directory.
/// </summary>
internal static TaskEnvironment Create() =>
new TaskEnvironment(new ProcessTaskEnvironmentDriver(Environment.CurrentDirectory));
internal static TaskEnvironment Create() => TaskEnvironment.Fallback;

Check failure on line 23 in src/Tasks/Common/TaskEnvironmentDefaults.cs

View check run for this annotation

Azure Pipelines / dotnet-sdk-public-ci (Build TestBuild: linux (x64))

src/Tasks/Common/TaskEnvironmentDefaults.cs#L23

src/Tasks/Common/TaskEnvironmentDefaults.cs(23,69): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'TaskEnvironment' does not contain a definition for 'Fallback'

Check failure on line 23 in src/Tasks/Common/TaskEnvironmentDefaults.cs

View check run for this annotation

Azure Pipelines / dotnet-sdk-public-ci (Build TestBuild: linux (arm64))

src/Tasks/Common/TaskEnvironmentDefaults.cs#L23

src/Tasks/Common/TaskEnvironmentDefaults.cs(23,69): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'TaskEnvironment' does not contain a definition for 'Fallback'

Check failure on line 23 in src/Tasks/Common/TaskEnvironmentDefaults.cs

View check run for this annotation

Azure Pipelines / dotnet-sdk-public-ci (Build TestBuild: macOS (x64))

src/Tasks/Common/TaskEnvironmentDefaults.cs#L23

src/Tasks/Common/TaskEnvironmentDefaults.cs(23,69): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'TaskEnvironment' does not contain a definition for 'Fallback'

Check failure on line 23 in src/Tasks/Common/TaskEnvironmentDefaults.cs

View check run for this annotation

Azure Pipelines / dotnet-sdk-public-ci (Build AoT: macOS (x64))

src/Tasks/Common/TaskEnvironmentDefaults.cs#L23

src/Tasks/Common/TaskEnvironmentDefaults.cs(23,69): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'TaskEnvironment' does not contain a definition for 'Fallback'

Check failure on line 23 in src/Tasks/Common/TaskEnvironmentDefaults.cs

View check run for this annotation

Azure Pipelines / dotnet-sdk-public-ci (Build TestBuild: macOS (arm64))

src/Tasks/Common/TaskEnvironmentDefaults.cs#L23

src/Tasks/Common/TaskEnvironmentDefaults.cs(23,69): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'TaskEnvironment' does not contain a definition for 'Fallback'

Check failure on line 23 in src/Tasks/Common/TaskEnvironmentDefaults.cs

View check run for this annotation

Azure Pipelines / dotnet-sdk-public-ci

src/Tasks/Common/TaskEnvironmentDefaults.cs#L23

src/Tasks/Common/TaskEnvironmentDefaults.cs(23,69): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'TaskEnvironment' does not contain a definition for 'Fallback'

Check failure on line 23 in src/Tasks/Common/TaskEnvironmentDefaults.cs

View check run for this annotation

Azure Pipelines / dotnet-sdk-public-ci

src/Tasks/Common/TaskEnvironmentDefaults.cs#L23

src/Tasks/Common/TaskEnvironmentDefaults.cs(23,69): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'TaskEnvironment' does not contain a definition for 'Fallback'
}
}

#endif
#endif
27 changes: 18 additions & 9 deletions src/Tasks/Microsoft.NET.Build.Tasks/ShowPreviewMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,32 @@ namespace Microsoft.NET.Build.Tasks
/// <summary>
/// Provides a localizable mechanism for logging messages with different levels of importance from the SDK targets.
/// </summary>
[MSBuildMultiThreadableTask]
public class ShowPreviewMessage : TaskBase
{
private static readonly object s_previewMessageLock = new();

protected override void ExecuteCore()
{
const string previewMessageKey = "Microsoft.NET.Build.Tasks.DisplayPreviewMessageKey";

object messageDisplayed =
BuildEngine4.GetRegisteredTaskObject(previewMessageKey, RegisteredTaskObjectLifetime.Build);
if (messageDisplayed == null)
if (BuildEngine4.GetRegisteredTaskObject(previewMessageKey, RegisteredTaskObjectLifetime.Build) is not null)
{
return;
}

lock (s_previewMessageLock)
Comment thread
OvesN marked this conversation as resolved.
{
Log.LogMessage(MessageImportance.High, Strings.UsingPreviewSdk);
if (BuildEngine4.GetRegisteredTaskObject(previewMessageKey, RegisteredTaskObjectLifetime.Build) is null)
{
Log.LogMessage(MessageImportance.High, Strings.UsingPreviewSdk);

BuildEngine4.RegisterTaskObject(
previewMessageKey,
new object(),
RegisteredTaskObjectLifetime.Build,
true);
BuildEngine4.RegisterTaskObject(
previewMessageKey,
new object(),
RegisteredTaskObjectLifetime.Build,
true);
}
}
}
}
Expand Down
142 changes: 7 additions & 135 deletions test/Microsoft.NET.Build.Tasks.Tests/TaskEnvironmentHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.

// Helper class for creating TaskEnvironment instances in tests.
// NOT gated with #if — always available in the test project.
// Adapted from: https://github.com/dotnet/msbuild/blob/main/src/UnitTests.Shared/TaskEnvironmentHelper.cs

using System;
using System.Collections;
using System.Collections.Generic;
// Delegates to MSBuild's public TaskEnvironment factory APIs (see
// https://github.com/dotnet/msbuild/pull/13462) instead of constructing a
// reflection-based driver locally.

using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Build.Framework;

namespace Microsoft.NET.Build.Tasks.UnitTests
Expand All @@ -22,142 +17,19 @@ namespace Microsoft.NET.Build.Tasks.UnitTests
public static class TaskEnvironmentHelper
{
/// <summary>
/// Creates a TaskEnvironment using the current working directory as the project directory.
/// Creates a TaskEnvironment using the current process environment and CWD.
/// </summary>
public static TaskEnvironment CreateForTest()
{
return CreateForTest(Directory.GetCurrentDirectory());
return TaskEnvironment.Fallback;
}

/// <summary>
/// Creates a TaskEnvironment with the specified project directory.
/// Uses reflection to work around internal visibility of ITaskEnvironmentDriver and TaskEnvironment ctor.
/// </summary>
public static TaskEnvironment CreateForTest(string projectDirectory)
{
// Get the internal ITaskEnvironmentDriver type from Microsoft.Build.Framework
var driverInterfaceType = typeof(TaskEnvironment).Assembly
.GetType("Microsoft.Build.Framework.ITaskEnvironmentDriver", throwOnError: true)!;

// Create a DispatchProxy that implements ITaskEnvironmentDriver dynamically.
// DispatchProxy.Create<TInterface, TProxy>() is called via reflection since TInterface is internal.
var createMethod = typeof(DispatchProxy)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.First(m => m.Name == nameof(DispatchProxy.Create) && m.GetGenericArguments().Length == 2)
.MakeGenericMethod(driverInterfaceType, typeof(TestDriverProxy));

var proxy = createMethod.Invoke(null, null)!;

// Initialize the proxy with the project directory
((TestDriverProxy)proxy).Initialize(projectDirectory);

// Call the internal TaskEnvironment(ITaskEnvironmentDriver) constructor via reflection
var ctor = typeof(TaskEnvironment)
.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(c =>
{
var parameters = c.GetParameters();
return parameters.Length == 1 && parameters[0].ParameterType == driverInterfaceType;
});

if (ctor is null)
{
throw new InvalidOperationException("Could not find TaskEnvironment constructor with ITaskEnvironmentDriver parameter.");
}

return (TaskEnvironment)ctor.Invoke(new[] { proxy });
}
}

/// <summary>
/// DispatchProxy-based implementation of the internal ITaskEnvironmentDriver interface.
/// Stores its own project directory independently from the process's CWD,
/// enabling tests to verify tasks resolve paths relative to ProjectDirectory, not CWD.
/// </summary>
internal class TestDriverProxy : DispatchProxy
{
private string _projectDirectory = string.Empty;
private Dictionary<string, string> _environmentVariables = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

internal void Initialize(string projectDirectory)
{
_projectDirectory = projectDirectory;

// Seed from the current process environment
foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables())
{
if (entry.Key is string key && entry.Value is string value)
_environmentVariables[key] = value;
}
}

protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
{
if (targetMethod == null) return null;

return targetMethod.Name switch
{
"get_ProjectDirectory" => new AbsolutePath(_projectDirectory),
"set_ProjectDirectory" => SetProjectDir(args),
"GetAbsolutePath" => ResolveAbsolutePath((string)args![0]!),
"GetEnvironmentVariable" => DoGetEnvVar(args),
"GetEnvironmentVariables" => GetEnvVars(),
"SetEnvironmentVariable" => DoSetEnvVar(args),
"SetEnvironment" => DoSetEnv(args),
"GetProcessStartInfo" => throw new NotImplementedException(
"GetProcessStartInfo is not used by SDK tasks."),
"Dispose" => null,
_ => throw new NotSupportedException($"Method '{targetMethod.Name}' is not supported by {nameof(TestDriverProxy)}."),
};
return TaskEnvironment.CreateWithProjectDirectoryAndEnvironment(projectDirectory);
}

private object? SetProjectDir(object?[]? args)
{
_projectDirectory = ((AbsolutePath)args![0]!).Value;
return null;
}

private AbsolutePath ResolveAbsolutePath(string path)
{
if (Path.IsPathRooted(path))
return new AbsolutePath(path);
return new AbsolutePath(path, new AbsolutePath(_projectDirectory));
}

private object? DoGetEnvVar(object?[]? args)
{
var name = (string)args![0]!;
return _environmentVariables.TryGetValue(name, out var value) ? value : null;
}

private IReadOnlyDictionary<string, string> GetEnvVars()
{
return new Dictionary<string, string>(_environmentVariables, StringComparer.OrdinalIgnoreCase);
}

private object? DoSetEnvVar(object?[]? args)
{
var name = (string)args![0]!;
var value = (string?)args[1];
if (value == null)
{
_environmentVariables.Remove(name);
}
else
{
_environmentVariables[name] = value;
}
return null;
}

private object? DoSetEnv(object?[]? args)
{
var newEnv = (IDictionary<string, string>)args![0]!;
_environmentVariables.Clear();
foreach (var kvp in newEnv)
_environmentVariables[kvp.Key] = kvp.Value;
return null;
}

}
}
}
Loading