Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
65 changes: 65 additions & 0 deletions src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs
Comment thread
AlesProkop marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.Build.Tasks;
using Microsoft.Build.Utilities;
using Microsoft.Runtime.Hosting;
using Shouldly;
using Xunit;

#nullable disable
Expand Down Expand Up @@ -309,6 +310,70 @@ public void TaskFailsWhenImproperlySigned()
}
}
}

/// <summary>
/// Regression test for the multithreaded task migration: a relative KeyFile must be
/// resolved against the task's project directory (via TaskEnvironment), not the
/// process working directory.
/// </summary>
[Fact]
public void RelativeKeyFile_ResolvedAgainstTaskEnvironmentProjectDirectory()
{
using TestEnvironment env = TestEnvironment.Create();
string projectDir = env.CreateFolder().Path;
string keyFileName = "myKey.key";
string keyFilePath = Path.Combine(projectDir, keyFileName);
File.WriteAllBytes(keyFilePath, new byte[] { 0x01, 0x02, 0x03, 0x04 });

var t = new ResolveComReference.AxImp
{
ActiveXControlName = "FakeControl.ocx",
ToolPath = projectDir,
KeyFile = keyFileName, // relative — must be resolved against projectDir
TaskEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment(projectDir),
};

// Process CWD intentionally differs from projectDir. With the multithreaded fix
Comment thread
AlesProkop marked this conversation as resolved.
Outdated
// the relative KeyFile resolves via TaskEnvironment.ProjectDirectory, so the
// "InvalidKeyFileSpecified" error must not be logged; the task should instead
// proceed and fail because the file is not a valid key pair.
MockEngine e = new MockEngine();
t.BuildEngine = e;
t.Execute().ShouldBeFalse();

Utilities.VerifyLogDoesNotContainErrorFromResource(e, t.Log, "AxTlbBaseTask.InvalidKeyFileSpecified", keyFileName);
Utilities.VerifyLogContainsErrorFromResource(e, t.Log, "AxTlbBaseTask.StrongNameUtils.NoKeyPairInFile", keyFileName);
}

/// <summary>
/// Regression test for the multithreaded task migration: a relative ToolPath must be
/// resolved against the task's project directory (via TaskEnvironment), not the
/// process working directory.
/// </summary>
[Fact]
public void RelativeToolPath_ResolvedAgainstTaskEnvironmentProjectDirectory()
{
using TestEnvironment env = TestEnvironment.Create();
string projectDir = env.CreateFolder().Path;
string toolDirName = "tools";
Directory.CreateDirectory(Path.Combine(projectDir, toolDirName));

var t = new ResolveComReference.AxImp
{
ActiveXControlName = "FakeControl.ocx",
ToolPath = toolDirName, // relative — must be resolved against projectDir
TaskEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment(projectDir),
};

// Process CWD intentionally differs from projectDir. With the multithreaded fix
// the relative ToolPath resolves via TaskEnvironment.ProjectDirectory, so the
// "SdkOrToolPathNotSpecifiedOrInvalid" error must not be logged.
MockEngine e = new MockEngine();
t.BuildEngine = e;
t.Execute().ShouldBeFalse(); // task still fails for other reasons (missing tool exe)

Utilities.VerifyLogDoesNotContainErrorFromResource(e, t.Log, "AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", t.SdkToolsPath ?? "", t.ToolPath ?? "");
}
}

internal sealed class Utilities
Expand Down
3 changes: 3 additions & 0 deletions src/Tasks/AxImp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

using System;

using Microsoft.Build.Framework;

#nullable disable

