From 02a98d4e63e02928b8d359e4a0fff7d78530b67f Mon Sep 17 00:00:00 2001
From: William Lee <wul@microsoft.com>
Date: Fri, 19 Jan 2018 17:15:34 -0800
Subject: [PATCH] [tools] Integrate NuGet (#8414)

* Integrate NuGet ask

* Update NuGet version. Rely on NuGet to filter TFM. And use asset.json to find entrypoint

* Update XML file to per TFM

* Add extra property to the fake project according to nuget

* Treat nuget fallback folder as offline cache for tool

* Require -g to install global tool

* Copy test asset during test project build

* Address code review on LockFileMatchChecker

* Get NETCorePlatformsImplicitPackageVersion from PackageDefinitions

* Edit and add missing loc

* Change LockFileMatchChecker to local function

* Adding comment

* Add to content instead of copy

* Download platform package instead

* disable SDK side implicit NuGetFallbackFolder

* merge loc

* Revert extra line

* use a prerelease platforms version that supports alpine
---
 build/MSBuildExtensions.targets               |   4 +
 src/dotnet/CommonLocalizableStrings.resx      |   6 +
 .../ToolPackage/LockFileMatchChecker.cs       |  57 ++++++++
 ... => ToolConfigurationAndExecutablePath.cs} |  11 +-
 src/dotnet/ToolPackage/ToolPackageObtainer.cs |  90 ++++++++----
 .../commands/dotnet-install/InstallCommand.cs |   2 -
 .../dotnet-install-tool/InstallToolCommand.cs |  28 ++--
 .../InstallToolCommandParser.cs               |   4 +
 .../LocalizableStrings.resx                   |  13 +-
 .../xlf/LocalizableStrings.cs.xlf             |  15 ++
 .../xlf/LocalizableStrings.de.xlf             |  15 ++
 .../xlf/LocalizableStrings.es.xlf             |  15 ++
 .../xlf/LocalizableStrings.fr.xlf             |  15 ++
 .../xlf/LocalizableStrings.it.xlf             |  15 ++
 .../xlf/LocalizableStrings.ja.xlf             |  15 ++
 .../xlf/LocalizableStrings.ko.xlf             |  15 ++
 .../xlf/LocalizableStrings.pl.xlf             |  15 ++
 .../xlf/LocalizableStrings.pt-BR.xlf          |  15 ++
 .../xlf/LocalizableStrings.ru.xlf             |  15 ++
 .../xlf/LocalizableStrings.tr.xlf             |  15 ++
 .../xlf/LocalizableStrings.zh-Hans.xlf        |  15 ++
 .../xlf/LocalizableStrings.zh-Hant.xlf        |  15 ++
 .../xlf/CommonLocalizableStrings.cs.xlf       |  10 ++
 .../xlf/CommonLocalizableStrings.de.xlf       |  10 ++
 .../xlf/CommonLocalizableStrings.es.xlf       |  10 ++
 .../xlf/CommonLocalizableStrings.fr.xlf       |  10 ++
 .../xlf/CommonLocalizableStrings.it.xlf       |  10 ++
 .../xlf/CommonLocalizableStrings.ja.xlf       |  10 ++
 .../xlf/CommonLocalizableStrings.ko.xlf       |  10 ++
 .../xlf/CommonLocalizableStrings.pl.xlf       |  10 ++
 .../xlf/CommonLocalizableStrings.pt-BR.xlf    |  10 ++
 .../xlf/CommonLocalizableStrings.ru.xlf       |  10 ++
 .../xlf/CommonLocalizableStrings.tr.xlf       |  10 ++
 .../xlf/CommonLocalizableStrings.zh-Hans.xlf  |  10 ++
 .../xlf/CommonLocalizableStrings.zh-Hant.xlf  |  10 ++
 .../LockFileMatcherTests.cs                   |  28 ++++
 .../Microsoft.DotNet.ToolPackage.Tests.csproj |  19 ++-
 .../SampleGlobalTool/includepublish.nuspec    |  10 +-
 .../ToolPackageObtainerTests.cs               | 137 +++++++++++-------
 .../ParserTests/InstallToolParserTests.cs     |  15 +-
 40 files changed, 640 insertions(+), 109 deletions(-)
 create mode 100644 src/dotnet/ToolPackage/LockFileMatchChecker.cs
 rename src/dotnet/ToolPackage/{ToolConfigurationAndExecutableDirectory.cs => ToolConfigurationAndExecutablePath.cs} (62%)
 create mode 100644 test/Microsoft.DotNet.ToolPackage.Tests/LockFileMatcherTests.cs

diff --git a/build/MSBuildExtensions.targets b/build/MSBuildExtensions.targets
index a1da1d223..f1afbe3cb 100644
--- a/build/MSBuildExtensions.targets
+++ b/build/MSBuildExtensions.targets
@@ -99,6 +99,8 @@
     <ItemGroup>
       <_NETStandardLibraryVersions Include="@(PackageDefinitions->'%(Version)')"
                                    Condition="%(PackageDefinitions.Name) == 'NetStandard.Library'" />
+      <_NETCorePlatformsImplicitPackageVersion Include="@(PackageDefinitions->'%(Version)')"
+                                   Condition="%(PackageDefinitions.Name) == 'Microsoft.NETCore.Platforms'" />
     </ItemGroup>
 
     <Error Condition="@(_NETStandardLibraryVersions->Distinct()->Count()) != 1"
@@ -107,6 +109,7 @@
     <PropertyGroup>
       <_NETCoreAppPackageVersion>$(MicrosoftNETCoreAppPackageVersion)</_NETCoreAppPackageVersion>
       <_NETStandardPackageVersion>@(_NETStandardLibraryVersions->Distinct())</_NETStandardPackageVersion>
+      <_NETCorePlatformsImplicitPackageVersion>@(_NETCorePlatformsImplicitPackageVersion->Distinct())</_NETCorePlatformsImplicitPackageVersion>
 
       <!-- Use only major and minor in target framework version -->
       <_NETCoreAppTargetFrameworkVersion>$(_NETCoreAppPackageVersion.Split('.')[0]).$(_NETCoreAppPackageVersion.Split('.')[1])</_NETCoreAppTargetFrameworkVersion>
