diff --git a/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs b/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs
index f40dd8d215d..c1f4849a2c5 100644
--- a/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs
+++ b/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs
@@ -8,6 +8,7 @@
using Microsoft.Build.Tasks;
using Microsoft.Build.Utilities;
using Microsoft.Runtime.Hosting;
+using Shouldly;
using Xunit;
#nullable disable
@@ -309,6 +310,74 @@ public void TaskFailsWhenImproperlySigned()
}
}
}
+
+ ///
+ /// 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.
+ ///
+ [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),
+ };
+
+ // Precondition: process CWD must differ from projectDir, otherwise this test
+ // would pass trivially without exercising the TaskEnvironment-based resolution.
+ Assert.NotEqual(projectDir, Environment.CurrentDirectory);
+
+ 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);
+ }
+
+ ///
+ /// 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.
+ ///
+ [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),
+ };
+
+ // Precondition: process CWD must differ from projectDir, otherwise this test
+ // would pass trivially without exercising the TaskEnvironment-based resolution.
+ Assert.NotEqual(projectDir, Environment.CurrentDirectory);
+
+ // 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
diff --git a/src/Tasks.UnitTests/GenerateLauncher_Tests.cs b/src/Tasks.UnitTests/GenerateLauncher_Tests.cs
new file mode 100644
index 00000000000..7c83c87b0ce
--- /dev/null
+++ b/src/Tasks.UnitTests/GenerateLauncher_Tests.cs
@@ -0,0 +1,106 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Tasks;
+using Microsoft.Build.UnitTests.Shared;
+using Microsoft.Build.Utilities;
+using Shouldly;
+using Xunit;
+
+#nullable disable
+
+namespace Microsoft.Build.UnitTests
+{
+ public sealed class GenerateLauncher_Tests
+ {
+ // MSB3964 is the unique code for the "GenerateLauncher.MissingLauncherExe" resource
+ // ("Could not find required file '{0}'"). Asserting on the code lets the test be
+ // independent of localized resource text.
+ private const string MissingLauncherExeCode = "MSB3964";
+
+ private readonly ITestOutputHelper _output;
+
+ public GenerateLauncher_Tests(ITestOutputHelper output)
+ {
+ _output = output;
+ }
+
+ ///
+ /// Proves that resolves a relative LauncherPath
+ /// against rather than against the
+ /// process-wide current directory. We place a file with a unique relative name inside
+ /// the project directory; if resolution went through CWD instead, the launcher would
+ /// not be found and the task would log MSB3964. The test does not assert on the final
+ /// outcome of the task (the placed file is not a real PE binary, so the subsequent
+ /// resource-update step will fail) — it only asserts on where the file was *looked
+ /// for*, which is exactly what the MT-safe contract requires.
+ ///
+ [WindowsOnlyFact]
+ public void RelativeLauncherPath_ResolvesAgainstProjectDirectory_NotCurrentDirectory()
+ {
+ using TestEnvironment env = TestEnvironment.Create(_output);
+ TransientTestFolder projectDir = env.CreateFolder();
+ TransientTestFolder outputDir = env.CreateFolder();
+
+ // Use a relative name that is extremely unlikely to exist anywhere on disk
+ // outside of projectDir, so a CWD-based resolution would fail.
+ const string relativeLauncherName = "GenerateLauncher_Tests_Probe.exe";
+ File.WriteAllBytes(Path.Combine(projectDir.Path, relativeLauncherName), [0]);
+
+ var engine = new MockEngine(_output);
+ var task = new GenerateLauncher
+ {
+ BuildEngine = engine,
+ TaskEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment(projectDir.Path),
+ LauncherPath = relativeLauncherName,
+ OutputPath = outputDir.Path,
+ EntryPoint = new TaskItem("App.exe"),
+ };
+
+ task.Execute();
+
+ // Resolution went through ProjectDirectory → the file was found → MSB3964 must NOT appear.
+ // A later step (resource update) will fail with a different code, which is fine for this test.
+ engine.AssertLogDoesntContain(MissingLauncherExeCode);
+ }
+
+ ///
+ /// Proves that user-facing diagnostic messages quote the path the caller supplied,
+ /// not the absolutized form produced internally. The test deliberately points
+ /// LauncherPath at a non-existent relative file so
+ /// logs MSB3964 ("Could not find required file '{0}'"), then asserts the message
+ /// contains the literal relative input but not the absolutized project-directory-
+ /// qualified path.
+ ///
+ [WindowsOnlyFact]
+ public void MissingLauncher_ErrorMessage_PreservesOriginalRelativePath()
+ {
+ using TestEnvironment env = TestEnvironment.Create(_output);
+ TransientTestFolder projectDir = env.CreateFolder();
+ TransientTestFolder outputDir = env.CreateFolder();
+
+ const string relativeLauncherName = "GenerateLauncher_Tests_DoesNotExist.exe";
+ string absolutizedLauncherPath = Path.Combine(projectDir.Path, relativeLauncherName);
+
+ var engine = new MockEngine(_output);
+ var task = new GenerateLauncher
+ {
+ BuildEngine = engine,
+ TaskEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment(projectDir.Path),
+ LauncherPath = relativeLauncherName,
+ OutputPath = outputDir.Path,
+ EntryPoint = new TaskItem("App.exe"),
+ };
+
+ bool result = task.Execute();
+
+ result.ShouldBeFalse();
+ engine.AssertLogContains(MissingLauncherExeCode);
+ // The message must quote the original input, not the absolutized form.
+ engine.AssertLogContains($"'{relativeLauncherName}'");
+ engine.AssertLogDoesntContain($"'{absolutizedLauncherPath}'");
+ }
+ }
+}
diff --git a/src/Tasks/AxImp.cs b/src/Tasks/AxImp.cs
index 14eeb123d8c..f4b049d0370 100644
--- a/src/Tasks/AxImp.cs
+++ b/src/Tasks/AxImp.cs
@@ -3,6 +3,8 @@
using System;
+using Microsoft.Build.Framework;
+
#nullable disable
namespace Microsoft.Build.Tasks
@@ -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.
///
+ [MSBuildMultiThreadableTask]
internal class AxImp : AxTlbBaseTask
{
#region Properties
diff --git a/src/Tasks/AxTlbBaseTask.cs b/src/Tasks/AxTlbBaseTask.cs
index e058cd21a99..6aa70ca5951 100644
--- a/src/Tasks/AxTlbBaseTask.cs
+++ b/src/Tasks/AxTlbBaseTask.cs
@@ -3,6 +3,7 @@
using System;
using System.Reflection;
+using Microsoft.Build.Framework;
using Microsoft.Build.Shared.FileSystem;
#nullable disable
@@ -107,14 +108,16 @@ protected internal override void AddCommandLineCommands(CommandLineBuilderExtens
protected override string GenerateFullPathToTool()
{
string pathToTool = SdkToolsPathUtility.GeneratePathToTool(
- SdkToolsPathUtility.FileInfoExists,
+ f => !string.IsNullOrEmpty(f)
+ ? SdkToolsPathUtility.FileInfoExists(TaskEnvironment.GetAbsolutePath(f))
+ : SdkToolsPathUtility.FileInfoExists(f),
Utilities.ProcessorArchitecture.CurrentProcessArchitecture,
SdkToolsPath,
ToolName,
Log,
true);
- return pathToTool;
+ return string.IsNullOrEmpty(pathToTool) ? pathToTool : TaskEnvironment.GetAbsolutePath(pathToTool).Value;
}
///
@@ -124,9 +127,12 @@ protected override string GenerateFullPathToTool()
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)))
+ // we'll worry about that later. ToolPath and SdkToolsPath are alternative locations;
+ // an unset (null/empty) value just means "not configured" and is not an error on its
+ // own -- only failing to find the tool in *either* location is. A non-empty path that
+ // cannot be absolutized (e.g. contains illegal characters) is unusable, so we log a
+ // low-importance diagnostic to make the swallowed failure discoverable.
+ if (!IsToolDirectoryConfigured(ToolPath) && !IsToolDirectoryConfigured(SdkToolsPath))
{
Log.LogErrorWithCodeFromResources("AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", SdkToolsPath ?? "", ToolPath ?? "");
return false;
@@ -141,6 +147,27 @@ protected override bool ValidateParameters()
return false;
}
+ private bool IsToolDirectoryConfigured(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ return false;
+ }
+
+ AbsolutePath absolutePath;
+ try
+ {
+ absolutePath = TaskEnvironment.GetAbsolutePath(path);
+ }
+ catch (ArgumentException e)
+ {
+ Log.LogMessageFromResources(MessageImportance.Low, "General.FailedToAbsolutizePath", path, e.Message);
+ return false;
+ }
+
+ return FileSystems.Default.DirectoryExists(absolutePath);
+ }
+
///
/// Adds options involving strong name signing -- syntax is the same between
/// AxImp and TlbImp
@@ -155,6 +182,7 @@ private void AddStrongNameOptions(CommandLineBuilderExtension commandLine)
// throw an error.
//
// So use /publickey if that's all our KeyFile contains, but KeyFile otherwise.
+ // Relative paths are allowed in the KeyFile path.
if (_delaySigningAndKeyFileOnlyContainsPublicKey)
{
commandLine.AppendSwitchIfNotNull("/publickey:", KeyFile);
@@ -175,12 +203,16 @@ 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))
+ AbsolutePath absolutePathFromKeyFile = TaskEnvironment.GetAbsolutePath(KeyFile);
+
+ if (FileSystems.Default.FileExists(absolutePathFromKeyFile))
{
+ keyFileForRead = absolutePathFromKeyFile;
keyFileExists = true;
}
else
@@ -216,7 +248,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)
{
diff --git a/src/Tasks/Resources/Strings.resx b/src/Tasks/Resources/Strings.resx
index 940e9aec7f5..5f8847208ed 100644
--- a/src/Tasks/Resources/Strings.resx
+++ b/src/Tasks/Resources/Strings.resx
@@ -503,6 +503,9 @@
Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
MSB3082: Task failed because "{0}" was not found, or the .NET Framework {1} is not installed. Please install the .NET Framework {1}.{StrBegin="MSB3082: "}
diff --git a/src/Tasks/Resources/xlf/Strings.cs.xlf b/src/Tasks/Resources/xlf/Strings.cs.xlf
index e8418cd6ead..5d283220dbf 100644
--- a/src/Tasks/Resources/xlf/Strings.cs.xlf
+++ b/src/Tasks/Resources/xlf/Strings.cs.xlf
@@ -619,6 +619,11 @@
Očekávaný soubor {0} neexistuje.
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.Nepodařilo se normalizovat cestu „{0}“ překladem segmentů „.“ a „..“: {1}. Cesta bude použita tak, jak je.
diff --git a/src/Tasks/Resources/xlf/Strings.de.xlf b/src/Tasks/Resources/xlf/Strings.de.xlf
index cb9eddd59f5..7dae52c56b4 100644
--- a/src/Tasks/Resources/xlf/Strings.de.xlf
+++ b/src/Tasks/Resources/xlf/Strings.de.xlf
@@ -619,6 +619,11 @@
Die erwartete Datei "{0}" ist nicht vorhanden.
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.Der Pfad „{0}“ konnte nicht normalisiert werden, indem die Segmente „.“ und „..“ aufgelöst wurden: {1}. Der Pfad wird wie besehen verwendet.
diff --git a/src/Tasks/Resources/xlf/Strings.es.xlf b/src/Tasks/Resources/xlf/Strings.es.xlf
index cf7afc6f52e..530ae3c3d5b 100644
--- a/src/Tasks/Resources/xlf/Strings.es.xlf
+++ b/src/Tasks/Resources/xlf/Strings.es.xlf
@@ -619,6 +619,11 @@
El archivo esperado "{0}" no existe.
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.No se pudo normalizar la ruta "{0}" al resolver los segmentos "." y "..": {1}. La ruta de acceso se usará tal cual.
diff --git a/src/Tasks/Resources/xlf/Strings.fr.xlf b/src/Tasks/Resources/xlf/Strings.fr.xlf
index 693730ba9b1..c8b0f50c26d 100644
--- a/src/Tasks/Resources/xlf/Strings.fr.xlf
+++ b/src/Tasks/Resources/xlf/Strings.fr.xlf
@@ -619,6 +619,11 @@
Le fichier attendu "{0}" n'existe pas.
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.Nous n’avons pas pu normaliser le chemin d’accès « {0} » en résolvant les segments « . » et « .. » : {1}. Le chemin d’accès sera utilisé tel quel.
diff --git a/src/Tasks/Resources/xlf/Strings.it.xlf b/src/Tasks/Resources/xlf/Strings.it.xlf
index bc617fd2b16..70d16a88950 100644
--- a/src/Tasks/Resources/xlf/Strings.it.xlf
+++ b/src/Tasks/Resources/xlf/Strings.it.xlf
@@ -619,6 +619,11 @@
Il file previsto "{0}" non esiste.
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.Non è possibile normalizzare il percorso "{0}" risolvendo i segmenti "." e "..": {1}. Il percorso verrà usato così com'è.
diff --git a/src/Tasks/Resources/xlf/Strings.ja.xlf b/src/Tasks/Resources/xlf/Strings.ja.xlf
index 242daf2960e..c10ceb42577 100644
--- a/src/Tasks/Resources/xlf/Strings.ja.xlf
+++ b/src/Tasks/Resources/xlf/Strings.ja.xlf
@@ -619,6 +619,11 @@
指定されたファイル "{0}" は存在しません。
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is."." および ".." セグメントを解決してパス "{0}" を正規化できませんでした: {1}。パスはそのまま使用されます。
diff --git a/src/Tasks/Resources/xlf/Strings.ko.xlf b/src/Tasks/Resources/xlf/Strings.ko.xlf
index c1ec9553d72..c57259e114a 100644
--- a/src/Tasks/Resources/xlf/Strings.ko.xlf
+++ b/src/Tasks/Resources/xlf/Strings.ko.xlf
@@ -619,6 +619,11 @@
필요한 "{0}" 파일이 없습니다.
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is."." 및 ".." 세그먼트를 확인하여 경로 "{0}"을(를) 정규화할 수 없습니다. {1}. 경로가 있는 그대로 사용됩니다.
diff --git a/src/Tasks/Resources/xlf/Strings.pl.xlf b/src/Tasks/Resources/xlf/Strings.pl.xlf
index d63de0b6552..01f57424240 100644
--- a/src/Tasks/Resources/xlf/Strings.pl.xlf
+++ b/src/Tasks/Resources/xlf/Strings.pl.xlf
@@ -619,6 +619,11 @@
Oczekiwany plik „{0}” nie istnieje.
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.Nie można znormalizować ścieżki „{0}”, rozpoznając segmenty „.” i „.”: {1}. Ścieżka będzie używana w stanie, w jakim jest.
diff --git a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf
index 2fa19530253..66defa09128 100644
--- a/src/Tasks/Resources/xlf/Strings.pt-BR.xlf
+++ b/src/Tasks/Resources/xlf/Strings.pt-BR.xlf
@@ -619,6 +619,11 @@
O arquivo esperado "{0}" não existe.
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.Não foi possível normalizar o caminho "{0}" resolvendo segmentos "." e ".": {1}. O caminho será usado no estado em que se encontra.
diff --git a/src/Tasks/Resources/xlf/Strings.ru.xlf b/src/Tasks/Resources/xlf/Strings.ru.xlf
index 944cd25126d..ee3a54cbd9f 100644
--- a/src/Tasks/Resources/xlf/Strings.ru.xlf
+++ b/src/Tasks/Resources/xlf/Strings.ru.xlf
@@ -619,6 +619,11 @@
Ожидаемый файл "{0}" не существует.
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.Не удалось нормализовать путь "{0}", разрешив сегменты "." и "..": {1}. Путь будет использоваться "как есть".
diff --git a/src/Tasks/Resources/xlf/Strings.tr.xlf b/src/Tasks/Resources/xlf/Strings.tr.xlf
index 178382f87ac..8c82fa00c3b 100644
--- a/src/Tasks/Resources/xlf/Strings.tr.xlf
+++ b/src/Tasks/Resources/xlf/Strings.tr.xlf
@@ -619,6 +619,11 @@
Beklenen "{0}" dosyası yok.
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is."." ve ".." segmentleri çözümlenerek "{0}" yolu normalleştirilemedi: {1}. Yol olduğu gibi kullanılacak.
diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf
index e0bdfdb8905..ffa6a9c6eb9 100644
--- a/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf
+++ b/src/Tasks/Resources/xlf/Strings.zh-Hans.xlf
@@ -619,6 +619,11 @@
所需文件“{0}”不存在。
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.无法通过解析 "." 和 ".." 段来规范化路径“{0}”: {1}。将按原样使用该路径。
diff --git a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf
index fbf4154a011..802a1afd2f6 100644
--- a/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf
+++ b/src/Tasks/Resources/xlf/Strings.zh-Hant.xlf
@@ -619,6 +619,11 @@
預期的檔案 "{0}" 不存在。
+
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+ Could not convert path "{0}" to an absolute path: {1}. The path will be treated as invalid.
+
+ Could not normalize path "{0}" by resolving "." and ".." segments: {1}. The path will be used as-is.無法透過解析 "." 和 ".." 區段來正常化路徑 "{0}": {1}。路徑將維持現況使用。
diff --git a/src/Tasks/StrongNameUtils.cs b/src/Tasks/StrongNameUtils.cs
index ea3e0ab1a87..bafc2dafcfb 100644
--- a/src/Tasks/StrongNameUtils.cs
+++ b/src/Tasks/StrongNameUtils.cs
@@ -6,6 +6,7 @@
using System.Reflection;
using System.Reflection.PortableExecutable;
using System.Security;
+using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Utilities;
@@ -33,6 +34,14 @@ internal static class StrongNameUtils
/// Reads contents of a key file. Reused from vsdesigner code.
///
internal static void ReadKeyFile(TaskLoggingHelper log, string keyFile, out StrongNameKeyPair keyPair, out byte[] publicKey)
+ {
+ ReadKeyFile(log, new AbsolutePath(keyFile, ignoreRootedCheck: true), out keyPair, out publicKey);
+ }
+
+ ///
+ /// Reads contents of a key file. Reused from vsdesigner code.
+ ///
+ internal static void ReadKeyFile(TaskLoggingHelper log, AbsolutePath keyFile, out StrongNameKeyPair keyPair, out byte[] publicKey)
{
// Initialize parameters
keyPair = null;
@@ -54,19 +63,19 @@ internal static void ReadKeyFile(TaskLoggingHelper log, string keyFile, out Stro
}
catch (ArgumentException e)
{
- log.LogErrorWithCodeFromResources("StrongNameUtils.KeyFileReadFailure", keyFile);
+ log.LogErrorWithCodeFromResources("StrongNameUtils.KeyFileReadFailure", keyFile.OriginalValue);
log.LogErrorFromException(e);
throw new StrongNameException(e);
}
catch (IOException e)
{
- log.LogErrorWithCodeFromResources("StrongNameUtils.KeyFileReadFailure", keyFile);
+ log.LogErrorWithCodeFromResources("StrongNameUtils.KeyFileReadFailure", keyFile.OriginalValue);
log.LogErrorFromException(e);
throw new StrongNameException(e);
}
catch (SecurityException e)
{
- log.LogErrorWithCodeFromResources("StrongNameUtils.KeyFileReadFailure", keyFile);
+ log.LogErrorWithCodeFromResources("StrongNameUtils.KeyFileReadFailure", keyFile.OriginalValue);
log.LogErrorFromException(e);
throw new StrongNameException(e);
}
@@ -126,6 +135,13 @@ internal static void GetStrongNameKey(TaskLoggingHelper log, string keyFile, str
}
}
+ ///
+ /// overload of ,
+ /// for callers that have already resolved the key file to an absolute path.
+ ///
+ internal static void GetStrongNameKey(TaskLoggingHelper log, AbsolutePath keyFile, string keyContainer, out StrongNameKeyPair keyPair, out byte[] publicKey)
+ => GetStrongNameKey(log, keyFile.Value, keyContainer, out keyPair, out publicKey);
+
///
/// Given an assembly path, determine if the assembly is [delay] signed or not.
///
diff --git a/src/Tasks/TaskEnvironmentExtensions.cs b/src/Tasks/TaskEnvironmentExtensions.cs
index fbfa204da53..e32ae831ef4 100644
--- a/src/Tasks/TaskEnvironmentExtensions.cs
+++ b/src/Tasks/TaskEnvironmentExtensions.cs
@@ -58,6 +58,7 @@ internal static AbsolutePath GetCanonicalFormNoThrow(this AbsolutePath absoluteP
}
///
+ /// Converts an array of to a string array.
/// Returns the absolute path string for , or the input unchanged
/// when it is null or empty. Useful for tolerating optional path inputs that callers may
/// later combine with other path segments.
diff --git a/src/Tasks/TlbImp.cs b/src/Tasks/TlbImp.cs
index db367d81977..26433bbe5ed 100644
--- a/src/Tasks/TlbImp.cs
+++ b/src/Tasks/TlbImp.cs
@@ -3,6 +3,8 @@
using System;
+using Microsoft.Build.Framework;
+
#nullable disable
namespace Microsoft.Build.Tasks
@@ -40,6 +42,7 @@ internal enum TlbImpTransformFlags
/// Defines the "TlbImp" MSBuild task, which enables using TlbImp.exe
/// to generate assemblies from type libraries.
///
+ [MSBuildMultiThreadableTask]
internal class TlbImp : AxTlbBaseTask
{
#region Properties