bundled DotnetTool (#8606)

Extract packages to DotnetTools folder under sdk/{version}

Add new resolver to discover it

Add test to enforce package structure. It will fail when the structure
changed
This commit is contained in:
William Lee 2018-02-16 13:32:29 -08:00 committed by GitHub
parent f86ea50075
commit 08f050c012
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 393 additions and 12 deletions

View file

@ -57,6 +57,7 @@ tools\TestAssetsDependencies\TestAssetsDependencies.csproj
<Import Project="build/BundledTools.props" />
<Import Project="build/BundledSdks.props" />
<Import Project="build/BundledTemplates.props" />
<Import Project="build/BundledDotnetTools.props" />
<Import Project="build/BuildDefaults.props" />
<Import Project="build/BundledRuntimes.props" />

View file

@ -0,0 +1,27 @@
<Project ToolsVersion="15.0" DefaultTargets="ExtractDotnetToolsToOutput">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.tasks))\dir.tasks" />
<Target Name="ExtractDotnetToolsToOutput"
DependsOnTargets="EnsureDotnetToolsRestored">
<Message Text="Restore DotnetTools $(TemplateFillInPackageName) from $(DotnetToolsNuPkgPath) to $(DotnetToolsLayoutDirectory)."
Importance="High" />
</Target>
<Target Name="EnsureDotnetToolsRestored"
Condition="!Exists('$(DotnetToolsNuPkgPath)/$(TemplateFillInPackageName.ToLower())')">
<PropertyGroup>
<DotnetToolsRestoreAdditionalParameters>/p:TargetFramework=$(CliTargetFramework)</DotnetToolsRestoreAdditionalParameters>
<DotnetToolsRestoreAdditionalParameters>$(DotnetToolsRestoreAdditionalParameters) /p:TemplateFillInPackageName=$(TemplateFillInPackageName)</DotnetToolsRestoreAdditionalParameters>
<DotnetToolsRestoreAdditionalParameters>$(DotnetToolsRestoreAdditionalParameters) /p:TemplateFillInPackageVersion=$(TemplateFillInPackageVersion)</DotnetToolsRestoreAdditionalParameters>
<DotnetToolsRestoreAdditionalParameters>$(DotnetToolsRestoreAdditionalParameters) /p:RestorePackagesPath=$(DotnetToolsLayoutDirectory)</DotnetToolsRestoreAdditionalParameters>
<DotnetToolsRestoreAdditionalParameters Condition=" $(DotnetToolsRestoreProjectStyle) != '' " >$(DotnetToolsRestoreAdditionalParameters) /p:RestoreProjectStyle=$(DotnetToolsRestoreProjectStyle)</DotnetToolsRestoreAdditionalParameters>
</PropertyGroup>
<DotNetRestore ToolPath="$(PreviousStageDirectory)"
ProjectPath="$(MSBuildThisFileDirectory)/templates/templates.csproj"
AdditionalParameters="$(DotnetToolsRestoreAdditionalParameters)" />
</Target>
</Project>

View file

@ -0,0 +1,6 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<BundledDotnetTools Include="dotnet-watch" Version="$(AspNetCoreVersion)" />
<BundledDotnetTools Include="dotnet-dev-certs" Version="$(AspNetCoreVersion)" />
</ItemGroup>
</Project>

View file

