Merge pull request #4300 from livarcocc/support_for_toolsref

Support for toolsref
This commit is contained in:
Livar 2016-10-05 13:18:12 -07:00 committed by GitHub
commit 7a0dff8a96
25 changed files with 840 additions and 80 deletions

View file

@ -21,5 +21,11 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<DotNetCliToolsReference Include="dotnet-portable">
<Version>1.0.0</Version>
</DotNetCliToolsReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -63,7 +63,28 @@ namespace Microsoft.DotNet.Cli.Utils
string outputPath = null,
string applicationName = null)
{
var commandSpec = CommandResolver.TryResolveCommandSpec(commandName,
return Create(
new DefaultCommandResolverPolicy(),
commandName,
args,
framework,
configuration,
outputPath,
applicationName);
}
public static Command Create(
ICommandResolverPolicy commandResolverPolicy,
string commandName,
IEnumerable<string> args,
NuGetFramework framework = null,
string configuration = Constants.DefaultConfiguration,
string outputPath = null,
string applicationName = null)
{
var commandSpec = CommandResolver.TryResolveCommandSpec(
commandResolverPolicy,
commandName,
args,
framework,
configuration: configuration,

View file

@ -2,8 +2,13 @@ using Microsoft.DotNet.PlatformAbstractions;
namespace Microsoft.DotNet.Cli.Utils
{
public class DefaultCommandResolverPolicy
public class DefaultCommandResolverPolicy : ICommandResolverPolicy
{
public CompositeCommandResolver CreateCommandResolver()
{
return Create();
}
public static CompositeCommandResolver Create()
{
var environment = new EnvironmentProvider();
@ -37,7 +42,6 @@ namespace Microsoft.DotNet.Cli.Utils
compositeCommandResolver.AddCommandResolver(new MuxerCommandResolver());
compositeCommandResolver.AddCommandResolver(new RootedCommandResolver());
compositeCommandResolver.AddCommandResolver(new ProjectToolsCommandResolver(packagedCommandSpecFactory));
compositeCommandResolver.AddCommandResolver(new AppBaseDllCommandResolver());
compositeCommandResolver.AddCommandResolver(
new AppBaseCommandResolver(environment, platformCommandSpecFactory));

View file

@ -0,0 +1,7 @@
namespace Microsoft.DotNet.Cli.Utils
{
public interface ICommandResolverPolicy
{
CompositeCommandResolver CreateCommandResolver();
}
}

View file

@ -5,7 +5,7 @@ using NuGet.Frameworks;
namespace Microsoft.DotNet.Cli.Utils
{
internal static class CommandResolver
internal class CommandResolver
{
public static CommandSpec TryResolveCommandSpec(
string commandName,
@ -14,6 +14,25 @@ namespace Microsoft.DotNet.Cli.Utils
string configuration = Constants.DefaultConfiguration,
string outputPath = null,
string applicationName = null)
{
return TryResolveCommandSpec(
new DefaultCommandResolverPolicy(),
commandName,
args,
framework,
configuration,
outputPath,
applicationName);
}
public static CommandSpec TryResolveCommandSpec(
ICommandResolverPolicy commandResolverPolicy,
string commandName,
IEnumerable<string> args,
NuGetFramework framework = null,
string configuration = Constants.DefaultConfiguration,
string outputPath = null,
string applicationName = null)
{
var commandResolverArgs = new CommandResolverArguments
{
@ -26,7 +45,7 @@ namespace Microsoft.DotNet.Cli.Utils
ApplicationName = applicationName
};
var defaultCommandResolver = DefaultCommandResolverPolicy.Create();
var defaultCommandResolver = commandResolverPolicy.CreateCommandResolver();
return defaultCommandResolver.Resolve(commandResolverArgs);
}

View file

@ -0,0 +1,315 @@
// 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.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.DependencyModel;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.ProjectModel;
namespace Microsoft.DotNet.Cli.CommandResolution
{
internal class DepsJsonBuilder
{
private readonly VersionFolderPathResolver _versionFolderPathResolver;
public DepsJsonBuilder()
{
// This resolver is only used for building file names, so that base path is not required.
_versionFolderPathResolver = new VersionFolderPathResolver(path: null);
}
public DependencyContext Build(
SingleProjectInfo mainProjectInfo,
CompilationOptions compilationOptions,
LockFile lockFile,
NuGetFramework framework,
string runtime)
{
bool includeCompilationLibraries = compilationOptions != null;
LockFileTarget lockFileTarget = lockFile.GetTarget(framework, runtime);
IEnumerable<LockFileTargetLibrary> runtimeExports = lockFileTarget.GetRuntimeLibraries();
IEnumerable<LockFileTargetLibrary> compilationExports =
includeCompilationLibraries ?
lockFileTarget.GetCompileLibraries() :
Enumerable.Empty<LockFileTargetLibrary>();
var dependencyLookup = compilationExports
.Concat(runtimeExports)
.Distinct()
.Select(library => new Dependency(library.Name, library.Version.ToString()))
.ToDictionary(dependency => dependency.Name, StringComparer.OrdinalIgnoreCase);
var libraryLookup = lockFile.Libraries.ToDictionary(l => l.Name, StringComparer.OrdinalIgnoreCase);
var runtimeSignature = GenerateRuntimeSignature(runtimeExports);
IEnumerable<RuntimeLibrary> runtimeLibraries =
GetLibraries(runtimeExports, libraryLookup, dependencyLookup, runtime: true).Cast<RuntimeLibrary>();
IEnumerable<CompilationLibrary> compilationLibraries;
if (includeCompilationLibraries)
{
CompilationLibrary projectCompilationLibrary = GetProjectCompilationLibrary(
mainProjectInfo,
lockFile,
lockFileTarget,
dependencyLookup);
compilationLibraries = new[] { projectCompilationLibrary }
.Concat(
GetLibraries(compilationExports, libraryLookup, dependencyLookup, runtime: false)
.Cast<CompilationLibrary>());
}
else
{
compilationLibraries = Enumerable.Empty<CompilationLibrary>();
}
return new DependencyContext(
new TargetInfo(framework.DotNetFrameworkName, runtime, runtimeSignature, lockFileTarget.IsPortable()),
compilationOptions ?? CompilationOptions.Default,
compilationLibraries,
runtimeLibraries,
new RuntimeFallbacks[] { });
}
private static string GenerateRuntimeSignature(IEnumerable<LockFileTargetLibrary> runtimeExports)
{
var sha1 = SHA1.Create();
var builder = new StringBuilder();
var packages = runtimeExports
.Where(libraryExport => libraryExport.Type == "package");
var separator = "|";
foreach (var libraryExport in packages)
{
builder.Append(libraryExport.Name);
builder.Append(separator);
builder.Append(libraryExport.Version.ToString());
builder.Append(separator);
}
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(builder.ToString()));
builder.Clear();
foreach (var hashByte in hash)
{
builder.AppendFormat("{0:x2}", hashByte);
}
return builder.ToString();
}
private List<Dependency> GetProjectDependencies(
LockFile lockFile,
LockFileTarget lockFileTarget,
Dictionary<string, Dependency> dependencyLookup)
{
List<Dependency> dependencies = new List<Dependency>();
IEnumerable<ProjectFileDependencyGroup> projectFileDependencies = lockFile
.ProjectFileDependencyGroups
.Where(dg => dg.FrameworkName == string.Empty ||
dg.FrameworkName == lockFileTarget.TargetFramework.DotNetFrameworkName);
foreach (string projectFileDependency in projectFileDependencies.SelectMany(dg => dg.Dependencies))
{
int separatorIndex = projectFileDependency.IndexOf(' ');
string dependencyName = separatorIndex > 0 ?
projectFileDependency.Substring(0, separatorIndex) :
projectFileDependency;
Dependency dependency;
if (dependencyLookup.TryGetValue(dependencyName, out dependency))
{
dependencies.Add(dependency);
}
}
return dependencies;
}
private RuntimeLibrary GetProjectRuntimeLibrary(
SingleProjectInfo projectInfo,
LockFile lockFile,
LockFileTarget lockFileTarget,
Dictionary<string, Dependency> dependencyLookup)
{
RuntimeAssetGroup[] runtimeAssemblyGroups = new[] { new RuntimeAssetGroup(string.Empty, projectInfo.GetOutputName()) };
List<Dependency> dependencies = GetProjectDependencies(lockFile, lockFileTarget, dependencyLookup);
ResourceAssembly[] resourceAssemblies = projectInfo
.ResourceAssemblies
.Select(r => new ResourceAssembly(r.RelativePath, r.Culture))
.ToArray();
return new RuntimeLibrary(
type: "project",
name: projectInfo.Name,
version: projectInfo.Version,
hash: string.Empty,
runtimeAssemblyGroups: runtimeAssemblyGroups,
nativeLibraryGroups: new RuntimeAssetGroup[] { },
resourceAssemblies: resourceAssemblies,
dependencies: dependencies.ToArray(),
serviceable: false);
}
private CompilationLibrary GetProjectCompilationLibrary(
SingleProjectInfo projectInfo,
LockFile lockFile,
LockFileTarget lockFileTarget,
Dictionary<string, Dependency> dependencyLookup)
{
List<Dependency> dependencies = GetProjectDependencies(lockFile, lockFileTarget, dependencyLookup);
return new CompilationLibrary(
type: "project",
name: projectInfo.Name,
version: projectInfo.Version,
hash: string.Empty,
assemblies: new[] { projectInfo.GetOutputName() },
dependencies: dependencies.ToArray(),
serviceable: false);
}
private IEnumerable<Library> GetLibraries(
IEnumerable<LockFileTargetLibrary> exports,
IDictionary<string, LockFileLibrary> libraryLookup,
IDictionary<string, Dependency> dependencyLookup,
bool runtime)
{
return exports.Select(export => GetLibrary(export, libraryLookup, dependencyLookup, runtime));
}
private Library GetLibrary(
LockFileTargetLibrary export,
IDictionary<string, LockFileLibrary> libraryLookup,
IDictionary<string, Dependency> dependencyLookup,
bool runtime)
{
var type = export.Type;
// TEMPORARY: All packages are serviceable in RC2
// See https://github.com/dotnet/cli/issues/2569
var serviceable = export.Type == "package";
var libraryDependencies = new HashSet<Dependency>();
foreach (PackageDependency libraryDependency in export.Dependencies)
{
Dependency dependency;
if (dependencyLookup.TryGetValue(libraryDependency.Id, out dependency))
{
libraryDependencies.Add(dependency);
}
}
string hash = string.Empty;
string path = null;
string hashPath = null;
LockFileLibrary library;
if (libraryLookup.TryGetValue(export.Name, out library))
{
if (!string.IsNullOrEmpty(library.Sha512))
{
hash = "sha512-" + library.Sha512;
hashPath = _versionFolderPathResolver.GetHashFileName(export.Name, export.Version);
}
path = library.Path;
}
if (runtime)
{
return new RuntimeLibrary(
type.ToLowerInvariant(),
export.Name,
export.Version.ToString(),
hash,
CreateRuntimeAssemblyGroups(export),
CreateNativeLibraryGroups(export),
export.ResourceAssemblies.FilterPlaceHolderFiles().Select(CreateResourceAssembly),
libraryDependencies,
serviceable,
path,
hashPath);
}
else
{
IEnumerable<string> assemblies = export
.CompileTimeAssemblies
.FilterPlaceHolderFiles()
.Select(libraryAsset => libraryAsset.Path);
return new CompilationLibrary(
type.ToString().ToLowerInvariant(),
export.Name,
export.Version.ToString(),
hash,
assemblies,
libraryDependencies,
serviceable,
path,
hashPath);
}
}
private IReadOnlyList<RuntimeAssetGroup> CreateRuntimeAssemblyGroups(LockFileTargetLibrary export)
{
List<RuntimeAssetGroup> assemblyGroups = new List<RuntimeAssetGroup>();
assemblyGroups.Add(
new RuntimeAssetGroup(
string.Empty,
export.RuntimeAssemblies.FilterPlaceHolderFiles().Select(a => a.Path)));
foreach (var runtimeTargetsGroup in export.GetRuntimeTargetsGroups("runtime"))
{
assemblyGroups.Add(
new RuntimeAssetGroup(
runtimeTargetsGroup.Key,
runtimeTargetsGroup.Select(t => t.Path)));
}
return assemblyGroups;
}
private IReadOnlyList<RuntimeAssetGroup> CreateNativeLibraryGroups(LockFileTargetLibrary export)
{
List<RuntimeAssetGroup> nativeGroups = new List<RuntimeAssetGroup>();
nativeGroups.Add(
new RuntimeAssetGroup(
string.Empty,
export.NativeLibraries.FilterPlaceHolderFiles().Select(a => a.Path)));
foreach (var runtimeTargetsGroup in export.GetRuntimeTargetsGroups("native"))
{
nativeGroups.Add(
new RuntimeAssetGroup(
runtimeTargetsGroup.Key,
runtimeTargetsGroup.Select(t => t.Path)));
}
return nativeGroups;
}
private ResourceAssembly CreateResourceAssembly(LockFileItem resourceAssembly)
{
string locale;
if (!resourceAssembly.Properties.TryGetValue("locale", out locale))
{
locale = null;
}
return new ResourceAssembly(resourceAssembly.Path, locale);
}
}
}

View file

@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using NuGet.ProjectModel;
namespace Microsoft.DotNet.Cli.CommandResolution
{
internal interface IProject
{
LockFile GetLockFile();
IEnumerable<SingleProjectInfo> GetTools();
}
}

View file

@ -0,0 +1,101 @@
// 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.Collections.Generic;
using System.Linq;
using NuGet.Packaging.Core;
using NuGet.ProjectModel;
namespace Microsoft.DotNet.Cli.CommandResolution
{
internal static class LockFileTargetExtensions
{
public static bool IsPortable(this LockFileTarget lockFileTarget)
{
return string.IsNullOrEmpty(lockFileTarget.RuntimeIdentifier) &&
lockFileTarget.GetPlatformLibrary() != null;
}
public static LockFileTargetLibrary GetPlatformLibrary(this LockFileTarget lockFileTarget)
{
// TODO: https://github.com/dotnet/sdk/issues/17 get this from the lock file
var platformPackageName = "Microsoft.NETCore.App";
var platformLibrary = lockFileTarget
.Libraries
.FirstOrDefault(e => e.Name.Equals(platformPackageName, StringComparison.OrdinalIgnoreCase));
return platformLibrary;
}
public static HashSet<string> GetPlatformExclusionList(
this LockFileTarget lockFileTarget,
IDictionary<string, LockFileTargetLibrary> libraryLookup)
{
var platformLibrary = lockFileTarget.GetPlatformLibrary();
var exclusionList = new HashSet<string>();
exclusionList.Add(platformLibrary.Name);
CollectDependencies(libraryLookup, platformLibrary.Dependencies, exclusionList);
return exclusionList;
}
public static IEnumerable<LockFileTargetLibrary> GetRuntimeLibraries(this LockFileTarget lockFileTarget)
{
IEnumerable<LockFileTargetLibrary> runtimeLibraries = lockFileTarget.Libraries;
Dictionary<string, LockFileTargetLibrary> libraryLookup =
runtimeLibraries.ToDictionary(e => e.Name, StringComparer.OrdinalIgnoreCase);
HashSet<string> allExclusionList = new HashSet<string>();
if (lockFileTarget.IsPortable())
{
allExclusionList.UnionWith(lockFileTarget.GetPlatformExclusionList(libraryLookup));
}
return runtimeLibraries.Filter(allExclusionList).ToArray();
}
public static IEnumerable<LockFileTargetLibrary> GetCompileLibraries(this LockFileTarget lockFileTarget)
{
return lockFileTarget.Libraries;
}
public static IEnumerable<LockFileTargetLibrary> Filter(
this IEnumerable<LockFileTargetLibrary> libraries,
HashSet<string> exclusionList)
{
return libraries.Where(e => !exclusionList.Contains(e.Name));
}
public static IEnumerable<IGrouping<string, LockFileRuntimeTarget>> GetRuntimeTargetsGroups(
this LockFileTargetLibrary library,
string assetType)
{
return library.RuntimeTargets
.FilterPlaceHolderFiles()
.Cast<LockFileRuntimeTarget>()
.Where(t => string.Equals(t.AssetType, assetType, StringComparison.OrdinalIgnoreCase))
.GroupBy(t => t.Runtime);
}
private static void CollectDependencies(
IDictionary<string, LockFileTargetLibrary> libraryLookup,
IEnumerable<PackageDependency> dependencies,
HashSet<string> exclusionList)
{
foreach (PackageDependency dependency in dependencies)
{
LockFileTargetLibrary library = libraryLookup[dependency.Id];
if (library.Version.Equals(dependency.VersionRange.MinVersion))
{
if (exclusionList.Add(library.Name))
{
CollectDependencies(libraryLookup, library.Dependencies, exclusionList);
}
}
}
}
}
}

View file

@ -0,0 +1,48 @@
// 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.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Evaluation;
using NuGet.ProjectModel;
namespace Microsoft.DotNet.Cli.CommandResolution
{
internal class MSBuildProject : IProject
{
private Project _project;
public MSBuildProject(string msBuildProjectPath)
{
var globalProperties = new Dictionary<string, string>()
{
{ "MSBuildExtensionsPath", AppContext.BaseDirectory }
};
_project = new Project(msBuildProjectPath, globalProperties, null);
}
public LockFile GetLockFile()
{
var intermediateOutputPath = _project
.AllEvaluatedProperties
.FirstOrDefault(p => p.Name.Equals("BaseIntermediateOutputPath"))
.EvaluatedValue;
var lockFilePath = Path.Combine(intermediateOutputPath, "project.assets.json");
return new LockFileFormat().Read(lockFilePath);
}
public IEnumerable<SingleProjectInfo> GetTools()
{
var toolsReferences = _project.AllEvaluatedItems.Where(i => i.ItemType.Equals("DotNetCliToolsReference"));
var tools = toolsReferences.Select(t => new SingleProjectInfo(
t.EvaluatedInclude,
t.GetMetadataValue("Version"),
Enumerable.Empty<ResourceAssemblyInfo>()));
return tools;
}
}
}

View file

@ -0,0 +1,25 @@
// 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.Collections.Generic;
using System.IO;
using System.Linq;
using NuGet.Packaging.Core;
using NuGet.ProjectModel;
namespace Microsoft.DotNet.Cli.CommandResolution
{
internal static class NuGetUtils
{
public static bool IsPlaceholderFile(string path)
{
return string.Equals(Path.GetFileName(path), PackagingCoreConstants.EmptyFolder, StringComparison.Ordinal);
}
public static IEnumerable<LockFileItem> FilterPlaceHolderFiles(this IEnumerable<LockFileItem> files)
{
return files.Where(f => !IsPlaceholderFile(f.Path));
}
}
}

View file

@ -0,0 +1,50 @@
// 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;
namespace Microsoft.DotNet.Cli.CommandResolution
{
internal class ProjectFactory
{
public IProject GetProject(string projectDirectory)
{
return GetMSBuildProj(projectDirectory) ??
GetProjectJsonProject(projectDirectory);
}
private IProject GetMSBuildProj(string projectDirectory)
{
string msBuildProjectPath = GetMSBuildProjPath(projectDirectory);
if(msBuildProjectPath == null)
{
return null;
}
return new MSBuildProject(msBuildProjectPath);
}
private IProject GetProjectJsonProject(string projectDirectory)
{
return new ProjectJsonProject(projectDirectory);
}
private string GetMSBuildProjPath(string projectDirectory)
{
string[] projectFiles = Directory.GetFiles(projectDirectory, "*.*proj");
if (projectFiles.Length == 0)
{
return null;
}
else if (projectFiles.Length > 1)
{
throw new InvalidOperationException(
$"Specify which project file to use because this '{projectDirectory}' contains more than one project file.");
}
return projectFiles[0];
}
}
}

View file

@ -0,0 +1,37 @@
// 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.Collections.Generic;
using System.IO;
using System.Linq;
using NuGet.ProjectModel;
namespace Microsoft.DotNet.Cli.CommandResolution
{
internal class ProjectJsonProject : IProject
{
private LockFile _lockFile;
public ProjectJsonProject(string projectDirectory)
{
var lockFilePath = Path.Combine(projectDirectory, LockFileFormat.LockFileName);
_lockFile = new LockFileFormat().Read(lockFilePath);
}
public LockFile GetLockFile()
{
return _lockFile;
}
public IEnumerable<SingleProjectInfo> GetTools()
{
var tools = _lockFile.Tools.Where(t => t.Name.Contains(".NETCoreApp")).SelectMany(t => t.Libraries);
return tools.Select(t => new SingleProjectInfo(
t.Name,
t.Version.ToFullString(),
Enumerable.Empty<ResourceAssemblyInfo>()));
}
}
}

View file

@ -1,17 +1,21 @@
using System;
// 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.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.InternalAbstractions;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Tools.Common;
using Microsoft.Extensions.DependencyModel;
using NuGet.Frameworks;
using NuGet.LibraryModel;
using NuGet.ProjectModel;
using NuGet.Versioning;
using FileFormatException = Microsoft.DotNet.ProjectModel.FileFormatException;
namespace Microsoft.DotNet.Cli.Utils
namespace Microsoft.DotNet.Cli.CommandResolution
{
public class ProjectToolsCommandResolver : ICommandResolver
{
@ -52,31 +56,26 @@ namespace Microsoft.DotNet.Cli.Utils
IEnumerable<string> args,
string projectDirectory)
{
var projectContext = GetProjectContextFromDirectoryForFirstTarget(projectDirectory);
if (projectContext == null)
{
return null;
}
var toolsLibraries = projectContext.ProjectFile.Tools.OrEmptyIfNull();
var projectFactory = new ProjectFactory();
var project = projectFactory.GetProject(projectDirectory);
var tools = project.GetTools();
return ResolveCommandSpecFromAllToolLibraries(
toolsLibraries,
tools,
commandName,
args,
projectContext);
project.GetLockFile());
}
private CommandSpec ResolveCommandSpecFromAllToolLibraries(
IEnumerable<LibraryDependency> toolsLibraries,
IEnumerable<SingleProjectInfo> toolsLibraries,
string commandName,
IEnumerable<string> args,
ProjectContext projectContext)
LockFile lockFile)
{
foreach (var toolLibrary in toolsLibraries)
{
var commandSpec = ResolveCommandSpecFromToolLibrary(toolLibrary.LibraryRange, commandName, args, projectContext);
var commandSpec = ResolveCommandSpecFromToolLibrary(toolLibrary, commandName, args, lockFile);
if (commandSpec != null)
{
@ -88,17 +87,18 @@ namespace Microsoft.DotNet.Cli.Utils
}
private CommandSpec ResolveCommandSpecFromToolLibrary(
LibraryRange toolLibraryRange,
SingleProjectInfo toolLibraryRange,
string commandName,
IEnumerable<string> args,
ProjectContext projectContext)
LockFile lockFile)
{
var nugetPackagesRoot = projectContext.PackagesDirectory;
var nugetPackagesRoot = lockFile.PackageFolders.First().Path;
var lockFile = GetToolLockFile(toolLibraryRange, nugetPackagesRoot);
var toolLockFile = GetToolLockFile(toolLibraryRange, nugetPackagesRoot);
var toolLibrary = lockFile.Targets
.FirstOrDefault(t => t.TargetFramework.GetShortFolderName().Equals(s_toolPackageFramework.GetShortFolderName()))
var toolLibrary = toolLockFile.Targets
.FirstOrDefault(
t => t.TargetFramework.GetShortFolderName().Equals(s_toolPackageFramework.GetShortFolderName()))
?.Libraries.FirstOrDefault(l => l.Name == toolLibraryRange.Name);
if (toolLibrary == null)
@ -106,8 +106,8 @@ namespace Microsoft.DotNet.Cli.Utils
return null;
}
var depsFileRoot = Path.GetDirectoryName(lockFile.Path);
var depsFilePath = GetToolDepsFilePath(toolLibraryRange, lockFile, depsFileRoot);
var depsFileRoot = Path.GetDirectoryName(toolLockFile.Path);
var depsFilePath = GetToolDepsFilePath(toolLibraryRange, toolLockFile, depsFileRoot);
var normalizedNugetPackagesRoot = PathUtility.EnsureNoTrailingDirectorySeparator(nugetPackagesRoot);
@ -123,7 +123,7 @@ namespace Microsoft.DotNet.Cli.Utils
}
private LockFile GetToolLockFile(
LibraryRange toolLibrary,
SingleProjectInfo toolLibrary,
string nugetPackagesRoot)
{
var lockFilePath = GetToolLockFilePath(toolLibrary, nugetPackagesRoot);
@ -148,36 +148,19 @@ namespace Microsoft.DotNet.Cli.Utils
}
private string GetToolLockFilePath(
LibraryRange toolLibrary,
SingleProjectInfo toolLibrary,
string nugetPackagesRoot)
{
var toolPathCalculator = new ToolPathCalculator(nugetPackagesRoot);
return toolPathCalculator.GetBestLockFilePath(
toolLibrary.Name,
toolLibrary.VersionRange,
new VersionRange(new NuGetVersion(toolLibrary.Version)),
s_toolPackageFramework);
}
private ProjectContext GetProjectContextFromDirectoryForFirstTarget(string projectRootPath)
{
if (projectRootPath == null)
{
return null;
}
if (!File.Exists(Path.Combine(projectRootPath, Project.FileName)))
{
return null;
}
var projectContext = ProjectContext.CreateContextForEachTarget(projectRootPath).FirstOrDefault();
return projectContext;
}
private string GetToolDepsFilePath(
LibraryRange toolLibrary,
SingleProjectInfo toolLibrary,
LockFile toolLockFile,
string depsPathRoot)
{
@ -185,42 +168,32 @@ namespace Microsoft.DotNet.Cli.Utils
depsPathRoot,
toolLibrary.Name + FileNameSuffixes.DepsJson);
EnsureToolJsonDepsFileExists(toolLockFile, depsJsonPath);
EnsureToolJsonDepsFileExists(toolLockFile, depsJsonPath, toolLibrary);
return depsJsonPath;
}
private void EnsureToolJsonDepsFileExists(
LockFile toolLockFile,
string depsPath)
string depsPath,
SingleProjectInfo toolLibrary)
{
if (!File.Exists(depsPath))
{
GenerateDepsJsonFile(toolLockFile, depsPath);
GenerateDepsJsonFile(toolLockFile, depsPath, toolLibrary);
}
}
// Need to unit test this, so public
public void GenerateDepsJsonFile(
internal void GenerateDepsJsonFile(
LockFile toolLockFile,
string depsPath)
string depsPath,
SingleProjectInfo toolLibrary)
{
Reporter.Verbose.WriteLine($"Generating deps.json at: {depsPath}");
var projectContext = new ProjectContextBuilder()
.WithLockFile(toolLockFile)
.WithTargetFramework(s_toolPackageFramework.ToString())
.Build();
var exporter = projectContext.CreateExporter(Constants.DefaultConfiguration);
var dependencyContext = new DependencyContextBuilder()
.Build(null,
null,
exporter.GetAllExports(),
true,
s_toolPackageFramework,
string.Empty);
var dependencyContext = new DepsJsonBuilder()
.Build(toolLibrary, null, toolLockFile, s_toolPackageFramework, null);
var tempDepsFile = Path.GetTempFileName();
using (var fileStream = File.Open(tempDepsFile, FileMode.Open, FileAccess.Write))

View file

@ -0,0 +1,21 @@
// 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.Cli.Utils;
namespace Microsoft.DotNet.Cli.CommandResolution
{
public class ProjectToolsCommandResolverPolicy : ICommandResolverPolicy
{
public CompositeCommandResolver CreateCommandResolver()
{
var defaultCommandResolverPolicy = new DefaultCommandResolverPolicy();
var compositeCommandResolver = defaultCommandResolverPolicy.CreateCommandResolver();
var packagedCommandSpecFactory = new PackagedCommandSpecFactory();
compositeCommandResolver.AddCommandResolver(new ProjectToolsCommandResolver(packagedCommandSpecFactory));
return compositeCommandResolver;
}
}
}

View file

@ -0,0 +1,17 @@
// 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.
namespace Microsoft.DotNet.Cli.CommandResolution
{
internal class ResourceAssemblyInfo
{
public string Culture { get; }
public string RelativePath { get; }
public ResourceAssemblyInfo(string culture, string relativePath)
{
Culture = culture;
RelativePath = relativePath;
}
}
}

View file

@ -0,0 +1,27 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
namespace Microsoft.DotNet.Cli.CommandResolution
{
internal class SingleProjectInfo
{
public string Name { get; }
public string Version { get; }
public IEnumerable<ResourceAssemblyInfo> ResourceAssemblies { get; }
public SingleProjectInfo(string name, string version, IEnumerable<ResourceAssemblyInfo> resourceAssemblies)
{
Name = name;
Version = version;
ResourceAssemblies = resourceAssemblies;
}
public string GetOutputName()
{
return $"{Name}.dll";
}
}
}

View file

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.DotNet.Cli.CommandResolution;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
using Microsoft.DotNet.PlatformAbstractions;
@ -188,7 +189,12 @@ namespace Microsoft.DotNet.Cli
}
else
{
CommandResult result = Command.Create("dotnet-" + command, appArgs, FrameworkConstants.CommonFrameworks.NetStandardApp15)
var projectToolsCommandResolver = new ProjectToolsCommandResolverPolicy();
CommandResult result = Command.Create(
projectToolsCommandResolver,
"dotnet-" + command,
appArgs,
FrameworkConstants.CommonFrameworks.NetStandardApp15)
.Execute();
exitCode = result.ExitCode;
}

View file

@ -43,5 +43,29 @@ namespace Microsoft.DotNet.Tests.EndToEnd
.HaveStdOutContaining("Hello World!");
}
}
[Fact]
public void ItCanRunToolsInACSProj()
{
var testAppName = "MSBuildTestApp";
var testInstance = TestAssetsManager
.CreateTestInstance(testAppName);
var testProjectDirectory = testInstance.TestRoot;
new Restore3Command()
.WithWorkingDirectory(testProjectDirectory)
.Execute()
.Should()
.Pass();
new DotnetCommand()
.WithWorkingDirectory(testInstance.TestRoot)
.ExecuteWithCapturedOutput("portable")
.Should()
.Pass()
.And
.HaveStdOutContaining("Hello Portable World!");;
}
}
}

