diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandPermission.cs b/src/Microsoft.DotNet.Cli.Utils/CommandPermission.cs
new file mode 100644
index 000000000..d26d2f640
--- /dev/null
+++ b/src/Microsoft.DotNet.Cli.Utils/CommandPermission.cs
@@ -0,0 +1,29 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Runtime.InteropServices;
+
+namespace Microsoft.DotNet.Cli.Utils
+{
+ internal class FilePermissionSetter : IFilePermissionSetter
+ {
+ public void SetUserExecutionPermission(string path)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return;
+ }
+
+ CommandResult result = new CommandFactory()
+ .Create("chmod", new[] { "u+x", path })
+ .CaptureStdOut()
+ .CaptureStdErr()
+ .Execute();
+
+ if (result.ExitCode != 0)
+ {
+ throw new FilePermissionSettingException(result.StdErr);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.DotNet.Cli.Utils/FilePermissionSettingException.cs b/src/Microsoft.DotNet.Cli.Utils/FilePermissionSettingException.cs
new file mode 100644
index 000000000..b7aaaaba8
--- /dev/null
+++ b/src/Microsoft.DotNet.Cli.Utils/FilePermissionSettingException.cs
@@ -0,0 +1,28 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Runtime.Serialization;
+
+namespace Microsoft.DotNet.Cli.Utils
+{
+ [Serializable]
+ internal class FilePermissionSettingException : Exception
+ {
+ public FilePermissionSettingException()
+ {
+ }
+
+ public FilePermissionSettingException(string message) : base(message)
+ {
+ }
+
+ public FilePermissionSettingException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ protected FilePermissionSettingException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/Microsoft.DotNet.Cli.Utils/IFilePermissionSetter.cs b/src/Microsoft.DotNet.Cli.Utils/IFilePermissionSetter.cs
new file mode 100644
index 000000000..9753fc232
--- /dev/null
+++ b/src/Microsoft.DotNet.Cli.Utils/IFilePermissionSetter.cs
@@ -0,0 +1,7 @@
+namespace Microsoft.DotNet.Cli.Utils
+{
+ internal interface IFilePermissionSetter
+ {
+ void SetUserExecutionPermission(string path);
+ }
+}
diff --git a/src/Microsoft.DotNet.InternalAbstractions/FileWrapper.cs b/src/Microsoft.DotNet.InternalAbstractions/FileWrapper.cs
index 512037f33..c46f8e37a 100644
--- a/src/Microsoft.DotNet.InternalAbstractions/FileWrapper.cs
+++ b/src/Microsoft.DotNet.InternalAbstractions/FileWrapper.cs
@@ -51,6 +51,11 @@ namespace Microsoft.Extensions.EnvironmentAbstractions
File.Move(source, destination);
}
+ public void Copy(string sourceFileName, string destFileName)
+ {
+ File.Copy(sourceFileName, destFileName);
+ }
+
public void Delete(string path)
{
File.Delete(path);
diff --git a/src/Microsoft.DotNet.InternalAbstractions/IFile.cs b/src/Microsoft.DotNet.InternalAbstractions/IFile.cs
index a04f70dcd..044297b6e 100644
--- a/src/Microsoft.DotNet.InternalAbstractions/IFile.cs
+++ b/src/Microsoft.DotNet.InternalAbstractions/IFile.cs
@@ -27,6 +27,8 @@ namespace Microsoft.Extensions.EnvironmentAbstractions
void Move(string source, string destination);
+ void Copy(string source, string destination);
+
void Delete(string path);
}
}
diff --git a/src/dotnet/CommonLocalizableStrings.resx b/src/dotnet/CommonLocalizableStrings.resx
index ad89d2c03..f4c170343 100644
--- a/src/dotnet/CommonLocalizableStrings.resx
+++ b/src/dotnet/CommonLocalizableStrings.resx
@@ -640,4 +640,10 @@ setx PATH "%PATH%;{0}"
Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.
+
+ More than one packaged shim is available: {0}.
+
+
+ Failed to read NuGet LockFile for tool package '{0}': {1}
+
diff --git a/src/dotnet/ShellShim/AppHostShimMaker.cs b/src/dotnet/ShellShim/AppHostShimMaker.cs
new file mode 100644
index 000000000..9b5eaf2de
--- /dev/null
+++ b/src/dotnet/ShellShim/AppHostShimMaker.cs
@@ -0,0 +1,53 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.IO;
+using System.Runtime.InteropServices;
+using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.PlatformAbstractions;
+using Microsoft.DotNet.Tools.Common;
+using Microsoft.Extensions.EnvironmentAbstractions;
+
+namespace Microsoft.DotNet.ShellShim
+{
+ internal class AppHostShellShimMaker : IAppHostShellShimMaker
+ {
+ private const string ApphostNameWithoutExtension = "apphost";
+ private readonly string _appHostSourceDirectory;
+ private readonly IFilePermissionSetter _filePermissionSetter;
+
+ public AppHostShellShimMaker(string appHostSourceDirectory = null, IFilePermissionSetter filePermissionSetter = null)
+ {
+ _appHostSourceDirectory =
+ appHostSourceDirectory
+ ?? Path.Combine(ApplicationEnvironment.ApplicationBasePath, "AppHostTemplate");
+
+ _filePermissionSetter =
+ filePermissionSetter
+ ?? new FilePermissionSetter();
+ }
+
+ public void CreateApphostShellShim(FilePath entryPoint, FilePath shimPath)
+ {
+ string appHostSourcePath;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension + ".exe");
+ }
+ else
+ {
+ appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension);
+ }
+
+ var appHostDestinationFilePath = shimPath.Value;
+ var appBinaryFilePath = PathUtility.GetRelativePath(appHostDestinationFilePath, entryPoint.Value);
+
+ EmbedAppNameInHost.EmbedAndReturnModifiedAppHostPath(
+ appHostSourceFilePath: appHostSourcePath,
+ appHostDestinationFilePath: appHostDestinationFilePath,
+ appBinaryFilePath: appBinaryFilePath);
+
+ _filePermissionSetter.SetUserExecutionPermission(appHostDestinationFilePath);
+ }
+ }
+}
diff --git a/src/dotnet/ShellShim/IApphostShellShimMaker.cs b/src/dotnet/ShellShim/IApphostShellShimMaker.cs
new file mode 100644
index 000000000..962e68881
--- /dev/null
+++ b/src/dotnet/ShellShim/IApphostShellShimMaker.cs
@@ -0,0 +1,9 @@
+using Microsoft.Extensions.EnvironmentAbstractions;
+
+namespace Microsoft.DotNet.ShellShim
+{
+ internal interface IAppHostShellShimMaker
+ {
+ void CreateApphostShellShim(FilePath entryPoint, FilePath shimPath);
+ }
+}
diff --git a/src/dotnet/ShellShim/IShellShimRepository.cs b/src/dotnet/ShellShim/IShellShimRepository.cs
index 737b548df..e8049fa6e 100644
--- a/src/dotnet/ShellShim/IShellShimRepository.cs
+++ b/src/dotnet/ShellShim/IShellShimRepository.cs
@@ -1,12 +1,13 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System.Collections.Generic;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.ShellShim
{
internal interface IShellShimRepository
{
- void CreateShim(FilePath targetExecutablePath, string commandName);
+ void CreateShim(FilePath targetExecutablePath, string commandName, IReadOnlyList packagedShims = null);
void RemoveShim(string commandName);
}
diff --git a/src/dotnet/ShellShim/ShellShimRepository.cs b/src/dotnet/ShellShim/ShellShimRepository.cs
index a7b890525..121582cf6 100644
--- a/src/dotnet/ShellShim/ShellShimRepository.cs
+++ b/src/dotnet/ShellShim/ShellShimRepository.cs
@@ -21,16 +21,24 @@ namespace Microsoft.DotNet.ShellShim
private const string ApphostNameWithoutExtension = "apphost";
private readonly DirectoryPath _shimsDirectory;
- private readonly string _appHostSourceDirectory;
+ private readonly IFileSystem _fileSystem;
+ private readonly IAppHostShellShimMaker _appHostShellShimMaker;
+ private readonly IFilePermissionSetter _filePermissionSetter;
- public ShellShimRepository(DirectoryPath shimsDirectory, string appHostSourcePath = null)
+ public ShellShimRepository(
+ DirectoryPath shimsDirectory,
+ string appHostSourceDirectory = null,
+ IFileSystem fileSystem = null,
+ IAppHostShellShimMaker appHostShellShimMaker = null,
+ IFilePermissionSetter filePermissionSetter = null)
{
_shimsDirectory = shimsDirectory;
- _appHostSourceDirectory = appHostSourcePath ?? Path.Combine(ApplicationEnvironment.ApplicationBasePath,
- "AppHostTemplate");
+ _fileSystem = fileSystem ?? new FileSystemWrapper();
+ _appHostShellShimMaker = appHostShellShimMaker ?? new AppHostShellShimMaker(appHostSourceDirectory: appHostSourceDirectory);
+ _filePermissionSetter = filePermissionSetter ?? new FilePermissionSetter();
}
- public void CreateShim(FilePath targetExecutablePath, string commandName)
+ public void CreateShim(FilePath targetExecutablePath, string commandName, IReadOnlyList packagedShims = null)
{
if (string.IsNullOrEmpty(targetExecutablePath.Value))
{
@@ -54,19 +62,27 @@ namespace Microsoft.DotNet.ShellShim
{
try
{
- if (!Directory.Exists(_shimsDirectory.Value))
+ if (!_fileSystem.Directory.Exists(_shimsDirectory.Value))
{
- Directory.CreateDirectory(_shimsDirectory.Value);
+ _fileSystem.Directory.CreateDirectory(_shimsDirectory.Value);
}
- CreateApphostShim(
- commandName,
- entryPoint: targetExecutablePath);
-
- if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ if (TryGetPackagedShim(packagedShims, commandName, out FilePath? packagedShim))
{
- SetUserExecutionPermission(GetShimPath(commandName));
+ _fileSystem.File.Copy(packagedShim.Value.Value, GetShimPath(commandName).Value);
+ _filePermissionSetter.SetUserExecutionPermission(GetShimPath(commandName).Value);
}
+ else
+ {
+ _appHostShellShimMaker.CreateApphostShellShim(
+ targetExecutablePath,
+ GetShimPath(commandName));
+ }
+ }
+ catch (FilePermissionSettingException ex)
+ {
+ throw new ShellShimException(
+ string.Format(CommonLocalizableStrings.FailedSettingShimPermissions, ex.Message));
}
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
{
@@ -80,7 +96,7 @@ namespace Microsoft.DotNet.ShellShim
}
},
rollback: () => {
- foreach (var file in GetShimFiles(commandName).Where(f => File.Exists(f.Value)))
+ foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value)))
{
File.Delete(file.Value);
}
@@ -94,10 +110,10 @@ namespace Microsoft.DotNet.ShellShim
action: () => {
try
{
- foreach (var file in GetShimFiles(commandName).Where(f => File.Exists(f.Value)))
+ foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value)))
{
var tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
- File.Move(file.Value, tempPath);
+ _fileSystem.File.Move(file.Value, tempPath);
files[file.Value] = tempPath;
}
}
@@ -115,38 +131,17 @@ namespace Microsoft.DotNet.ShellShim
commit: () => {
foreach (var value in files.Values)
{
- File.Delete(value);
+ _fileSystem.File.Delete(value);
}
},
rollback: () => {
foreach (var kvp in files)
{
- File.Move(kvp.Value, kvp.Key);
+ _fileSystem.File.Move(kvp.Value, kvp.Key);
}
});
}
- private void CreateApphostShim(string commandName, FilePath entryPoint)
- {
- string appHostSourcePath;
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension + ".exe");
- }
- else
- {
- appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension);
- }
-
- var appHostDestinationFilePath = GetShimPath(commandName).Value;
- var appBinaryFilePath = PathUtility.GetRelativePath(appHostDestinationFilePath, entryPoint.Value);
-
- EmbedAppNameInHost.EmbedAndReturnModifiedAppHostPath(
- appHostSourceFilePath: appHostSourcePath,
- appHostDestinationFilePath: appHostDestinationFilePath,
- appBinaryFilePath: appBinaryFilePath);
- }
-
private class StartupOptions
{
public string appRoot { get; set; }
@@ -159,7 +154,7 @@ namespace Microsoft.DotNet.ShellShim
private bool ShimExists(string commandName)
{
- return GetShimFiles(commandName).Any(p => File.Exists(p.Value));
+ return GetShimFiles(commandName).Any(p => _fileSystem.File.Exists(p.Value));
}
private IEnumerable GetShimFiles(string commandName)
@@ -184,24 +179,37 @@ namespace Microsoft.DotNet.ShellShim
}
}
- private static void SetUserExecutionPermission(FilePath path)
+ private bool TryGetPackagedShim(
+ IReadOnlyList packagedShims,
+ string commandName,
+ out FilePath? packagedShim)
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ packagedShim = null;
+
+ if (packagedShims != null && packagedShims.Count > 0)
{
- return;
+ FilePath[] candidatepackagedShim =
+ packagedShims
+ .Where(s => string.Equals(
+ Path.GetFileName(s.Value),
+ Path.GetFileName(GetShimPath(commandName).Value))).ToArray();
+
+ if (candidatepackagedShim.Length > 1)
+ {
+ throw new ShellShimException(
+ string.Format(
+ CommonLocalizableStrings.MoreThanOnePackagedShimAvailable,
+ string.Join(';', candidatepackagedShim)));
+ }
+
+ if (candidatepackagedShim.Length == 1)
+ {
+ packagedShim = candidatepackagedShim.Single();
+ return true;
+ }
}
- CommandResult result = new CommandFactory()
- .Create("chmod", new[] { "u+x", path.Value })
- .CaptureStdOut()
- .CaptureStdErr()
- .Execute();
-
- if (result.ExitCode != 0)
- {
- throw new ShellShimException(
- string.Format(CommonLocalizableStrings.FailedSettingShimPermissions, result.StdErr));
- }
+ return false;
}
}
}
diff --git a/src/dotnet/ToolPackage/IToolPackage.cs b/src/dotnet/ToolPackage/IToolPackage.cs
index ebf351487..0b0a16c1e 100644
--- a/src/dotnet/ToolPackage/IToolPackage.cs
+++ b/src/dotnet/ToolPackage/IToolPackage.cs
@@ -20,6 +20,8 @@ namespace Microsoft.DotNet.ToolPackage
IEnumerable Warnings { get; }
+ IReadOnlyList PackagedShims { get; }
+
void Uninstall();
}
}
diff --git a/src/dotnet/ToolPackage/LockFileMatchChecker.cs b/src/dotnet/ToolPackage/LockFileMatchChecker.cs
index efa4af6b6..03d0a6db5 100644
--- a/src/dotnet/ToolPackage/LockFileMatchChecker.cs
+++ b/src/dotnet/ToolPackage/LockFileMatchChecker.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System.Collections.Generic;
using System.Linq;
using NuGet.ProjectModel;
@@ -22,36 +23,60 @@ namespace Microsoft.DotNet.ToolPackage
string[] entryPointPathInArray = SplitPathByDirectorySeparator(targetRelativeFilePath);
return entryPointPathInArray.Length >= 1
- && PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder()
- && SubPathMatchesTargetFilePath();
+ && PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder(
+ pathInLockFilePathInArray,
+ entryPointPathInArray)
+ && SubPathMatchesTargetFilePath(pathInLockFilePathInArray, entryPointPathInArray);
+ }
- bool SubPathMatchesTargetFilePath()
+ ///
+ /// Check if LockFileItem is under targetRelativePath directory.
+ /// The path in LockFileItem is in pattern tools/TFM/RID/my/tool.dll. Tools/TFM/RID is selected by NuGet.
+ /// And there will be only one TFM/RID combination.
+ /// When "my/folder/of/tool/tools.dll" part under targetRelativePath "my/folder/of" or "my/folder", return true.
+ ///
+ internal static bool MatchesDirectoryPath(LockFileItem lockFileItem, string targetRelativePath)
+ {
+ string[] pathInLockFilePathInArray = SplitPathByDirectorySeparator(lockFileItem.Path);
+ string[] targetDirectoryPathInArray = SplitPathByDirectorySeparator(targetRelativePath);
+
+ return pathInLockFilePathInArray[0] == "tools"
+ && SubPathMatchesTargetFilePath(pathInLockFilePathInArray, targetDirectoryPathInArray);
+ }
+
+ private static bool SubPathMatchesTargetFilePath(string[] pathInLockFilePathInArray, string[] targetInArray)
+ {
+ string[] pathAfterToolsTfmRid = pathInLockFilePathInArray.Skip(3).ToArray();
+ return !targetInArray
+ .Where((directoryOnEveryLevel, i) => directoryOnEveryLevel != pathAfterToolsTfmRid[i])
+ .Any();
+ }
+
+ private static bool PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder(
+ string[] pathInLockFilePathInArray,
+ string[] targetInArray)
+ {
+ if (pathInLockFilePathInArray.Length - targetInArray.Length != 3)
{
- string[] pathAfterToolsTfmRid = pathInLockFilePathInArray.Skip(3).ToArray();
- return !pathAfterToolsTfmRid
- .Where((directoryOnEveryLevel, i) => directoryOnEveryLevel != entryPointPathInArray[i])
- .Any();
+ return false;
}
- bool PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder()
+ if (pathInLockFilePathInArray[0] != "tools")
{
- if (pathInLockFilePathInArray.Length - entryPointPathInArray.Length != 3)
- {
- return false;
- }
-
- if (pathInLockFilePathInArray[0] != "tools")
- {
- return false;
- }
-
- return true;
+ return false;
}
- string[] SplitPathByDirectorySeparator(string path)
+ return true;
+ }
+
+ private static string[] SplitPathByDirectorySeparator(string path)
+ {
+ if (string.IsNullOrEmpty(path))
{
- return path.Split('\\', '/');
+ return new string[0];
}
+
+ return path.Split('\\', '/');
}
}
}
diff --git a/src/dotnet/ToolPackage/ToolPackageInstance.cs b/src/dotnet/ToolPackage/ToolPackageInstance.cs
index 4dd9733fb..c8be8d5ca 100644
--- a/src/dotnet/ToolPackage/ToolPackageInstance.cs
+++ b/src/dotnet/ToolPackage/ToolPackageInstance.cs
@@ -7,13 +7,17 @@ using Microsoft.DotNet.Tools;
using Microsoft.Extensions.EnvironmentAbstractions;
using NuGet.ProjectModel;
using NuGet.Versioning;
+using Microsoft.DotNet.Cli.Utils;
namespace Microsoft.DotNet.ToolPackage
{
// This is named "ToolPackageInstance" because "ToolPackage" would conflict with the namespace
internal class ToolPackageInstance : IToolPackage
{
+ private const string PackagedShimsDirectoryConvention = "shims";
+
public IEnumerable Warnings => _toolConfiguration.Value.Warnings;
+
public PackageId Id { get; private set; }
public NuGetVersion Version { get; private set; }
@@ -28,12 +32,21 @@ namespace Microsoft.DotNet.ToolPackage
}
}
+ public IReadOnlyList PackagedShims
+ {
+ get
+ {
+ return _packagedShims.Value;
+ }
+ }
+
private const string AssetsFileName = "project.assets.json";
private const string ToolSettingsFileName = "DotnetToolSettings.xml";
private IToolPackageStore _store;
private Lazy> _commands;
private Lazy _toolConfiguration;
+ private Lazy> _packagedShims;
public ToolPackageInstance(
IToolPackageStore store,
@@ -43,6 +56,7 @@ namespace Microsoft.DotNet.ToolPackage
{
_store = store ?? throw new ArgumentNullException(nameof(store));
_commands = new Lazy>(GetCommands);
+ _packagedShims = new Lazy>(GetPackagedShims);
Id = id;
Version = version ?? throw new ArgumentNullException(nameof(version));
@@ -107,11 +121,11 @@ namespace Microsoft.DotNet.ToolPackage
try
{
var commands = new List();
- var lockFile = new LockFileFormat().Read(PackageDirectory.WithFile(AssetsFileName).Value);
- var library = FindLibraryInLockFile(lockFile);
+ LockFile lockFile = new LockFileFormat().Read(PackageDirectory.WithFile(AssetsFileName).Value);
+ LockFileTargetLibrary library = FindLibraryInLockFile(lockFile);
ToolConfiguration configuration = _toolConfiguration.Value;
- var entryPointFromLockFile = FindItemInTargetLibrary(library, configuration.ToolAssemblyEntryPoint);
+ LockFileItem entryPointFromLockFile = FindItemInTargetLibrary(library, configuration.ToolAssemblyEntryPoint);
if (entryPointFromLockFile == null)
{
throw new ToolConfigurationException(
@@ -125,11 +139,7 @@ namespace Microsoft.DotNet.ToolPackage
commands.Add(new CommandSettings(
configuration.CommandName,
"dotnet",
- PackageDirectory
- .WithSubDirectories(
- Id.ToString(),
- library.Version.ToNormalizedString())
- .WithFile(entryPointFromLockFile.Path)));
+ LockFileRelativePathToFullFilePath(entryPointFromLockFile.Path, library)));
return commands;
}
@@ -143,6 +153,15 @@ namespace Microsoft.DotNet.ToolPackage
}
}
+ private FilePath LockFileRelativePathToFullFilePath(string lockFileRelativePath, LockFileTargetLibrary library)
+ {
+ return PackageDirectory
+ .WithSubDirectories(
+ Id.ToString(),
+ library.Version.ToNormalizedString())
+ .WithFile(lockFileRelativePath);
+ }
+
private ToolConfiguration GetToolConfiguration()
{
try
@@ -161,6 +180,55 @@ namespace Microsoft.DotNet.ToolPackage
}
}
+ private IReadOnlyList GetPackagedShims()
+ {
+ LockFileTargetLibrary library;
+ try
+ {
+ LockFile lockFile = new LockFileFormat().Read(PackageDirectory.WithFile(AssetsFileName).Value);
+ library = FindLibraryInLockFile(lockFile);
+ }
+ catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
+ {
+ throw new ToolPackageException(
+ string.Format(
+ CommonLocalizableStrings.FailedToReadNuGetLockFile,
+ Id,
+ ex.Message),
+ ex);
+ }
+
+ IEnumerable filesUnderShimsDirectory = library
+ ?.ToolsAssemblies
+ ?.Where(t => LockFileMatcher.MatchesDirectoryPath(t, PackagedShimsDirectoryConvention));
+
+ if (filesUnderShimsDirectory == null)
+ {
+ return Array.Empty();
+ }
+
+ IEnumerable allAvailableShimRuntimeIdentifiers = filesUnderShimsDirectory
+ .Select(f => f.Path.Split('\\', '/')?[4]) // ex: "tools/netcoreapp2.1/any/shims/osx-x64/demo" osx-x64 is at [4]
+ .Where(f => !string.IsNullOrEmpty(f));
+
+ if (new FrameworkDependencyFile().TryGetMostFitRuntimeIdentifier(
+ DotnetFiles.VersionFileObject.BuildRid,
+ allAvailableShimRuntimeIdentifiers.ToArray(),
+ out var mostFitRuntimeIdentifier))
+ {
+ return library
+ ?.ToolsAssemblies
+ ?.Where(l =>
+ LockFileMatcher.MatchesDirectoryPath(l, $"{PackagedShimsDirectoryConvention}/{mostFitRuntimeIdentifier}"))
+ .Select(l => LockFileRelativePathToFullFilePath(l.Path, library)).ToArray()
+ ?? Array.Empty();
+ }
+ else
+ {
+ return Array.Empty();
+ }
+ }
+
private ToolConfiguration DeserializeToolConfiguration(string ToolSettingsFileName, LockFileTargetLibrary library)
{
var dotnetToolSettings = FindItemInTargetLibrary(library, ToolSettingsFileName);
diff --git a/src/dotnet/commands/dotnet-tool/install/ToolInstallCommand.cs b/src/dotnet/commands/dotnet-tool/install/ToolInstallCommand.cs
index 42bcb8790..845644239 100644
--- a/src/dotnet/commands/dotnet-tool/install/ToolInstallCommand.cs
+++ b/src/dotnet/commands/dotnet-tool/install/ToolInstallCommand.cs
@@ -142,7 +142,7 @@ namespace Microsoft.DotNet.Tools.Tool.Install
foreach (var command in package.Commands)
{
- shellShimRepository.CreateShim(command.Executable, command.Name);
+ shellShimRepository.CreateShim(command.Executable, command.Name, package.PackagedShims);
}
scope.Complete();
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf b/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf
index aa36da3b1..8e1334b72 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf
@@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.
+
+ More than one packaged shim is available: {0}.
+ More than one packaged shim is available: {0}.
+
+
+
+ Failed to read NuGet LockFile for tool package '{0}': {1}
+ Failed to read NuGet LockFile for tool package '{0}': {1}
+
+