@ -11,26 +11,26 @@
<Copy SourceFiles="@(TemplateContent)"
DestinationFiles="@(TemplateContent->'$(TemplateLayoutDirectory)/%(RecursiveDir)%(FileName)%(Extension)')" />
<Message Text="Copied template $(TemplatePackageName) from $(TemplateNuPkgPath) to $(TemplateLayoutDirectory)."
<Message Text="Copied template $(TemplateFillInPackageName) from $(TemplateNuPkgPath) to $(TemplateLayoutDirectory)."
Importance="High" />
</Target>
<Target Name="GetTemplateItemsToCopy">
<ItemGroup>
<TemplateContent Include="$(TemplateNuPkgPath)/$(TemplatePackageName.ToLower()).$(TemplatePackageVersion.ToLower()).nupkg" />
<TemplateContent Include="$(TemplateNuPkgPath)/$(TemplateFillInPackageName.ToLower()).$(TemplateFillInPackageVersion.ToLower()).nupkg" />
</ItemGroup>
</Target>
<Target Name="EnsureTemplateRestored"
Condition="!Exists('$(TemplateNuPkgPath)/$(TemplatePackageName.ToLower()).nuspec')">
Condition="!Exists('$(TemplateNuPkgPath)/$(TemplateFillInPackageName.ToLower()).nuspec')">
<DotNetRestore ToolPath="$(PreviousStageDirectory)"
ProjectPath="$(MSBuildThisFileDirectory)/templates/templates.csproj"
AdditionalParameters="/p:TemplatePackageName=$(TemplatePackageName) /p:TemplatePackageVersion=$(TemplatePackageVersion)" />
AdditionalParameters="/p:TargetFramework=netcoreapp1.0 /p:TemplateFillInPackageName=$(TemplateFillInPackageName) /p:TemplateFillInPackageVersion=$(TemplateFillInPackageVersion)" />
</Target>
<Target Name="PrepareBundledTemplateProps">
<PropertyGroup>
<TemplateNuPkgPath>$(NuGetPackagesDir)/$(TemplatePackageName.ToLower())/$(TemplatePackageVersion.ToLower())</TemplateNuPkgPath>
<TemplateNuPkgPath>$(NuGetPackagesDir)/$(TemplateFillInPackageName.ToLower())/$(TemplateFillInPackageVersion.ToLower())</TemplateNuPkgPath>
</PropertyGroup>
</Target>
</Project>

View file

@ -3,12 +3,11 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp1.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="$(TemplatePackageName)" Version="$(TemplatePackageVersion)" />
<PackageReference Include="$(TemplateFillInPackageName)" Version="$(TemplateFillInPackageVersion)" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View file

@ -14,6 +14,9 @@ namespace Microsoft.DotNet.Cli.Utils
// command loaded from project tools nuget package
ProjectToolsPackage,
// command loaded from bundled DotnetTools nuget package
DotnetToolsPackage,
// command loaded from the same directory as the executing assembly
BaseDirectory,

View file

@ -44,6 +44,7 @@ namespace Microsoft.DotNet.Cli.Utils
var compositeCommandResolver = new CompositeCommandResolver();
compositeCommandResolver.AddCommandResolver(new MuxerCommandResolver());
compositeCommandResolver.AddCommandResolver(new DotnetToolsCommandResolver());
compositeCommandResolver.AddCommandResolver(new RootedCommandResolver());
compositeCommandResolver.AddCommandResolver(
new ProjectToolsCommandResolver(packagedCommandSpecFactory, environment));

View file