namespace Microsoft.Build.Tasks
Expand All @@ -16,6 +18,7 @@ public sealed partial class ResolveComReference
/// Defines the "AxImp" MSBuild task, which enables using AxImp.exe
/// to generate Windows Forms wrappers for ActiveX controls.
/// </summary>
[MSBuildMultiThreadableTask]
internal class AxImp : AxTlbBaseTask
{
#region Properties
Expand Down
39 changes: 33 additions & 6 deletions src/Tasks/AxTlbBaseTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared.FileSystem;

#nullable disable
Expand Down Expand Up @@ -107,14 +108,20 @@ protected internal override void AddCommandLineCommands(CommandLineBuilderExtens
protected override string GenerateFullPathToTool()
{
string pathToTool = SdkToolsPathUtility.GeneratePathToTool(
Comment thread
AlesProkop marked this conversation as resolved.
SdkToolsPathUtility.FileInfoExists,
FileInfoExists,
Utilities.ProcessorArchitecture.CurrentProcessArchitecture,
SdkToolsPath,
ToolName,
Log,
true);

return pathToTool;
if (string.IsNullOrEmpty(pathToTool))
{
return pathToTool;
}

AbsolutePath absolutePath = TaskEnvironment.GetAbsolutePathIfValid(pathToTool, Log);
Comment thread
AlesProkop marked this conversation as resolved.
Outdated
return absolutePath.Value ?? pathToTool;
}

/// <summary>
Expand All @@ -125,8 +132,8 @@ protected override bool ValidateParameters()
{
// Verify that a path for the tool exists -- if the tool doesn't exist in it
// we'll worry about that later
if ((String.IsNullOrEmpty(ToolPath) || !FileSystems.Default.DirectoryExists(ToolPath)) &&
(String.IsNullOrEmpty(SdkToolsPath) || !FileSystems.Default.DirectoryExists(SdkToolsPath)))
if ((string.IsNullOrEmpty(ToolPath) || !DirectoryExists(ToolPath)) &&
(string.IsNullOrEmpty(SdkToolsPath) || !DirectoryExists(SdkToolsPath)))
{
Log.LogErrorWithCodeFromResources("AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", SdkToolsPath ?? "", ToolPath ?? "");
return false;
Expand Down Expand Up @@ -155,6 +162,9 @@ private void AddStrongNameOptions(CommandLineBuilderExtension commandLine)
// throw an error.
//
// So use /publickey if that's all our KeyFile contains, but KeyFile otherwise.
// The KeyFile path is passed verbatim to the spawned tool; ToolTask sets the child
Comment thread
AlesProkop marked this conversation as resolved.
Outdated
// process's WorkingDirectory to the project directory (via TaskEnvironment), so the
// tool resolves relative paths correctly without us absolutizing here.
if (_delaySigningAndKeyFileOnlyContainsPublicKey)
{
commandLine.AppendSwitchIfNotNull("/publickey:", KeyFile);
Expand All @@ -175,11 +185,14 @@ private void AddStrongNameOptions(CommandLineBuilderExtension commandLine)
private bool ValidateStrongNameParameters()
{
bool keyFileExists = false;
AbsolutePath keyFileForRead = default;

// Make sure that if KeyFile is defined, it's a real file.
if (!String.IsNullOrEmpty(KeyFile))
{
if (FileSystems.Default.FileExists(KeyFile))
keyFileForRead = TaskEnvironment.GetAbsolutePathIfValid(KeyFile, Log);
Comment thread
AlesProkop marked this conversation as resolved.
Outdated

if (keyFileForRead.Value != null && FileSystems.Default.FileExists(keyFileForRead))
Comment thread
AlesProkop marked this conversation as resolved.
Outdated
{
keyFileExists = true;
}
Expand Down Expand Up @@ -216,7 +229,7 @@ private bool ValidateStrongNameParameters()

try
{
StrongNameUtils.GetStrongNameKey(Log, KeyFile, KeyContainer, out keyPair, out publicKey);
StrongNameUtils.GetStrongNameKey(Log, keyFileForRead, KeyContainer, out keyPair, out publicKey);
}
catch (StrongNameException e)
{
Expand Down Expand Up @@ -260,6 +273,20 @@ private bool ValidateStrongNameParameters()
return true;
}

private bool DirectoryExists(string path)
{
Comment thread
AlesProkop marked this conversation as resolved.
Outdated
// Match the original Directory.Exists semantics: invalid paths return false rather than throwing.
AbsolutePath absolutePath = TaskEnvironment.GetAbsolutePathIfValid(path, Log);
return absolutePath.Value != null && FileSystems.Default.DirectoryExists(absolutePath);
Comment thread
AlesProkop marked this conversation as resolved.
Outdated
}

private bool FileInfoExists(string path)
{
// Match the original FileInfo semantics: null/empty/invalid paths throw rather than silently returning false.
AbsolutePath absolutePath = TaskEnvironment.GetAbsolutePathAllowEmpty(path);
return SdkToolsPathUtility.FileInfoExists(absolutePath);
}

#endregion // ToolTask Members
}
}
3 changes: 3 additions & 0 deletions src/Tasks/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,9 @@
<data name="General.FailedToCanonicalizePath">
<value>Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.</value>
</data>
<data name="General.FailedToAbsolutizePath">
<value>Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.</value>
</data>
<data name="General.FrameworksFileNotFound">
<value>MSB3082: Task failed because "{0}" was not found, or the .NET Framework {1} is not installed. Please install the .NET Framework {1}.</value>
<comment>{StrBegin="MSB3082: "}</comment>
Expand Down
8 changes: 7 additions & 1 deletion src/Tasks/Resources/xlf/Strings.cs.xlf

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

8 changes: 7 additions & 1 deletion src/Tasks/Resources/xlf/Strings.de.xlf

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

8 changes: 7 additions & 1 deletion src/Tasks/Resources/xlf/Strings.es.xlf

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

8 changes: 7 additions & 1 deletion src/Tasks/Resources/xlf/Strings.fr.xlf

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

8 changes: 7 additions & 1 deletion src/Tasks/Resources/xlf/Strings.it.xlf

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

8 changes: 7 additions & 1 deletion src/Tasks/Resources/xlf/Strings.ja.xlf

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

Loading