@@ -131,6 +134,7 @@ Copyright (c) .NET Foundation. All rights reserved.
     <BundledNETCoreAppPackageVersion>$(_NETCoreAppPackageVersion)</BundledNETCoreAppPackageVersion>
     <BundledNETStandardTargetFrameworkVersion>$(_NETStandardTargetFrameworkVersion)</BundledNETStandardTargetFrameworkVersion>
     <BundledNETStandardPackageVersion>$(_NETStandardPackageVersion)</BundledNETStandardPackageVersion>
+    <NETCorePlatformsImplicitPackageVersion>$(_NETCorePlatformsImplicitPackageVersion)</NETCorePlatformsImplicitPackageVersion>
   </PropertyGroup>
 </Project>
 ]]>
diff --git a/src/dotnet/CommonLocalizableStrings.resx b/src/dotnet/CommonLocalizableStrings.resx
index b85099724..8a236f571 100644
--- a/src/dotnet/CommonLocalizableStrings.resx
+++ b/src/dotnet/CommonLocalizableStrings.resx
@@ -584,4 +584,10 @@ Output: {1}</value>
   <data name="FailInstallToolSameName" xml:space="preserve">
     <value>Failed to install tool {0}. A command with the same name already exists.</value>
   </data>
+  <data name="ToolPackageMissingEntryPointFile" xml:space="preserve">
+    <value>Package '{0}' is missing entry point file {1}.</value>
+  </data>
+  <data name="ToolPackageMissingSettingsFile" xml:space="preserve">
+    <value>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</value>
+  </data>
 </root>
\ No newline at end of file
diff --git a/src/dotnet/ToolPackage/LockFileMatchChecker.cs b/src/dotnet/ToolPackage/LockFileMatchChecker.cs
new file mode 100644
index 000000000..efa4af6b6
--- /dev/null
+++ b/src/dotnet/ToolPackage/LockFileMatchChecker.cs
@@ -0,0 +1,57 @@
+// 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.Linq;
+using NuGet.ProjectModel;
+
+namespace Microsoft.DotNet.ToolPackage
+{
+    internal class LockFileMatcher
+    {
+        /// <summary>
+        /// Check if LockFileItem matches the targetRelativeFilePath.
+        /// 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/tools.dll" part matches exactly with the targetRelativeFilePath, return true.
+        /// </summary>
+        /// <param name="lockFileItem">LockFileItem from asset.json restored from temp project</param>
+        /// <param name="targetRelativeFilePath">file path relative to tools/TFM/RID</param>
+        internal static bool MatchesFile(LockFileItem lockFileItem, string targetRelativeFilePath)
+        {
+            string[] pathInLockFilePathInArray = SplitPathByDirectorySeparator(lockFileItem.Path);
+            string[] entryPointPathInArray = SplitPathByDirectorySeparator(targetRelativeFilePath);
+
+            return entryPointPathInArray.Length >= 1
+                   && PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder()
+                   && SubPathMatchesTargetFilePath();
+
+            bool SubPathMatchesTargetFilePath()
+            {
+                string[] pathAfterToolsTfmRid = pathInLockFilePathInArray.Skip(3).ToArray();
+                return !pathAfterToolsTfmRid
+                    .Where((directoryOnEveryLevel, i) => directoryOnEveryLevel != entryPointPathInArray[i])
+                    .Any();
+            }
+
+            bool PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder()
+            {
+                if (pathInLockFilePathInArray.Length - entryPointPathInArray.Length != 3)
+                {
+                    return false;
+                }
+
+                if (pathInLockFilePathInArray[0] != "tools")
+                {
+                    return false;
+                }
+
+                return true;
+            }
+
+            string[] SplitPathByDirectorySeparator(string path)
+            {
+                return path.Split('\\', '/');
+            }
+        }
+    }
+}
diff --git a/src/dotnet/ToolPackage/ToolConfigurationAndExecutableDirectory.cs b/src/dotnet/ToolPackage/ToolConfigurationAndExecutablePath.cs
similarity index 62%
rename from src/dotnet/ToolPackage/ToolConfigurationAndExecutableDirectory.cs
rename to src/dotnet/ToolPackage/ToolConfigurationAndExecutablePath.cs
index 06539b0a8..b191155b2 100644
--- a/src/dotnet/ToolPackage/ToolConfigurationAndExecutableDirectory.cs
+++ b/src/dotnet/ToolPackage/ToolConfigurationAndExecutablePath.cs
@@ -5,17 +5,18 @@ using Microsoft.Extensions.EnvironmentAbstractions;
 
 namespace Microsoft.DotNet.ToolPackage
 {
-    internal class ToolConfigurationAndExecutableDirectory
+    internal class ToolConfigurationAndExecutablePath
     {
-        public ToolConfigurationAndExecutableDirectory(
+        public ToolConfigurationAndExecutablePath(
             ToolConfiguration toolConfiguration,
-            DirectoryPath executableDirectory)
+            FilePath executable)
         {
             Configuration = toolConfiguration;
-            ExecutableDirectory = executableDirectory;
+            Executable = executable;
         }
 
         public ToolConfiguration Configuration { get; }
-        public DirectoryPath ExecutableDirectory { get; }
+
+        public FilePath Executable { get; }
     }
 }
diff --git a/src/dotnet/ToolPackage/ToolPackageObtainer.cs b/src/dotnet/ToolPackage/ToolPackageObtainer.cs
index 1f0eec7dd..151fe83e7 100644
--- a/src/dotnet/ToolPackage/ToolPackageObtainer.cs
+++ b/src/dotnet/ToolPackage/ToolPackageObtainer.cs
@@ -1,10 +1,12 @@
 using System;
-using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Xml.Linq;
 using Microsoft.DotNet.Tools;
+using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Configurer;
 using Microsoft.Extensions.EnvironmentAbstractions;