@ -0,0 +1,89 @@
// 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.IO;
using System.Reflection;
using System.Collections.Generic;
using Microsoft.DotNet.PlatformAbstractions;
namespace Microsoft.DotNet.Cli.Utils
{
public class DotnetToolsCommandResolver : ICommandResolver
{
private string _dotnetToolPath;
public DotnetToolsCommandResolver(string dotnetToolPath = null)
{
if (dotnetToolPath == null)
{
_dotnetToolPath = Path.Combine(ApplicationEnvironment.ApplicationBasePath,
"DotnetTools");
}
else
{
_dotnetToolPath = dotnetToolPath;
}
}
public CommandSpec Resolve(CommandResolverArguments arguments)
{
if (string.IsNullOrEmpty(arguments.CommandName))
{
return null;
}
var packageId = new DirectoryInfo(Path.Combine(_dotnetToolPath, arguments.CommandName));
if (!packageId.Exists)
{
return null;
}
var version = packageId.GetDirectories()[0];
var dll = version.GetDirectories("tools")[0]
.GetDirectories()[0] // TFM
.GetDirectories()[0] // RID
.GetFiles($"{arguments.CommandName}.dll")[0];
return CreatePackageCommandSpecUsingMuxer(
dll.FullName,
arguments.CommandArguments,
CommandResolutionStrategy.DotnetToolsPackage);
}
private CommandSpec CreatePackageCommandSpecUsingMuxer(
string commandPath,
IEnumerable<string> commandArguments,
CommandResolutionStrategy commandResolutionStrategy)
{
var arguments = new List<string>();
var muxer = new Muxer();
var host = muxer.MuxerPath;
if (host == null)
{
throw new Exception(LocalizableStrings.UnableToLocateDotnetMultiplexer);
}
arguments.Add(commandPath);
if (commandArguments != null)
{
arguments.AddRange(commandArguments);
}
return CreateCommandSpec(host, arguments, commandResolutionStrategy);
}
private CommandSpec CreateCommandSpec(
string commandPath,
IEnumerable<string> commandArguments,
CommandResolutionStrategy commandResolutionStrategy)
{
var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(commandArguments);
return new CommandSpec(commandPath, escapedArgs, commandResolutionStrategy);
}
}
}

View file

@ -179,8 +179,8 @@
<TemplatesToBundle Include="$(RepoRoot)/build/BundledTemplates.proj">
<Properties>
TemplateLayoutDirectory=$(SdkOutputDirectory)/Templates;
TemplatePackageName=%(BundledTemplate.Identity);
TemplatePackageVersion=%(BundledTemplate.Version);
TemplateFillInPackageName=%(BundledTemplate.Identity);
TemplateFillInPackageVersion=%(BundledTemplate.Version);
PreviousStageDirectory=$(PreviousStageDirectory)
</Properties>
</TemplatesToBundle>
@ -192,6 +192,25 @@
</MSBuild>
</Target>
<Target Name="PublishDotnetTools"
AfterTargets="Publish">
<ItemGroup>
<DotnetToolsToBundle Include="$(RepoRoot)/build/BundledDotnetTools.proj">
<Properties>
DotnetToolsLayoutDirectory=$(SdkOutputDirectory)/DotnetTools;
TemplateFillInPackageName=%(BundledDotnetTools.Identity);
TemplateFillInPackageVersion=%(BundledDotnetTools.Version);
PreviousStageDirectory=$(PreviousStageDirectory)
</Properties>
</DotnetToolsToBundle>
</ItemGroup>
<MSBuild
BuildInParallel="False"
Projects="@(DotnetToolsToBundle)">
</MSBuild>
</Target>
<Target Name="PublishLzmaArchive"
Condition="'$(CLIBUILD_SKIP_LZMA)' != 'true'"
DependsOnTargets="GetNuGetPackagesArchive"

View file

@ -0,0 +1,17 @@
using FluentAssertions;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
namespace EndToEnd
{
public class GivenDotnetUsesDotnetTools : TestBase
{
[Fact]
public void ThenOneDotnetToolsCanBeCalled()
{
new DotnetCommand()
.ExecuteWithCapturedOutput("dev-certs --help")
.Should().Pass();
}
}
}

View file

