Skip to content
Draft
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
71 changes: 60 additions & 11 deletions src/Tasks/Microsoft.NET.Build.Tasks/RunReadyToRunCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@

namespace Microsoft.NET.Build.Tasks
{
public class RunReadyToRunCompiler : ToolTask
[MSBuildMultiThreadableTask]
public class RunReadyToRunCompiler : ToolTask, IMultiThreadableTask
{
#if NET
public override TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback;
#elif NETFRAMEWORK
public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback;
#endif

public ITaskItem CrossgenTool { get; set; }
public ITaskItem Crossgen2Tool { get; set; }

Expand Down Expand Up @@ -70,7 +77,40 @@ protected override string ToolName
}
}

protected override string GenerateFullPathToTool() => ToolName;
protected override string GenerateFullPathToTool() => TaskEnvironment.GetAbsolutePath(ToolName);

#if NETFRAMEWORK
protected override System.Diagnostics.ProcessStartInfo GetProcessStartInfo(
string pathToTool,
string commandLineCommands,
string responseFileSwitch)
{
System.Diagnostics.ProcessStartInfo startInfo = base.GetProcessStartInfo(
pathToTool,
commandLineCommands,
responseFileSwitch);
System.Diagnostics.ProcessStartInfo environmentStartInfo = TaskEnvironment.GetProcessStartInfo();

// The minimum .NET Framework ToolTask predates TaskEnvironment and starts with process-global state.
startInfo.WorkingDirectory = environmentStartInfo.WorkingDirectory;
startInfo.EnvironmentVariables.Clear();
foreach (string key in environmentStartInfo.EnvironmentVariables.Keys)
{
startInfo.EnvironmentVariables[key] = environmentStartInfo.EnvironmentVariables[key];
}

if (EnvironmentVariables != null)
{
foreach (string variable in EnvironmentVariables)
{
string[] nameValuePair = variable.Split(['='], 2);
startInfo.EnvironmentVariables[nameValuePair[0]] = nameValuePair[1];
}
}

return startInfo;
}
#endif

private string DiaSymReader => CrossgenTool.GetMetadata(MetadataKeys.DiaSymReader);