+using NuGet.ProjectModel;
 
 namespace Microsoft.DotNet.ToolPackage
 {
@@ -15,9 +17,11 @@ namespace Microsoft.DotNet.ToolPackage
         private readonly IPackageToProjectFileAdder _packageToProjectFileAdder;
         private readonly IProjectRestorer _projectRestorer;
         private readonly DirectoryPath _toolsPath;
+        private readonly DirectoryPath _offlineFeedPath;
 
         public ToolPackageObtainer(
             DirectoryPath toolsPath,
+            DirectoryPath offlineFeedPath,
             Func<FilePath> getTempProjectPath,
             Lazy<string> bundledTargetFrameworkMoniker,
             IPackageToProjectFileAdder packageToProjectFileAdder,
@@ -30,9 +34,10 @@ namespace Microsoft.DotNet.ToolPackage
             _packageToProjectFileAdder = packageToProjectFileAdder ??
                                          throw new ArgumentNullException(nameof(packageToProjectFileAdder));
             _toolsPath = toolsPath;
+            _offlineFeedPath = offlineFeedPath;
         }
 
-        public ToolConfigurationAndExecutableDirectory ObtainAndReturnExecutablePath(
+        public ToolConfigurationAndExecutablePath ObtainAndReturnExecutablePath(
             string packageId,
             string packageVersion = null,
             FilePath? nugetconfig = null,
@@ -50,7 +55,7 @@ namespace Microsoft.DotNet.ToolPackage
                 {
                     throw new PackageObtainException(
                         string.Format(CommonLocalizableStrings.NuGetConfigurationFileDoesNotExist,
-                        Path.GetFullPath(nugetconfig.Value.Value)));
+                            Path.GetFullPath(nugetconfig.Value.Value)));
                 }
             }
 
@@ -95,16 +100,52 @@ namespace Microsoft.DotNet.ToolPackage
                 packageVersion = concreteVersion;
             }
 
-            ToolConfiguration toolConfiguration = GetConfiguration(packageId: packageId, packageVersion: packageVersion, individualToolVersion: toolDirectory);
+            LockFile lockFile = new LockFileFormat()
+                .ReadWithLock(toolDirectory.WithFile("project.assets.json").Value)
+                .Result;
 
-            return new ToolConfigurationAndExecutableDirectory(
+            LockFileItem dotnetToolSettings = FindAssetInLockFile(lockFile, "DotnetToolSettings.xml", packageId);
+
+            if (dotnetToolSettings == null)
+            {
+                throw new PackageObtainException(
+                    string.Format(CommonLocalizableStrings.ToolPackageMissingSettingsFile, packageId));
+            }
+
+            FilePath toolConfigurationPath =
+                toolDirectory
+                    .WithSubDirectories(packageId, packageVersion)
+                    .WithFile(dotnetToolSettings.Path);
+
+            ToolConfiguration toolConfiguration =
+                ToolConfigurationDeserializer.Deserialize(toolConfigurationPath.Value);
+
+            var entryPointFromLockFile =
+                FindAssetInLockFile(lockFile, toolConfiguration.ToolAssemblyEntryPoint, packageId);
+
+            if (entryPointFromLockFile == null)
+            {
+                throw new PackageObtainException(string.Format(CommonLocalizableStrings.ToolPackageMissingEntryPointFile,
+                    packageId, toolConfiguration.ToolAssemblyEntryPoint));
+            }
+
+            return new ToolConfigurationAndExecutablePath(
                 toolConfiguration,
                 toolDirectory.WithSubDirectories(
-                    packageId,
-                    packageVersion,
-                    "tools",
-                    targetframework,
-                    "any"));
+                        packageId,
+                        packageVersion)
+                    .WithFile(entryPointFromLockFile.Path));
+        }
+
+        private static LockFileItem FindAssetInLockFile(
+            LockFile lockFile,
+            string targetRelativeFilePath, string packageId)
+        {
+            return lockFile
+                .Targets?.SingleOrDefault(t => t.RuntimeIdentifier != null)
+                ?.Libraries?.SingleOrDefault(l => l.Name == packageId)
+                ?.ToolsAssemblies
+                ?.SingleOrDefault(t => LockFileMatcher.MatchesFile(t, targetRelativeFilePath));
         }
 
         private static void MoveToVersionedDirectory(
@@ -119,22 +160,6 @@ namespace Microsoft.DotNet.ToolPackage
             Directory.Move(temporary.Value, versioned.Value);
         }
 