@ -0,0 +1,73 @@
// 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 Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
using System;
using System.IO;
using FluentAssertions;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
namespace Microsoft.DotNet.Cli.InsertionTests
{
public class GivenRestoreDotnetTools : TestBase
{
// Assets are placed during build of this project
private static string GetDotnetToolPath() =>
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestDotnetToolsLayoutDirectory");
private IEnumerable<DirectoryInfo> GetDotnetToolDirectory() =>
new DirectoryInfo(GetDotnetToolPath()).GetDirectories().Where(d => d.Name.StartsWith("dotnet-"));
[Fact]
public void Then_there_is_DotnetTools()
{
new DirectoryInfo(GetDotnetToolPath()).GetDirectories().Should().Contain(d => d.Name.StartsWith("dotnet-"));
}
[Fact]
public void Then_there_is_only_1_version()
{
foreach (var packageFolder in GetDotnetToolDirectory())
{
packageFolder.GetDirectories().Should().HaveCount(1);
}
}
[Fact]
public void Then_there_is_only_1_tfm()
{
foreach (var packageFolder in GetDotnetToolDirectory())
{
packageFolder.GetDirectories()[0]
.GetDirectories("tools")[0]
.GetDirectories().Should().HaveCount(1);
}
}
[Fact]
public void Then_there_is_only_1_rid()
{
foreach (var packageFolder in GetDotnetToolDirectory())
{
packageFolder.GetDirectories()[0]
.GetDirectories("tools")[0]
.GetDirectories()[0]
.GetDirectories().Should().HaveCount(1);
}
}
[Fact]
public void Then_packageName_is_the_same_as_dll()
{
foreach (var packageFolder in GetDotnetToolDirectory())
{
var packageId = packageFolder.Name;
packageFolder.GetDirectories()[0].GetDirectories("tools")[0].GetDirectories()[0].GetDirectories()[0]
.GetFiles()
.Should().Contain(f => string.Equals(f.Name, $"{packageId}.dll", StringComparison.Ordinal));
}
}
}
}

View file

@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFramework>$(CliTargetFramework)</TargetFramework>
<RuntimeFrameworkVersion>$(MicrosoftNETCoreAppPackageVersion)</RuntimeFrameworkVersion>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<AssemblyName>msbuild.IntegrationTests</AssemblyName>
<AssetTargetFallback>$(AssetTargetFallback);dotnet5.4;portable-net451+win8</AssetTargetFallback>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.DotNet.Tools.Tests.Utilities\Microsoft.DotNet.Tools.Tests.Utilities.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<PackageReference Include="xunit" Version="2.2.0" />
</ItemGroup>
<Target Name="CreateDotnetToolsTestAsset" AfterTargets="Build">
<PropertyGroup>
<testAssetSourceRoot>$(OutputPath)/TestDotnetToolsLayoutDirectory</testAssetSourceRoot>
</PropertyGroup>
<ItemGroup>
<TestDotnetTools Include="$(RepoRoot)/build/BundledDotnetTools.proj">
<Properties>
DotnetToolsLayoutDirectory=$(SdkOutputDirectory)/DotnetTools;
TemplateFillInPackageName=%(BundledDotnetTools.Identity);
TemplateFillInPackageVersion=%(BundledDotnetTools.Version);
PreviousStageDirectory=$(PreviousStageDirectory);
DotnetToolsLayoutDirectory=$(testAssetSourceRoot);
DotnetToolsRestoreProjectStyle=DotnetToolReference
</Properties>
</TestDotnetTools>
</ItemGroup>
<MSBuild BuildInParallel="False" Projects="@(TestDotnetTools)" />
</Target>
</Project>

View file