View file

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

View file

@ -20,10 +20,10 @@
},
"System.Diagnostics.TraceSource": "4.0.0",
"System.Runtime.Serialization.Primitives": "4.1.1",
"NuGet.Versioning": "3.6.0-beta.1.msbuild.15",
"NuGet.Packaging": "3.6.0-beta.1.msbuild.15",
"NuGet.Frameworks": "3.6.0-beta.1.msbuild.15",
"NuGet.ProjectModel": "3.6.0-beta.1.msbuild.15",
"NuGet.Versioning": "3.6.0-beta.1.msbuild.17",
"NuGet.Packaging": "3.6.0-beta.1.msbuild.17",
"NuGet.Frameworks": "3.6.0-beta.1.msbuild.17",
"NuGet.ProjectModel": "3.6.0-beta.1.msbuild.17",
"Microsoft.DotNet.ProjectModel": {
"target": "project"
},

View file

@ -22,7 +22,7 @@
},
"xunit": "2.2.0-beta3-build3330",
"dotnet-test-xunit": "1.0.0-rc2-350904-49",
"NuGet.ProjectModel": "3.6.0-beta.1.msbuild.15"
"NuGet.ProjectModel": "3.6.0-beta.1.msbuild.17"
},
"frameworks": {
"netcoreapp1.0": {

View file

@ -4,6 +4,8 @@
using System.IO;
using System.Linq;
using FluentAssertions;
using Microsoft.DotNet.Cli.CommandResolution;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.TestFramework;
using Microsoft.DotNet.Tools.Test.Utilities;
@ -12,7 +14,7 @@ using NuGet.ProjectModel;
using NuGet.Versioning;
using Xunit;
namespace Microsoft.DotNet.Cli.Utils.Tests
namespace Microsoft.DotNet.Tests
{
public class GivenAProjectToolsCommandResolver : TestBase
{
@ -220,7 +222,10 @@ namespace Microsoft.DotNet.Cli.Utils.Tests
File.WriteAllText(depsJsonFile, "temp");
var projectToolsCommandResolver = SetupProjectToolsCommandResolver();
projectToolsCommandResolver.GenerateDepsJsonFile(lockFile, depsJsonFile);
projectToolsCommandResolver.GenerateDepsJsonFile(
lockFile,
depsJsonFile,
new SingleProjectInfo("dotnet-portable", "1.0.0", Enumerable.Empty<ResourceAssemblyInfo>()));
File.ReadAllText(depsJsonFile).Should().Be("temp");
File.Delete(depsJsonFile);

View file

@ -0,0 +1,39 @@
// 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 FluentAssertions;
using Microsoft.DotNet.Cli.CommandResolution;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
namespace Microsoft.DotNet.Tests
{
public class GivenAProjectToolsCommandResolverPolicy
{
[Fact]
public void It_contains_resolvers_in_the_right_order()
{
var projectToolsCommandResolverPolicy = new ProjectToolsCommandResolverPolicy();
var defaultCommandResolver = projectToolsCommandResolverPolicy.CreateCommandResolver();
var resolvers = defaultCommandResolver.OrderedCommandResolvers;
resolvers.Should().HaveCount(7);
resolvers.Select(r => r.GetType())
.Should()
.ContainInOrder(
new []{
typeof(MuxerCommandResolver),
typeof(RootedCommandResolver),
typeof(AppBaseDllCommandResolver),
typeof(AppBaseCommandResolver),
typeof(PathCommandResolver),
typeof(PublishedPathCommandResolver),
typeof(ProjectToolsCommandResolver)
});
}
}
}

View file

@ -26,6 +26,7 @@ namespace Microsoft.DotNet.Tests
.HaveFile("project.lock.json");
new DotnetCommand()
.WithWorkingDirectory(testInstance.TestRoot)
.ExecuteWithCapturedOutput("crash")
.Should()
.Fail()

View file

@ -178,7 +178,7 @@ namespace Microsoft.DotNet.Tests
[Fact]
public void ToolsCanAccessDependencyContextProperly()
{
var testInstance = TestAssetsManager.CreateTestInstance("DependencyContextFromTool");
var testInstance = TestAssetsManager.CreateTestInstance("DependencyContextFromTool").WithLockFiles();
var appDirectory = testInstance.Path;