-        private static ToolConfiguration GetConfiguration(
-            string packageId,
-            string packageVersion,
-            DirectoryPath individualToolVersion)
-        {
-            FilePath toolConfigurationPath =
-                individualToolVersion
-                    .WithSubDirectories(packageId, packageVersion, "tools")
-                    .WithFile("DotnetToolSettings.xml");
-
-            ToolConfiguration toolConfiguration =
-                ToolConfigurationDeserializer.Deserialize(toolConfigurationPath.Value);
-
-            return toolConfiguration;
-        }
-
         private FilePath CreateTempProject(
             string packageId,
             PackageVersion packageVersion,
@@ -153,9 +178,16 @@ namespace Microsoft.DotNet.ToolPackage
                     new XAttribute("Sdk", "Microsoft.NET.Sdk"),
                     new XElement("PropertyGroup",
                         new XElement("TargetFramework", targetframework),
-                        new XElement("RestorePackagesPath", individualToolVersion.Value),
-                        new XElement("RestoreSolutionDirectory", Directory.GetCurrentDirectory()), // https://github.com/NuGet/Home/issues/6199
-                        new XElement("DisableImplicitFrameworkReferences", "true")
+                        new XElement("RestorePackagesPath", individualToolVersion.Value), // tool package will restore to tool folder
+                        new XElement("RestoreProjectStyle", "DotnetToolReference"), // without it, project cannot reference tool package
+                        new XElement("RestoreRootConfigDirectory", Directory.GetCurrentDirectory()), // config file probing start directory
+                        new XElement("DisableImplicitFrameworkReferences", "true"), // no Microsoft.NETCore.App in tool folder
+                        new XElement("RestoreFallbackFolders", "clear"), // do not use fallbackfolder, tool package need to be copied to tool folder
+                        new XElement("RestoreAdditionalProjectSources", // use fallbackfolder as feed to enable offline
+                            Directory.Exists(_offlineFeedPath.Value) ? _offlineFeedPath.Value : string.Empty),
+                        new XElement("RestoreAdditionalProjectFallbackFolders", string.Empty), // block other
+                        new XElement("RestoreAdditionalProjectFallbackFoldersExcludes", string.Empty),  // block other
+                        new XElement("DisableImplicitNuGetFallbackFolder","true")  // disable SDK side implicit NuGetFallbackFolder
                     ),
                     packageVersion.IsConcreteValue
                         ? new XElement("ItemGroup",
diff --git a/src/dotnet/commands/dotnet-install/InstallCommand.cs b/src/dotnet/commands/dotnet-install/InstallCommand.cs
index 1e2bfc0fd..11e823484 100644
--- a/src/dotnet/commands/dotnet-install/InstallCommand.cs
+++ b/src/dotnet/commands/dotnet-install/InstallCommand.cs
@@ -6,9 +6,7 @@ using System.Collections.Generic;
 using Microsoft.DotNet.Cli;
 using Microsoft.DotNet.Cli.CommandLine;
 using Microsoft.DotNet.Cli.Utils;
-using Microsoft.DotNet.Tools.Add;
 using Microsoft.DotNet.Tools.Install.Tool;
-using LocalizableStrings = Microsoft.DotNet.Tools.Install.LocalizableStrings;
 
 namespace Microsoft.DotNet.Tools.Install
 {
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommand.cs b/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommand.cs
index 2d5ec8de2..276a681f5 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommand.cs
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommand.cs
@@ -21,6 +21,7 @@ namespace Microsoft.DotNet.Tools.Install.Tool
         private static string _configFilePath;
         private static string _framework;
         private static string _source;
+        private static bool _global;
 
         public InstallToolCommand(
             AppliedOption appliedCommand,
@@ -37,27 +38,28 @@ namespace Microsoft.DotNet.Tools.Install.Tool
             _configFilePath = appliedCommand.ValueOrDefault<string>("configfile");
             _framework = appliedCommand.ValueOrDefault<string>("framework");
             _source = appliedCommand.ValueOrDefault<string>("source");
+            _global = appliedCommand.ValueOrDefault<bool>("global");
         }
 
         public override int Execute()
         {
-            var executablePackagePath = new DirectoryPath(new CliFolderPathCalculator().ExecutablePackagesPath);
+            if (!_global)
+            {
+                throw new GracefulException(LocalizableStrings.InstallToolCommandOnlySupportGlobal);
+            }
 
-            var toolConfigurationAndExecutableDirectory = ObtainPackage(executablePackagePath);
+            var cliFolderPathCalculator = new CliFolderPathCalculator();
+            var executablePackagePath = new DirectoryPath(cliFolderPathCalculator.ExecutablePackagesPath);
+            var offlineFeedPath = new DirectoryPath(cliFolderPathCalculator.CliFallbackFolderPath);
 
-            DirectoryPath executable = toolConfigurationAndExecutableDirectory
-                .ExecutableDirectory
-                .WithSubDirectories(
-                    toolConfigurationAndExecutableDirectory
-                        .Configuration
-                        .ToolAssemblyEntryPoint);
+            var toolConfigurationAndExecutablePath = ObtainPackage(executablePackagePath, offlineFeedPath);
 
             var shellShimMaker = new ShellShimMaker(executablePackagePath.Value);
-            var commandName = toolConfigurationAndExecutableDirectory.Configuration.CommandName;
+            var commandName = toolConfigurationAndExecutablePath.Configuration.CommandName;
             shellShimMaker.EnsureCommandNameUniqueness(commandName);
 
             shellShimMaker.CreateShim(
-                executable.Value,
+                toolConfigurationAndExecutablePath.Executable.Value,
                 commandName);
 
             EnvironmentPathFactory
@@ -70,7 +72,9 @@ namespace Microsoft.DotNet.Tools.Install.Tool
             return 0;
         }
 
-        private static ToolConfigurationAndExecutableDirectory ObtainPackage(DirectoryPath executablePackagePath)
+        private static ToolConfigurationAndExecutablePath ObtainPackage(
+            DirectoryPath executablePackagePath,
+            DirectoryPath offlineFeedPath)
         {
             try
             {
@@ -83,6 +87,7 @@ namespace Microsoft.DotNet.Tools.Install.Tool
                 var toolPackageObtainer =
                     new ToolPackageObtainer(
                         executablePackagePath,
+                        offlineFeedPath,
                         () => new DirectoryPath(Path.GetTempPath())
                             .WithSubDirectories(Path.GetRandomFileName())
                             .WithFile(Path.GetRandomFileName() + ".csproj"),
@@ -97,6 +102,7 @@ namespace Microsoft.DotNet.Tools.Install.Tool
                     targetframework: _framework,
                     source: _source);
             }
+
             catch (PackageObtainException ex)
             {
                 throw new GracefulException(
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommandParser.cs b/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommandParser.cs
index 75744bf82..6422517d9 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommandParser.cs
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommandParser.cs
@@ -15,6 +15,10 @@ namespace Microsoft.DotNet.Cli
                 Accept.ExactlyOneArgument(errorMessage: o => LocalizableStrings.SpecifyExactlyOnePackageId)
                     .With(name: LocalizableStrings.PackageIdArgumentName,
                           description: LocalizableStrings.PackageIdArgumentDescription),
+                Create.Option(
+                    "-g|--global",
+                    LocalizableStrings.GlobalOptionDescription,
+                    Accept.NoArguments()),
                 Create.Option(
                     "--version",
                     LocalizableStrings.VersionOptionDescription,
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/LocalizableStrings.resx b/src/dotnet/commands/dotnet-install/dotnet-install-tool/LocalizableStrings.resx
index 5217148a5..d689f4b11 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/LocalizableStrings.resx
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/LocalizableStrings.resx
@@ -129,13 +129,13 @@
   <data name="VersionOptionDescription" xml:space="preserve">
     <value>Version of the tool package in NuGet.</value>
   </data>
-    <data name="SourceOptionDescription" xml:space="preserve">
+  <data name="SourceOptionDescription" xml:space="preserve">
     <value>Specifies a NuGet package source to use during installation.</value>
   </data>
   <data name="SourceOptionName" xml:space="preserve">
     <value>SOURCE</value>
   </data>
-    <data name="CommandDescription" xml:space="preserve">
+  <data name="CommandDescription" xml:space="preserve">
     <value>Installs a tool for use on the command line.</value>
   </data>
   <data name="ConfigFileOptionDescription" xml:space="preserve">
@@ -172,4 +172,13 @@ The error was:
     <value>
 The installation succeeded. If there are no further instructions, you can type the following command in shell directly to invoke: {0}</value>
   </data>
+  <data name="GlobalOptionDescription" xml:space="preserve">
+    <value>Install user wide.</value>
+  </data>
+  <data name="InstallFullCommandNameLocalized" xml:space="preserve">
+    <value>.NET Install Command</value>
+  </data>
+  <data name="InstallToolCommandOnlySupportGlobal" xml:space="preserve">
+    <value>The --global switch (-g) is currently required because only user wide tools are supported.</value>
+  </data>
 </root>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.cs.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.cs.xlf
index 8cbde8a02..0904e75e8 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.cs.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.cs.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.de.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.de.xlf
index 2f8bd6e67..87144c359 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.de.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.de.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.es.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.es.xlf
index 0e69dee70..7449fa3ef 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.es.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.es.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.fr.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.fr.xlf
index e5db63855..789a063c5 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.fr.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.fr.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.it.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.it.xlf
index ab3f71d7c..b6ef111ac 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.it.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.it.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ja.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ja.xlf
index 12683d871..d5f88a8f6 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ja.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ja.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ko.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ko.xlf
index 3fae3a9af..2cf8b32f8 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ko.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ko.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pl.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pl.xlf
index cbd45c1c7..c6f63ffda 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pl.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pl.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pt-BR.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pt-BR.xlf
index 2d8e96c81..61cfe82c1 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pt-BR.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ru.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ru.xlf
index 5e3fb87fe..6c4047fab 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ru.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ru.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.tr.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.tr.xlf
index 40b5f0c98..61059f22b 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.tr.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.tr.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hans.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hans.xlf
index 32a4348f6..e51c1c828 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hans.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hant.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hant.xlf
index fac88c0f9..c74ff695d 100644
--- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hant.xlf
@@ -98,6 +98,21 @@ The error was:
         <target state="new">Please specify one tool Package Id to install.</target>
         <note />
       </trans-unit>
+      <trans-unit id="GlobalOptionDescription">
+        <source>Install user wide.</source>
+        <target state="new">Install user wide.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallFullCommandNameLocalized">
+        <source>.NET Install Command</source>
+        <target state="new">.NET Install Command</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="InstallToolCommandOnlySupportGlobal">
+        <source>The --global switch (-g) is currently required because only user wide tools are supported.</source>
+        <target state="new">The --global switch (-g) is currently required because only user wide tools are supported.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf b/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf
index 4ed20749a..fcff78d2f 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.de.xlf b/src/dotnet/xlf/CommonLocalizableStrings.de.xlf
index 325094c1f..777d128ea 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.de.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.de.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.es.xlf b/src/dotnet/xlf/CommonLocalizableStrings.es.xlf
index 8b53c8af1..082d2d6d0 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.es.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.es.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.fr.xlf b/src/dotnet/xlf/CommonLocalizableStrings.fr.xlf
index 06433f72a..04790f3aa 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.fr.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.fr.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.it.xlf b/src/dotnet/xlf/CommonLocalizableStrings.it.xlf
index 9f896eb7d..2f43ff640 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.it.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.it.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.ja.xlf b/src/dotnet/xlf/CommonLocalizableStrings.ja.xlf
index eb606f9b8..b2648f41d 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.ja.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.ja.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.ko.xlf b/src/dotnet/xlf/CommonLocalizableStrings.ko.xlf
index 5309f8d3c..c8e626ebc 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.ko.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.ko.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.pl.xlf b/src/dotnet/xlf/CommonLocalizableStrings.pl.xlf
index 930772b62..866502ed7 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.pl.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.pl.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.pt-BR.xlf b/src/dotnet/xlf/CommonLocalizableStrings.pt-BR.xlf
index 321b41834..d6590ff13 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.pt-BR.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.pt-BR.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.ru.xlf b/src/dotnet/xlf/CommonLocalizableStrings.ru.xlf
index 95deba0e2..0c8083046 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.ru.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.ru.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.tr.xlf b/src/dotnet/xlf/CommonLocalizableStrings.tr.xlf
index 87523978e..f1ff54bfb 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.tr.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.tr.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.zh-Hans.xlf b/src/dotnet/xlf/CommonLocalizableStrings.zh-Hans.xlf
index 8bf38a776..bbe34189e 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.zh-Hans.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.zh-Hans.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/src/dotnet/xlf/CommonLocalizableStrings.zh-Hant.xlf b/src/dotnet/xlf/CommonLocalizableStrings.zh-Hant.xlf
index 8e92007df..314f986c0 100644
--- a/src/dotnet/xlf/CommonLocalizableStrings.zh-Hant.xlf
+++ b/src/dotnet/xlf/CommonLocalizableStrings.zh-Hant.xlf
@@ -787,6 +787,16 @@ You can do this by running the following command:
 setx PATH "%PATH%;{1}"</target>
         <note />
       </trans-unit>
+      <trans-unit id="ToolPackageMissingEntryPointFile">
+        <source>Package '{0}' is missing entry point file {1}.</source>
+        <target state="new">Package '{0}' is missing entry point file {1}.</target>
+        <note />
+      </trans-unit>
+      <trans-unit id="ToolPackageMissingSettingsFile">
+        <source>Package '{0}' is missing tool settings file DotnetToolSettings.xml.</source>
+        <target state="new">Package '{0}' is missing tool settings file DotnetToolSettings.xml.</target>
+        <note />
+      </trans-unit>
     </body>
   </file>
 </xliff>
\ No newline at end of file
diff --git a/test/Microsoft.DotNet.ToolPackage.Tests/LockFileMatcherTests.cs b/test/Microsoft.DotNet.ToolPackage.Tests/LockFileMatcherTests.cs
new file mode 100644
index 000000000..0a0a0cb03
--- /dev/null
+++ b/test/Microsoft.DotNet.ToolPackage.Tests/LockFileMatcherTests.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 FluentAssertions;
+using Microsoft.DotNet.Tools.Test.Utilities;
+using NuGet.ProjectModel;
+using Xunit;
+
+namespace Microsoft.DotNet.ToolPackage.Tests
+{
+    public class LockFileMatcherTests : TestBase
+    {
+
+        [Theory]
+        [InlineData("tools/netcoreapp1.1/any/tool.dll", "tool.dll", true)]
+        [InlineData(@"tools\netcoreapp1.1\any\subDirectory\tool.dll", "subDirectory/tool.dll", true)]
+        [InlineData("tools/netcoreapp1.1/win-x64/tool.dll", "tool.dll", true)]
+        [InlineData("tools/netcoreapp1.1/any/subDirectory/tool.dll", "subDirectory/tool.dll", true)]
+        [InlineData("libs/netcoreapp1.1/any/tool.dll", "tool.dll", false)]
+        [InlineData("tools/netcoreapp1.1/any/subDirectory/tool.dll", "tool.dll", false)]
+        [InlineData("tools/netcoreapp1.1/any/subDirectory/tool.dll", "subDirectory/subDirectory/subDirectory/subDirectory/subDirectory/tool.dll", false)]
+        public void MatchesEntryPointTests(string pathInLockFileItem, string targetRelativeFilePath, bool shouldMatch)
+        {
+            LockFileMatcher.MatchesFile(new LockFileItem(pathInLockFileItem), targetRelativeFilePath)
+                .Should().Be(shouldMatch);
+        }
+    }
+}
diff --git a/test/Microsoft.DotNet.ToolPackage.Tests/Microsoft.DotNet.ToolPackage.Tests.csproj b/test/Microsoft.DotNet.ToolPackage.Tests/Microsoft.DotNet.ToolPackage.Tests.csproj
index 67cf2b3d4..736655e5b 100644
--- a/test/Microsoft.DotNet.ToolPackage.Tests/Microsoft.DotNet.ToolPackage.Tests.csproj
+++ b/test/Microsoft.DotNet.ToolPackage.Tests/Microsoft.DotNet.ToolPackage.Tests.csproj
@@ -33,16 +33,15 @@
     <None Update="DotnetToolSettingsGolden.xml">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </None>
-    <None Update="TestAssetLocalNugetFeed/*.*">
-      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-    </None>
 
     <Compile Remove="SampleGlobalTool/**" />
     <Content Remove="SampleGlobalTool/**" />
     <EmbeddedResource Remove="SampleGlobalTool/**" />
     <None Remove="SampleGlobalTool/**" />
   </ItemGroup>
-  
+
+  <UsingTask TaskName="DownloadFile" AssemblyFile="$(CLIBuildDll)" />
+
   <Target Name="CreateNupkgFromSource" BeforeTargets="Build">
     <PropertyGroup>
       <testAssetSourceRoot>$(BaseOutputPath)/TestAsset/SampleGlobalTool</testAssetSourceRoot>
@@ -53,4 +52,16 @@
     <MSBuild BuildInParallel="False" Projects="SampleGlobalTool/consoledemo.csproj" Targets="pack" Properties="Configuration=Release;NuspecFile=includepublish.nuspec;NuspecBasePath=$(testAssetSourceRoot);PackageOutputPath=$(OutputPath)/TestAssetLocalNugetFeed">
     </MSBuild>
   </Target>
+
+  <Target Name="DownloadTestAssetPackages" BeforeTargets="Build">
+    <PropertyGroup>
+      <!-- use a prerelease version that supports alpine -->
+      <PlatformsNupkgFileName>microsoft.netcore.platforms.2.1.0-preview1-26115-04.nupkg</PlatformsNupkgFileName>
+      <PlatformsNupkgUri>https://dotnet.myget.org/F/dotnet-core/api/v2/package/Microsoft.NETCore.Platforms/2.1.0-preview1-26115-04</PlatformsNupkgUri>
+    </PropertyGroup>
+
+    <DownloadFile Uri="$(PlatformsNupkgUri)"
+                  DestinationPath="$(OutputPath)/TestAssetLocalNugetFeed/$(PlatformsNupkgFileName)" />
+  </Target>
+
 </Project>
diff --git a/test/Microsoft.DotNet.ToolPackage.Tests/SampleGlobalTool/includepublish.nuspec b/test/Microsoft.DotNet.ToolPackage.Tests/SampleGlobalTool/includepublish.nuspec
index 181fe3f74..26cbe3c39 100644
--- a/test/Microsoft.DotNet.ToolPackage.Tests/SampleGlobalTool/includepublish.nuspec
+++ b/test/Microsoft.DotNet.ToolPackage.Tests/SampleGlobalTool/includepublish.nuspec
@@ -5,9 +5,15 @@
         <version>1.0.4</version>
         <description>test app</description>
         <authors>testauthor</authors>
+        <packageTypes>
+          <packageType name="DotnetTool" />
+        </packageTypes>
+      <dependencies>
+        <dependency id="Microsoft.NETCore.Platforms" version="2.1.0-preview1-26115-04" />
+      </dependencies>
     </metadata>
     <files>
         <file src="bin\Release\netcoreapp2.1\publish\*.*" target="tools\netcoreapp2.1\any\" />
-        <file src="DotnetToolSettings.xml" target="tools\DotnetToolSettings.xml" />
+        <file src="DotnetToolSettings.xml" target="tools\netcoreapp2.1\any\DotnetToolSettings.xml" />
     </files>
-</package>
\ No newline at end of file
+</package>
diff --git a/test/Microsoft.DotNet.ToolPackage.Tests/ToolPackageObtainerTests.cs b/test/Microsoft.DotNet.ToolPackage.Tests/ToolPackageObtainerTests.cs
index b09c87f91..4e948966e 100644
--- a/test/Microsoft.DotNet.ToolPackage.Tests/ToolPackageObtainerTests.cs
+++ b/test/Microsoft.DotNet.ToolPackage.Tests/ToolPackageObtainerTests.cs
@@ -23,24 +23,67 @@ namespace Microsoft.DotNet.ToolPackage.Tests
 
             var packageObtainer =
                 ConstructDefaultPackageObtainer(toolsPath);
-            ToolConfigurationAndExecutableDirectory toolConfigurationAndExecutableDirectory = packageObtainer.ObtainAndReturnExecutablePath(
+            ToolConfigurationAndExecutablePath toolConfigurationAndExecutablePath = packageObtainer.ObtainAndReturnExecutablePath(
                 packageId: TestPackageId,
                 packageVersion: TestPackageVersion,
                 nugetconfig: nugetConfigPath,
                 targetframework: _testTargetframework);
 
-            var executable = toolConfigurationAndExecutableDirectory
-                .ExecutableDirectory
-                .WithFile(
-                    toolConfigurationAndExecutableDirectory
-                        .Configuration
-                        .ToolAssemblyEntryPoint);
+            var executable = toolConfigurationAndExecutablePath
+                .Executable;
 
             File.Exists(executable.Value)
                 .Should()
                 .BeTrue(executable + " should have the executable");
         }
 
+        [Fact]
+        public void GivenNoFeedItThrows()
+        {
+            var toolsPath = Path.Combine(Directory.GetCurrentDirectory(), Path.GetRandomFileName());
+
+            ToolPackageObtainer packageObtainer =
+                ConstructDefaultPackageObtainer(toolsPath);
+
+            Action a = () => packageObtainer.ObtainAndReturnExecutablePath(
+                packageId: TestPackageId,
+                packageVersion: TestPackageVersion,
+                targetframework: _testTargetframework);
+
+            a.ShouldThrow<PackageObtainException>();
+        }
+
+        [Fact]
+        public void GivenOfflineFeedWhenCallItCanDownloadThePackage()
+        {
+            var toolsPath = Path.Combine(Directory.GetCurrentDirectory(), Path.GetRandomFileName());
+
+            ToolPackageObtainer packageObtainer =
+                new ToolPackageObtainer(
+                    toolsPath: new DirectoryPath(toolsPath),
+                    offlineFeedPath: new DirectoryPath(GetTestLocalFeedPath()),
+                    getTempProjectPath: GetUniqueTempProjectPathEachTest,
+                    bundledTargetFrameworkMoniker: new Lazy<string>(),
+                    packageToProjectFileAdder: new PackageToProjectFileAdder(),
+                    projectRestorer: new ProjectRestorer());
+
+            ToolConfigurationAndExecutablePath toolConfigurationAndExecutablePath =
+                packageObtainer.ObtainAndReturnExecutablePath(
+                    packageId: TestPackageId,
+                    packageVersion: TestPackageVersion,
+                    targetframework: _testTargetframework);
+
+            var executable = toolConfigurationAndExecutablePath
+                .Executable;
+
+            File.Exists(executable.Value)
+                .Should()
+                .BeTrue(executable + " should have the executable");
+
+            executable.Value.Should().NotContain(GetTestLocalFeedPath(), "Executalbe should not be still in fallbackfolder");
+            executable.Value.Should().Contain(toolsPath, "Executalbe should be copied to tools Path");
+        }
+
         [Fact]
         public void GivenNugetConfigAndPackageNameAndVersionAndTargetFrameworkWhenCallItCreateAssetFile()
         {
@@ -49,15 +92,23 @@ namespace Microsoft.DotNet.ToolPackage.Tests
 
             var packageObtainer =
                 ConstructDefaultPackageObtainer(toolsPath);
-            ToolConfigurationAndExecutableDirectory toolConfigurationAndExecutableDirectory =
-                packageObtainer.ObtainAndReturnExecutablePath(
-                    packageId: TestPackageId,
-                    packageVersion: TestPackageVersion,
-                    nugetconfig: nugetConfigPath,
-                    targetframework: _testTargetframework);
 
-            var assetJsonPath = toolConfigurationAndExecutableDirectory
-                .ExecutableDirectory
+            ToolConfigurationAndExecutablePath toolConfigurationAndExecutablePath = packageObtainer.ObtainAndReturnExecutablePath(
+                packageId: TestPackageId,
+                packageVersion: TestPackageVersion,
+                nugetconfig: nugetConfigPath,
+                targetframework: _testTargetframework);
+
+            /*
+              From mytool.dll to project.assets.json
+               .dotnet/.tools/packageid/version/packageid/version/mytool.dll
+                      /dependency1 package id/
+                      /dependency2 package id/
+                      /project.assets.json
+             */
+            var assetJsonPath = toolConfigurationAndExecutablePath
+                .Executable
+                .GetDirectoryPath()
                 .GetParentPath()
                 .GetParentPath()
                 .GetParentPath()
@@ -89,21 +140,18 @@ namespace Microsoft.DotNet.ToolPackage.Tests
             var packageObtainer =
                 new ToolPackageObtainer(
                     new DirectoryPath(toolsPath),
+                    new DirectoryPath("no such path"),
                     () => uniqueTempProjectPath,
                     new Lazy<string>(),
                     new PackageToProjectFileAdder(),
                     new ProjectRestorer());
-            ToolConfigurationAndExecutableDirectory toolConfigurationAndExecutableDirectory = packageObtainer.ObtainAndReturnExecutablePath(
-                packageId: TestPackageId,
-                packageVersion: TestPackageVersion,
-                targetframework: _testTargetframework);
+            ToolConfigurationAndExecutablePath toolConfigurationAndExecutablePath =
+                packageObtainer.ObtainAndReturnExecutablePath(
+                    packageId: TestPackageId,
+                    packageVersion: TestPackageVersion,
+                    targetframework: _testTargetframework);
 
-            var executable = toolConfigurationAndExecutableDirectory
-                .ExecutableDirectory
-                .WithFile(
-                    toolConfigurationAndExecutableDirectory
-                        .Configuration
-                        .ToolAssemblyEntryPoint);
+            var executable = toolConfigurationAndExecutablePath.Executable;
 
             File.Exists(executable.Value)
                 .Should()
@@ -118,17 +166,13 @@ namespace Microsoft.DotNet.ToolPackage.Tests
 
             var packageObtainer =
                 ConstructDefaultPackageObtainer(toolsPath);
-            ToolConfigurationAndExecutableDirectory toolConfigurationAndExecutableDirectory = packageObtainer.ObtainAndReturnExecutablePath(
+            ToolConfigurationAndExecutablePath toolConfigurationAndExecutablePath = packageObtainer.ObtainAndReturnExecutablePath(
                 packageId: TestPackageId,
+                packageVersion: TestPackageVersion,
                 nugetconfig: nugetConfigPath,
                 targetframework: _testTargetframework);
 
-            var executable = toolConfigurationAndExecutableDirectory
-                .ExecutableDirectory
-                .WithFile(
-                    toolConfigurationAndExecutableDirectory
-                        .Configuration
-                        .ToolAssemblyEntryPoint);
+            var executable = toolConfigurationAndExecutablePath.Executable;
 
             File.Exists(executable.Value)
                 .Should()
@@ -167,22 +211,18 @@ namespace Microsoft.DotNet.ToolPackage.Tests
             var packageObtainer =
                 new ToolPackageObtainer(
                     new DirectoryPath(toolsPath),
+                    new DirectoryPath("no such path"),
                     GetUniqueTempProjectPathEachTest,
                     new Lazy<string>(() => BundledTargetFramework.GetTargetFrameworkMoniker()),
                     new PackageToProjectFileAdder(),
                     new ProjectRestorer());
-            ToolConfigurationAndExecutableDirectory toolConfigurationAndExecutableDirectory =
+            ToolConfigurationAndExecutablePath toolConfigurationAndExecutablePath =
                 packageObtainer.ObtainAndReturnExecutablePath(
                     packageId: TestPackageId,
                     packageVersion: TestPackageVersion,
                     nugetconfig: nugetConfigPath);
 
-            var executable = toolConfigurationAndExecutableDirectory
-                .ExecutableDirectory
-                .WithFile(
-                    toolConfigurationAndExecutableDirectory
-                        .Configuration
-                        .ToolAssemblyEntryPoint);
+            var executable = toolConfigurationAndExecutablePath.Executable;
 
             File.Exists(executable.Value)
                 .Should()
@@ -205,7 +245,6 @@ namespace Microsoft.DotNet.ToolPackage.Tests
             a.ShouldThrow<PackageObtainException>()
                 .And
                 .Message.Should().Contain("does not exist");
-
         }
 
         [Fact]
@@ -218,16 +257,9 @@ namespace Microsoft.DotNet.ToolPackage.Tests
                 packageId: TestPackageId,
                 packageVersion: TestPackageVersion,
                 targetframework: _testTargetframework,
-                source: Path.Combine(
-                            Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
-                            "TestAssetLocalNugetFeed"));
+                source: GetTestLocalFeedPath());
 
-            var executable = toolConfigurationAndExecutableDirectory
-                .ExecutableDirectory
-                .WithFile(
-                    toolConfigurationAndExecutableDirectory
-                        .Configuration
-                        .ToolAssemblyEntryPoint);
+            var executable = toolConfigurationAndExecutableDirectory.Executable;
 
             File.Exists(executable.Value)
                 .Should()
@@ -247,6 +279,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests
         {
             return new ToolPackageObtainer(
                 new DirectoryPath(toolsPath),
+                new DirectoryPath("no such path"),
                 GetUniqueTempProjectPathEachTest,
                 new Lazy<string>(),
                 new PackageToProjectFileAdder(),
@@ -256,7 +289,6 @@ namespace Microsoft.DotNet.ToolPackage.Tests
         private static FilePath WriteNugetConfigFileToPointToTheFeed()
         {
             var nugetConfigName = "nuget.config";
-            var executeDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
 
             var tempPathForNugetConfigWithWhiteSpace =
                 Path.Combine(Path.GetTempPath(),
@@ -266,10 +298,13 @@ namespace Microsoft.DotNet.ToolPackage.Tests
             NuGetConfig.Write(
                 directory: tempPathForNugetConfigWithWhiteSpace,
                 configname: nugetConfigName,
-                localFeedPath: Path.Combine(executeDirectory, "TestAssetLocalNugetFeed"));
+                localFeedPath: GetTestLocalFeedPath());
+
             return new FilePath(Path.GetFullPath(Path.Combine(tempPathForNugetConfigWithWhiteSpace, nugetConfigName)));
         }
 
+        private static string GetTestLocalFeedPath() => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestAssetLocalNugetFeed");
+
         private readonly string _testTargetframework = BundledTargetFramework.GetTargetFrameworkMoniker();
         private const string TestPackageVersion = "1.0.4";
         private const string TestPackageId = "global.tool.console.demo";
diff --git a/test/dotnet.Tests/ParserTests/InstallToolParserTests.cs b/test/dotnet.Tests/ParserTests/InstallToolParserTests.cs
index 494df413a..625f110c1 100644
--- a/test/dotnet.Tests/ParserTests/InstallToolParserTests.cs
+++ b/test/dotnet.Tests/ParserTests/InstallToolParserTests.cs
@@ -24,7 +24,7 @@ namespace Microsoft.DotNet.Tests.ParserTests
         public void InstallGlobaltoolParserCanGetPackageIdAndPackageVersion()
         {
             var command = Parser.Instance;
-            var result = command.Parse("dotnet install tool console.test.app --version 1.0.1");
+            var result = command.Parse("dotnet install tool -g console.test.app --version 1.0.1");
 
             var parseResult = result["dotnet"]["install"]["tool"];
 
@@ -41,7 +41,7 @@ namespace Microsoft.DotNet.Tests.ParserTests
             var command = Parser.Instance;
             var result =
                 command.Parse(
-                    @"dotnet install tool console.test.app --version 1.0.1 --framework netcoreapp2.0 --configfile C:\TestAssetLocalNugetFeed");
+                    @"dotnet install tool -g console.test.app --version 1.0.1 --framework netcoreapp2.0 --configfile C:\TestAssetLocalNugetFeed");
 
             var parseResult = result["dotnet"]["install"]["tool"];
 
@@ -54,10 +54,19 @@ namespace Microsoft.DotNet.Tests.ParserTests
         {
             const string expectedSourceValue = "TestSourceValue";
 
-            var result = Parser.Instance.Parse($"dotnet install tool --source {expectedSourceValue} console.test.app");
+            var result = Parser.Instance.Parse($"dotnet install tool -g --source {expectedSourceValue} console.test.app");
 
             var appliedOptions = result["dotnet"]["install"]["tool"];
             appliedOptions.ValueOrDefault<string>("source").Should().Be(expectedSourceValue);
         }
+
+        [Fact]
+        public void InstallToolParserCanGetGlobalOption()
+        {
+            var result = Parser.Instance.Parse("dotnet install tool -g console.test.app");
+
+            var appliedOptions = result["dotnet"]["install"]["tool"];
+            appliedOptions.ValueOrDefault<bool>("global").Should().Be(true);
+        }
     }
 }