Expand Down Expand Up @@ -105,21 +145,23 @@ protected override bool ValidateParameters()
Log.LogError(Strings.Crossgen2ToolMissingWhenUseCrossgen2IsSet);
return false;
}
if (!File.Exists(Crossgen2Tool.ItemSpec))
if (string.IsNullOrEmpty(Crossgen2Tool.ItemSpec) ||
!File.Exists(TaskEnvironment.GetAbsolutePath(Crossgen2Tool.ItemSpec)))
{
Log.LogError(Strings.Crossgen2ToolExecutableNotFound, Crossgen2Tool.ItemSpec);
return false;
}
string hostPath = DotNetHostPath;
if (!string.IsNullOrEmpty(hostPath) && !File.Exists(hostPath))
if (!string.IsNullOrEmpty(hostPath) &&
!File.Exists(TaskEnvironment.GetAbsolutePath(hostPath)))
{
Log.LogError(Strings.DotNetHostExecutableNotFound, hostPath);
return false;
}
string jitPath = Crossgen2Tool.GetMetadata(MetadataKeys.JitPath);
if (!string.IsNullOrEmpty(jitPath))
{
if (!File.Exists(jitPath))
if (!File.Exists(TaskEnvironment.GetAbsolutePath(jitPath)))
{
Log.LogError(Strings.JitLibraryNotFound, jitPath);
return false;
Expand Down Expand Up @@ -153,12 +195,15 @@ protected override bool ValidateParameters()
Log.LogError(Strings.CrossgenToolMissingWhenUseCrossgen2IsNotSet);
return false;
}
if (!File.Exists(CrossgenTool.ItemSpec))
if (string.IsNullOrEmpty(CrossgenTool.ItemSpec) ||
!File.Exists(TaskEnvironment.GetAbsolutePath(CrossgenTool.ItemSpec)))
{
Log.LogError(Strings.CrossgenToolExecutableNotFound, CrossgenTool.ItemSpec);
return false;
}
if (!File.Exists(CrossgenTool.GetMetadata(MetadataKeys.JitPath)))
string jitPath = CrossgenTool.GetMetadata(MetadataKeys.JitPath);
if (string.IsNullOrEmpty(jitPath) ||
!File.Exists(TaskEnvironment.GetAbsolutePath(jitPath)))
{
Log.LogError(Strings.JitLibraryNotFound, MetadataKeys.JitPath);
return false;
Expand All @@ -171,7 +216,8 @@ protected override bool ValidateParameters()
{
_outputR2RImage = CompilationEntry.ItemSpec;

if (!string.IsNullOrEmpty(DiaSymReader) && !File.Exists(DiaSymReader))
if (!string.IsNullOrEmpty(DiaSymReader) &&
!File.Exists(TaskEnvironment.GetAbsolutePath(DiaSymReader)))
{
Log.LogError(Strings.DiaSymReaderLibraryNotFound, DiaSymReader);
return false;
Expand All @@ -183,7 +229,8 @@ protected override bool ValidateParameters()
Log.LogError(Strings.MissingOutputPDBImagePath);
}

if (!File.Exists(_outputR2RImage))
if (string.IsNullOrEmpty(_outputR2RImage) ||
!File.Exists(TaskEnvironment.GetAbsolutePath(_outputR2RImage)))
{
Log.LogError(Strings.PDBGeneratorInputExecutableNotFound, _outputR2RImage);
return false;
Expand All @@ -196,7 +243,8 @@ protected override bool ValidateParameters()
if (!_createCompositeImage)
{
_inputAssembly = CompilationEntry.ItemSpec;
if (!File.Exists(_inputAssembly))
if (string.IsNullOrEmpty(_inputAssembly) ||
!File.Exists(TaskEnvironment.GetAbsolutePath(_inputAssembly)))
{
Log.LogError(Strings.InputAssemblyNotFound, _inputAssembly);
return false;
Expand Down Expand Up @@ -433,7 +481,8 @@ protected override int ExecuteTool(string pathToTool, string responseFileCommand
{
// Ensure output sub-directories exists - Crossgen does not create directories for output files. Any relative path used with the
// '/out' parameter has to have an existing directory.
Directory.CreateDirectory(Path.GetDirectoryName(_outputR2RImage));
string outputDirectory = Path.GetDirectoryName(_outputR2RImage);
Directory.CreateDirectory(TaskEnvironment.GetAbsolutePath(outputDirectory));

WarningsDetected = false;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using FluentAssertions;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace Microsoft.NET.Build.Tasks.UnitTests;

[TestClass]
public class GivenARunReadyToRunCompilerMultiThreading : SdkTest
{
[TestMethod]
public void RelativePathsResolveIndependentlyForEachTask()
{
string firstProjectDirectory = TestAssetsManager.CreateTestDirectory(identifier: "first").Path;
string secondProjectDirectory = TestAssetsManager.CreateTestDirectory(identifier: "second").Path;
string relativeToolPath = Path.Combine("tools", "crossgen2");
string relativeInputPath = Path.Combine("input", "app.dll");

CreateFile(Path.Combine(firstProjectDirectory, relativeToolPath));
CreateFile(Path.Combine(firstProjectDirectory, relativeInputPath));
CreateFile(Path.Combine(secondProjectDirectory, relativeToolPath));
CreateFile(Path.Combine(secondProjectDirectory, relativeInputPath));

var firstTask = CreateTask(firstProjectDirectory, relativeToolPath, relativeInputPath);
var secondTask = CreateTask(secondProjectDirectory, relativeToolPath, relativeInputPath);

firstTask.ValidateParametersForTest().Should().BeTrue();
secondTask.ValidateParametersForTest().Should().BeTrue();
firstTask.GenerateFullPathToToolForTest().Should().Be(Path.Combine(firstProjectDirectory, relativeToolPath));
secondTask.GenerateFullPathToToolForTest().Should().Be(Path.Combine(secondProjectDirectory, relativeToolPath));
}

[TestMethod]
public void EmptyToolPathUsesExistingValidationDiagnostic()
{
string projectDirectory = TestAssetsManager.CreateTestDirectory().Path;
var task = CreateTask(projectDirectory, string.Empty, Path.Combine("input", "app.dll"));

task.ValidateParametersForTest().Should().BeFalse();
((MockBuildEngine)task.BuildEngine).Errors.Should().ContainSingle();
}

private static TestableRunReadyToRunCompiler CreateTask(
string projectDirectory,
string relativeToolPath,
string relativeInputPath)
{
var crossgen2Tool = new TaskItem(relativeToolPath);
crossgen2Tool.SetMetadata(MetadataKeys.TargetOS, "windows");
crossgen2Tool.SetMetadata(MetadataKeys.TargetArch, "x64");

var compilationEntry = new TaskItem(relativeInputPath);
compilationEntry.SetMetadata(MetadataKeys.OutputR2RImage, Path.Combine("output", "app.dll"));

return new TestableRunReadyToRunCompiler
{
BuildEngine = new MockBuildEngine(),
TaskEnvironment = TaskEnvironmentHelper.CreateForTest(projectDirectory),
Crossgen2Tool = crossgen2Tool,
CompilationEntry = compilationEntry,
ImplementationAssemblyReferences = [],
UseCrossgen2 = true,
};
}

private static void CreateFile(string path)
{
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
File.WriteAllBytes(path, []);
}

private sealed class TestableRunReadyToRunCompiler : RunReadyToRunCompiler
{
public bool ValidateParametersForTest() => ValidateParameters();

public string GenerateFullPathToToolForTest() => GenerateFullPathToTool();
}
}
Loading