@ -1,6 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2008
VisualStudioVersion = 15.0.27130.2024
MinimumVisualStudioVersion = 10.0.40219.1
Project("{13B669BE-BB05-4DDF-9536-439F39A36129}") = "dotnet-add-reference.Tests", "dotnet-add-reference.Tests\dotnet-add-reference.Tests.csproj", "{AB63A3E5-76A3-4EE9-A380-8E0C7B7644DC}"
EndProject
@ -90,6 +91,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Tools.Test
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-install-tool.Tests", "dotnet-install-tool.Tests\dotnet-install-tool.Tests.csproj", "{E2F5F115-0DE4-4CC0-920C-EF6F89D15EAB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InsertionTests", "InsertionTests\InsertionTests.csproj", "{A9713391-3D44-4664-9C41-75765218FD6C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -592,6 +595,18 @@ Global
{E2F5F115-0DE4-4CC0-920C-EF6F89D15EAB}.Release|x64.Build.0 = Release|Any CPU
{E2F5F115-0DE4-4CC0-920C-EF6F89D15EAB}.Release|x86.ActiveCfg = Release|Any CPU
{E2F5F115-0DE4-4CC0-920C-EF6F89D15EAB}.Release|x86.Build.0 = Release|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Debug|x64.ActiveCfg = Debug|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Debug|x64.Build.0 = Debug|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Debug|x86.ActiveCfg = Debug|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Debug|x86.Build.0 = Debug|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Release|Any CPU.Build.0 = Release|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Release|x64.ActiveCfg = Release|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Release|x64.Build.0 = Release|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Release|x86.ActiveCfg = Release|Any CPU
{A9713391-3D44-4664-9C41-75765218FD6C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -17,13 +17,14 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
var resolvers = defaultCommandResolver.OrderedCommandResolvers;
resolvers.Should().HaveCount(7);
resolvers.Should().HaveCount(8);
resolvers.Select(r => r.GetType())
.Should()
.ContainInOrder(
new []{
typeof(MuxerCommandResolver),
typeof(DotnetToolsCommandResolver),
typeof(RootedCommandResolver),
typeof(ProjectToolsCommandResolver),
typeof(AppBaseDllCommandResolver),

View file

@ -0,0 +1,68 @@
// 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 FluentAssertions;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
using System.Reflection;
namespace Microsoft.DotNet.Tests
{
public class GivenADotnetToolsCommandResolver : TestBase
{
private readonly DotnetToolsCommandResolver _dotnetToolsCommandResolver;
// Assets are placed during build of this project
private static string GetDotnetToolPath() => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestDotnetToolsLayoutDirectory");
public GivenADotnetToolsCommandResolver()
{
_dotnetToolsCommandResolver = new DotnetToolsCommandResolver(GetDotnetToolPath());
}
[Fact]
public void ItReturnsNullWhenCommandNameIsNull()
{
var commandResolverArguments = new CommandResolverArguments()
{
CommandName = null,
};
var result = _dotnetToolsCommandResolver.Resolve(commandResolverArguments);
result.Should().BeNull();
}
[Fact]
public void ItReturnsNullWhenCommandNameDoesNotExistInProjectTools()
{
var commandResolverArguments = new CommandResolverArguments()
{
CommandName = "nonexistent-command",
};
var result = _dotnetToolsCommandResolver.Resolve(commandResolverArguments);
result.Should().BeNull();
}
[Fact]
public void ItReturnsACommandSpec()
{
var commandResolverArguments = new CommandResolverArguments()
{
CommandName = "dotnet-watch",
};
var result = _dotnetToolsCommandResolver.Resolve(commandResolverArguments);
result.Should().NotBeNull();
var commandPath = result.Args.Trim('"');
commandPath.Should().Contain("dotnet-watch.dll");
}
}
}

View file

@ -40,4 +40,26 @@
<PackageReference Include="Microsoft.Build.Runtime" Version="$(MicrosoftBuildRuntimePackageVersion)" />
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
</ItemGroup>
<Target Name="CreateDotnetToolsTestAsset" AfterTargets="Build">
<PropertyGroup>
<testAssetSourceRoot>$(OutputPath)/TestDotnetToolsLayoutDirectory</testAssetSourceRoot>
</PropertyGroup>
<ItemGroup>
<TestDotnetTools Include="$(RepoRoot)/build/BundledDotnetTools.proj">
<Properties>
DotnetToolsLayoutDirectory=$(SdkOutputDirectory)/DotnetTools;
TemplateFillInPackageName=dotnet-watch;
TemplateFillInPackageVersion=$(AspNetCoreVersion);
PreviousStageDirectory=$(PreviousStageDirectory);
DotnetToolsLayoutDirectory=$(testAssetSourceRoot);
DotnetToolsRestoreProjectStyle=DotnetToolReference
</Properties>
</TestDotnetTools>
</ItemGroup>
<MSBuild BuildInParallel="False" Projects="@(TestDotnetTools)" />
</Target>
</Project>