diff --git a/TestAssets/TestPackages/dotnet-dependency-tool-invoker/Program.cs b/TestAssets/TestPackages/dotnet-dependency-tool-invoker/Program.cs index 53e103eaa..9db2e07b3 100644 --- a/TestAssets/TestPackages/dotnet-dependency-tool-invoker/Program.cs +++ b/TestAssets/TestPackages/dotnet-dependency-tool-invoker/Program.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.DotNet.ProjectModel; using Microsoft.DotNet.Cli.Utils; +using NuGet.Frameworks; namespace Microsoft.DotNet.Tools.DependencyInvoker { @@ -36,32 +37,73 @@ namespace Microsoft.DotNet.Tools.DependencyInvoker dotnetParams.Config, dotnetParams.Output, dotnetParams.BuildBasePath, - projectContexts.First().ProjectDirectory); - + dotnetParams.ProjectPath); + + var result = 0; + if(projectContexts.Any()) + { + result = InvokeDependencyToolForProjectJson(projectContexts, commandFactory, dotnetParams); + } + else + { + result = InvokeDependencyToolForMSBuild(commandFactory, dotnetParams); + } + + return result; + } + + private static int InvokeDependencyToolForMSBuild( + ProjectDependenciesCommandFactory commandFactory, + DotnetBaseParams dotnetParams) + { + Console.WriteLine($"Invoking '{dotnetParams.Command}' for '{dotnetParams.Framework.GetShortFolderName()}'."); + + return InvokeDependencyTool(commandFactory, dotnetParams, dotnetParams.Framework); + } + + private static int InvokeDependencyToolForProjectJson( + IEnumerable projectContexts, + ProjectDependenciesCommandFactory commandFactory, + DotnetBaseParams dotnetParams) + { foreach (var projectContext in projectContexts) { Console.WriteLine($"Invoking '{dotnetParams.Command}' for '{projectContext.TargetFramework}'."); - try + if (InvokeDependencyTool(commandFactory, dotnetParams, projectContext.TargetFramework) != 0) { - var exitCode = commandFactory.Create( - $"dotnet-{dotnetParams.Command}", - dotnetParams.RemainingArguments, - projectContext.TargetFramework, - dotnetParams.Config) - .ForwardStdErr() - .ForwardStdOut() - .Execute() - .ExitCode; - - Console.WriteLine($"Command returned {exitCode}"); - } - catch (CommandUnknownException) - { - Console.WriteLine($"Command not found"); return 1; } } + + return 0; + } + + private static int InvokeDependencyTool( + ProjectDependenciesCommandFactory commandFactory, + DotnetBaseParams dotnetParams, + NuGetFramework framework) + { + try + { + var exitCode = commandFactory.Create( + $"dotnet-{dotnetParams.Command}", + dotnetParams.RemainingArguments, + framework, + dotnetParams.Config) + .ForwardStdErr() + .ForwardStdOut() + .Execute() + .ExitCode; + + Console.WriteLine($"Command returned {exitCode}"); + } + catch (CommandUnknownException) + { + Console.WriteLine($"Command not found"); + return 1; + } + return 0; } @@ -76,7 +118,7 @@ namespace Microsoft.DotNet.Tools.DependencyInvoker if (!File.Exists(projectPath)) { - throw new InvalidOperationException($"{projectPath} does not exist."); + return Enumerable.Empty(); } return ProjectContext.CreateContextForEachFramework(projectPath); diff --git a/TestAssets/TestPackages/dotnet-dependency-tool-invoker/project.json b/TestAssets/TestPackages/dotnet-dependency-tool-invoker/project.json index 8bf2cd784..5540f2d1d 100644 --- a/TestAssets/TestPackages/dotnet-dependency-tool-invoker/project.json +++ b/TestAssets/TestPackages/dotnet-dependency-tool-invoker/project.json @@ -22,7 +22,8 @@ }, "Microsoft.DotNet.Compiler.Common": { "target": "project" - } + }, + "NuGet.Frameworks": "3.6.0-rc-1984" }, "frameworks": { "netcoreapp1.0": { diff --git a/TestAssets/TestProjects/MSBuildTestAppWithToolInDependencies/MSBuildTestAppWithToolInDependencies.csproj b/TestAssets/TestProjects/MSBuildTestAppWithToolInDependencies/MSBuildTestAppWithToolInDependencies.csproj new file mode 100644 index 000000000..7d998ff6b --- /dev/null +++ b/TestAssets/TestProjects/MSBuildTestAppWithToolInDependencies/MSBuildTestAppWithToolInDependencies.csproj @@ -0,0 +1,36 @@ + + + + + + Exe + netcoreapp1.0 + bin\$(Configuration) + + + + + + + + + 1.0.1 + + + 1.0.0-alpha-20161010-1 + All + + + 1.0.0 + All + + + + + + 1.0.0-* + + + + + \ No newline at end of file diff --git a/TestAssets/TestProjects/MSBuildTestAppWithToolInDependencies/Program.cs b/TestAssets/TestProjects/MSBuildTestAppWithToolInDependencies/Program.cs new file mode 100644 index 000000000..8dae6dd9e --- /dev/null +++ b/TestAssets/TestProjects/MSBuildTestAppWithToolInDependencies/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace MSBuildTestApp +{ + public class Program + { + public static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + } + } +} diff --git a/build/test/TestPackageProjects.targets b/build/test/TestPackageProjects.targets index 4b1e377c5..a22571df6 100644 --- a/build/test/TestPackageProjects.targets +++ b/build/test/TestPackageProjects.targets @@ -159,7 +159,7 @@ 1.0.0-preview3- $(TestPackageBuildVersionSuffix) False - netstandard1.3 + netstandard1.5 Microsoft.DotNet.ProjectModel @@ -186,7 +186,7 @@ 1.0.0-preview3- $(TestPackageBuildVersionSuffix) False - netstandard1.3 + netstandard1.5 Microsoft.DotNet.InternalAbstractions @@ -213,7 +213,7 @@ 1.0.0-preview3- $(TestPackageBuildVersionSuffix) False - netstandard1.3 + netstandard1.5 Microsoft.DotNet.Files @@ -222,7 +222,7 @@ 1.0.0-preview3- $(TestPackageBuildVersionSuffix) False - netstandard1.3 + netstandard1.5 diff --git a/build_projects/dotnet-cli-build/dotnet-cli-build.csproj b/build_projects/dotnet-cli-build/dotnet-cli-build.csproj index fd7a3dce1..c246e4c04 100644 --- a/build_projects/dotnet-cli-build/dotnet-cli-build.csproj +++ b/build_projects/dotnet-cli-build/dotnet-cli-build.csproj @@ -39,7 +39,7 @@ 6.2.2-preview - 3.6.0-rc-1979 + 3.6.0-rc-1984 0.1.0-preview-00043-160929 diff --git a/src/Microsoft.DotNet.Cli.Utils/Command.cs b/src/Microsoft.DotNet.Cli.Utils/Command.cs index 1024efdb6..67cec543e 100644 --- a/src/Microsoft.DotNet.Cli.Utils/Command.cs +++ b/src/Microsoft.DotNet.Cli.Utils/Command.cs @@ -29,6 +29,14 @@ namespace Microsoft.DotNet.Cli.Utils UseShellExecute = false }; + foreach(var environmentVariable in commandSpec.EnvironmentVariables) + { + if (!psi.Environment.ContainsKey(environmentVariable.Key)) + { + psi.Environment.Add(environmentVariable.Key, environmentVariable.Value); + } + } + _process = new Process { StartInfo = psi diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandResolution/DefaultCommandResolverPolicy.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/DefaultCommandResolverPolicy.cs index 689835f7c..ddc3b0a66 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandResolution/DefaultCommandResolverPolicy.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/DefaultCommandResolverPolicy.cs @@ -42,13 +42,15 @@ namespace Microsoft.DotNet.Cli.Utils compositeCommandResolver.AddCommandResolver(new MuxerCommandResolver()); compositeCommandResolver.AddCommandResolver(new RootedCommandResolver()); + compositeCommandResolver.AddCommandResolver( + new ProjectToolsCommandResolver(packagedCommandSpecFactory, environment)); compositeCommandResolver.AddCommandResolver(new AppBaseDllCommandResolver()); compositeCommandResolver.AddCommandResolver( new AppBaseCommandResolver(environment, platformCommandSpecFactory)); compositeCommandResolver.AddCommandResolver( new PathCommandResolver(environment, platformCommandSpecFactory)); compositeCommandResolver.AddCommandResolver( - new PublishedPathCommandResolver(environment, publishedPathCommandSpecFactory)); + new PublishedPathCommandResolver(environment, publishedPathCommandSpecFactory)); return compositeCommandResolver; } diff --git a/src/dotnet/CommandResolution/DepsJsonBuilder.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/DepsJsonBuilder.cs similarity index 99% rename from src/dotnet/CommandResolution/DepsJsonBuilder.cs rename to src/Microsoft.DotNet.Cli.Utils/CommandResolution/DepsJsonBuilder.cs index 89cfea873..4de9841d5 100644 --- a/src/dotnet/CommandResolution/DepsJsonBuilder.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/DepsJsonBuilder.cs @@ -12,7 +12,7 @@ using NuGet.Packaging; using NuGet.Packaging.Core; using NuGet.ProjectModel; -namespace Microsoft.DotNet.Cli.CommandResolution +namespace Microsoft.DotNet.Cli.Utils { internal class DepsJsonBuilder { diff --git a/src/dotnet/CommandResolution/IProject.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/IProject.cs similarity index 66% rename from src/dotnet/CommandResolution/IProject.cs rename to src/Microsoft.DotNet.Cli.Utils/CommandResolution/IProject.cs index 3af564385..640681c18 100644 --- a/src/dotnet/CommandResolution/IProject.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/IProject.cs @@ -4,12 +4,18 @@ using System.Collections.Generic; using NuGet.ProjectModel; -namespace Microsoft.DotNet.Cli.CommandResolution +namespace Microsoft.DotNet.Cli.Utils { internal interface IProject { LockFile GetLockFile(); IEnumerable GetTools(); + + string DepsJsonPath { get; } + + string RuntimeConfigJsonPath { get; } + + Dictionary EnvironmentVariables { get; } } } \ No newline at end of file diff --git a/src/dotnet/CommandResolution/LockFileTargetExtensions.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/LockFileTargetExtensions.cs similarity index 98% rename from src/dotnet/CommandResolution/LockFileTargetExtensions.cs rename to src/Microsoft.DotNet.Cli.Utils/CommandResolution/LockFileTargetExtensions.cs index d627021a7..9bb86d314 100644 --- a/src/dotnet/CommandResolution/LockFileTargetExtensions.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/LockFileTargetExtensions.cs @@ -7,7 +7,7 @@ using System.Linq; using NuGet.Packaging.Core; using NuGet.ProjectModel; -namespace Microsoft.DotNet.Cli.CommandResolution +namespace Microsoft.DotNet.Cli.Utils { internal static class LockFileTargetExtensions { diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandResolution/MSBuildProject.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/MSBuildProject.cs new file mode 100644 index 000000000..e658ff9f9 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/MSBuildProject.cs @@ -0,0 +1,125 @@ +// 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.Frameworks; +using NuGet.ProjectModel; + +namespace Microsoft.DotNet.Cli.Utils +{ + internal class MSBuildProject : IProject + { + private Project _project; + + private string _msBuildExePath; + + public string DepsJsonPath + { + get + { + return _project + .AllEvaluatedProperties + .FirstOrDefault(p => p.Name.Equals("_ProjectDepsFilePath")) + .EvaluatedValue; + } + } + + public string RuntimeConfigJsonPath + { + get + { + return _project + .AllEvaluatedProperties + .FirstOrDefault(p => p.Name.Equals("_ProjectRuntimeConfigFilePath")) + .EvaluatedValue; + } + } + + public Dictionary EnvironmentVariables + { + get + { + return new Dictionary + { + { Constants.MSBUILD_EXE_PATH, _msBuildExePath } + }; + } + } + + public MSBuildProject( + string msBuildProjectPath, + NuGetFramework framework, + string configuration, + string outputPath, + string msBuildExePath) + { + var globalProperties = new Dictionary() + { + { "MSBuildExtensionsPath", Path.GetDirectoryName(msBuildExePath) } + }; + + if(framework != null) + { + globalProperties.Add("TargetFramework", framework.GetShortFolderName()); + } + + if(outputPath != null) + { + globalProperties.Add("OutputPath", outputPath); + } + + if(configuration != null) + { + globalProperties.Add("Configuration", configuration); + } + + _project = ProjectCollection.GlobalProjectCollection.LoadProject( + msBuildProjectPath, + globalProperties, + null); + + _msBuildExePath = msBuildExePath; + } + + public IEnumerable GetTools() + { + var toolsReferences = _project.AllEvaluatedItems.Where(i => i.ItemType.Equals("DotNetCliToolReference")); + var tools = toolsReferences.Select(t => new SingleProjectInfo( + t.EvaluatedInclude, + t.GetMetadataValue("Version"), + Enumerable.Empty())); + + return tools; + } + + public LockFile GetLockFile() + { + var lockFilePath = GetLockFilePathFromProjectLockFileProperty() ?? + GetLockFilePathFromIntermediateBaseOutputPath(); + + return new LockFileFormat().Read(lockFilePath); + } + + private string GetLockFilePathFromProjectLockFileProperty() + { + return _project + .AllEvaluatedProperties + .Where(p => p.Name.Equals("ProjectLockFile")) + .Select(p => p.EvaluatedValue) + .FirstOrDefault(p => File.Exists(p)); + } + + private string GetLockFilePathFromIntermediateBaseOutputPath() + { + var intermediateOutputPath = _project + .AllEvaluatedProperties + .FirstOrDefault(p => p.Name.Equals("BaseIntermediateOutputPath")) + .EvaluatedValue; + return Path.Combine(intermediateOutputPath, "project.assets.json"); + } + } +} \ No newline at end of file diff --git a/src/dotnet/CommandResolution/NuGetUtils.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/NuGetUtils.cs similarity index 93% rename from src/dotnet/CommandResolution/NuGetUtils.cs rename to src/Microsoft.DotNet.Cli.Utils/CommandResolution/NuGetUtils.cs index 6e5821451..2c569a8ab 100644 --- a/src/dotnet/CommandResolution/NuGetUtils.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/NuGetUtils.cs @@ -8,7 +8,7 @@ using System.Linq; using NuGet.Packaging.Core; using NuGet.ProjectModel; -namespace Microsoft.DotNet.Cli.CommandResolution +namespace Microsoft.DotNet.Cli.Utils { internal static class NuGetUtils { diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectDependenciesCommandResolver.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectDependenciesCommandResolver.cs index 9fa5f7cd7..29d40a042 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectDependenciesCommandResolver.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectDependenciesCommandResolver.cs @@ -1,7 +1,8 @@ -using System; +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; @@ -67,37 +68,42 @@ namespace Microsoft.DotNet.Cli.Utils { var allowedExtensions = GetAllowedCommandExtensionsFromEnvironment(_environment); - var projectContext = GetProjectContextFromDirectory( + var projectFactory = new ProjectFactory(_environment); + var project = projectFactory.GetProject( projectDirectory, - framework); + framework, + configuration, + buildBasePath, + outputPath); - if (projectContext == null) + if (project == null) { + Reporter.Verbose.WriteLine($"projectdependenciescommandresolver: Didn't find a matching project {projectDirectory}."); return null; } - var depsFilePath = - projectContext.GetOutputPaths(configuration, buildBasePath, outputPath).RuntimeFiles.DepsJson; + var depsFilePath = project.DepsJsonPath; - if (! File.Exists(depsFilePath)) + if (!File.Exists(depsFilePath)) { Reporter.Verbose.WriteLine($"projectdependenciescommandresolver: {depsFilePath} does not exist"); return null; } - var runtimeConfigPath = - projectContext.GetOutputPaths(configuration, buildBasePath, outputPath).RuntimeFiles.RuntimeConfigJson; + var runtimeConfigPath = project.RuntimeConfigJsonPath; - if (! File.Exists(runtimeConfigPath)) + if (!File.Exists(runtimeConfigPath)) { Reporter.Verbose.WriteLine($"projectdependenciescommandresolver: {runtimeConfigPath} does not exist"); return null; } - var toolLibrary = GetToolLibraryForContext(projectContext, commandName); - var normalizedNugetPackagesRoot = PathUtility.EnsureNoTrailingDirectorySeparator(projectContext.PackagesDirectory); + var lockFile = project.GetLockFile(); + var toolLibrary = GetToolLibraryForContext(lockFile, commandName, framework); + var normalizedNugetPackagesRoot = + PathUtility.EnsureNoTrailingDirectorySeparator(lockFile.PackageFolders.First().Path); - return _packagedCommandSpecFactory.CreateCommandSpecFromLibrary( + var commandSpec = _packagedCommandSpecFactory.CreateCommandSpecFromLibrary( toolLibrary, commandName, commandArguments, @@ -106,14 +112,18 @@ namespace Microsoft.DotNet.Cli.Utils s_commandResolutionStrategy, depsFilePath, runtimeConfigPath); + + commandSpec?.AddEnvironmentVariablesFromProject(project); + + return commandSpec; } private LockFileTargetLibrary GetToolLibraryForContext( - ProjectContext projectContext, string commandName) + LockFile lockFile, string commandName, NuGetFramework targetFramework) { - var toolLibraries = projectContext.LockFile.Targets + var toolLibraries = lockFile.Targets .FirstOrDefault(t => t.TargetFramework.GetShortFolderName() - .Equals(projectContext.TargetFramework.GetShortFolderName())) + .Equals(targetFramework.GetShortFolderName())) ?.Libraries.Where(l => l.Name == commandName || l.RuntimeAssemblies.Any(r => Path.GetFileNameWithoutExtension(r.Path) == commandName)).ToList(); @@ -122,29 +132,11 @@ namespace Microsoft.DotNet.Cli.Utils throw new InvalidOperationException($"Ambiguous command name: {commandName}"); } + Reporter.Verbose.WriteLine($"projectdependenciescommandresolver: tool library found {toolLibraries?.Count() > 0}"); + return toolLibraries?.FirstOrDefault(); } - private ProjectContext GetProjectContextFromDirectory(string directory, NuGetFramework framework) - { - if (directory == null || framework == null) - { - return null; - } - - var projectRootPath = directory; - - if (!File.Exists(Path.Combine(projectRootPath, Project.FileName))) - { - return null; - } - - return ProjectContext.Create( - projectRootPath, - framework, - RuntimeEnvironmentRidExtensions.GetAllCandidateRuntimeIdentifiers()); - } - private IEnumerable GetAllowedCommandExtensionsFromEnvironment(IEnvironmentProvider environment) { var allowedCommandExtensions = new List(); diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectFactory.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectFactory.cs new file mode 100644 index 000000000..951d21913 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectFactory.cs @@ -0,0 +1,95 @@ +// 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.Exceptions; +using Microsoft.DotNet.ProjectModel; +using NuGet.Frameworks; + +namespace Microsoft.DotNet.Cli.Utils +{ + internal class ProjectFactory + { + private IEnvironmentProvider _environment; + + public ProjectFactory(IEnvironmentProvider environment) + { + _environment = environment; + } + + public IProject GetProject( + string projectDirectory, + NuGetFramework framework, + string configuration, + string buildBasePath, + string outputPath) + { + return GetMSBuildProj(projectDirectory, framework, configuration, outputPath) ?? + GetProjectJsonProject(projectDirectory, framework, configuration, buildBasePath, outputPath); + } + + private IProject GetMSBuildProj(string projectDirectory, NuGetFramework framework, string configuration, string outputPath) + { + var msBuildExePath = _environment.GetEnvironmentVariable(Constants.MSBUILD_EXE_PATH); + msBuildExePath = string.IsNullOrEmpty(msBuildExePath) ? + Path.Combine(AppContext.BaseDirectory, "MSBuild.exe") : + msBuildExePath; + + Reporter.Verbose.WriteLine($"projetfactory: MSBUILD_EXE_PATH = {msBuildExePath}"); + + string msBuildProjectPath = GetMSBuildProjPath(projectDirectory); + Reporter.Verbose.WriteLine($"projetfactory: MSBuild project path = {msBuildProjectPath}"); + if(msBuildProjectPath == null) + { + return null; + } + + try + { + return new MSBuildProject(msBuildProjectPath, framework, configuration, outputPath, msBuildExePath); + } + catch (InvalidProjectFileException ex) + { + Reporter.Verbose.WriteLine(ex.ToString().Red()); + return null; + } + } + + private IProject GetProjectJsonProject( + string projectDirectory, + NuGetFramework framework, + string configuration, + string buildBasePath, + string outputPath) + { + if (!File.Exists(Path.Combine(projectDirectory, Project.FileName))) + { + return null; + } + + return new ProjectJsonProject(projectDirectory, framework, configuration, buildBasePath, outputPath); + } + + private string GetMSBuildProjPath(string projectDirectory) + { + IEnumerable projectFiles = Directory + .GetFiles(projectDirectory, "*.*proj") + .Where(d => !d.EndsWith(".xproj")); + + if (projectFiles.Count() == 0) + { + return null; + } + else if (projectFiles.Count() > 1) + { + throw new InvalidOperationException( + $"Specify which project file to use because this '{projectDirectory}' contains more than one project file."); + } + + return projectFiles.First(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectJsonProject.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectJsonProject.cs new file mode 100644 index 000000000..e6170da68 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectJsonProject.cs @@ -0,0 +1,110 @@ +// 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 NuGet.Frameworks; +using NuGet.ProjectModel; + +namespace Microsoft.DotNet.Cli.Utils +{ + internal class ProjectJsonProject : IProject + { + private LockFile _lockFile; + + private ProjectContext _projectContext; + + private string _projectDirectory; + + private string _configuration; + + private string _outputPath; + + private string _buildBasePath; + + private NuGetFramework _framework; + + private ProjectContext ProjectContext + { + get + { + if(_projectContext == null) + { + _projectContext = ProjectContext.Create( + _projectDirectory, + _framework, + RuntimeEnvironmentRidExtensions.GetAllCandidateRuntimeIdentifiers()); + } + + return _projectContext; + } + } + + public string DepsJsonPath + { + get + { + return ProjectContext.GetOutputPaths( + _configuration, + _buildBasePath, + _outputPath).RuntimeFiles.DepsJson; + } + } + + public string RuntimeConfigJsonPath + { + get + { + return ProjectContext.GetOutputPaths( + _configuration, + _buildBasePath, + _outputPath).RuntimeFiles.RuntimeConfigJson; + } + } + + public Dictionary EnvironmentVariables + { + get + { + return new Dictionary(); + } + } + + public ProjectJsonProject( + string projectDirectory, + NuGetFramework framework, + string configuration, + string buildBasePath, + string outputPath) + { + var lockFilePath = Path.Combine(projectDirectory, LockFileFormat.LockFileName); + _lockFile = new LockFileFormat().Read(lockFilePath); + + _projectDirectory = projectDirectory; + _framework = framework; + _configuration = configuration; + _buildBasePath = buildBasePath; + _outputPath = outputPath; + } + + public LockFile GetLockFile() + { + return _lockFile; + } + + public IEnumerable 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())); + } + } +} \ No newline at end of file diff --git a/src/dotnet/CommandResolution/ProjectToolsCommandResolver.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectToolsCommandResolver.cs similarity index 82% rename from src/dotnet/CommandResolution/ProjectToolsCommandResolver.cs rename to src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectToolsCommandResolver.cs index 3517a11e7..fce36d55e 100644 --- a/src/dotnet/CommandResolution/ProjectToolsCommandResolver.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ProjectToolsCommandResolver.cs @@ -5,8 +5,6 @@ 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; @@ -15,7 +13,7 @@ using NuGet.ProjectModel; using NuGet.Versioning; using FileFormatException = Microsoft.DotNet.ProjectModel.FileFormatException; -namespace Microsoft.DotNet.Cli.CommandResolution +namespace Microsoft.DotNet.Cli.Utils { public class ProjectToolsCommandResolver : ICommandResolver { @@ -27,9 +25,14 @@ namespace Microsoft.DotNet.Cli.CommandResolution private List _allowedCommandExtensions; private IPackagedCommandSpecFactory _packagedCommandSpecFactory; - public ProjectToolsCommandResolver(IPackagedCommandSpecFactory packagedCommandSpecFactory) + private IEnvironmentProvider _environment; + + public ProjectToolsCommandResolver( + IPackagedCommandSpecFactory packagedCommandSpecFactory, + IEnvironmentProvider environment) { _packagedCommandSpecFactory = packagedCommandSpecFactory; + _environment = environment; _allowedCommandExtensions = new List() { @@ -45,37 +48,43 @@ namespace Microsoft.DotNet.Cli.CommandResolution return null; } - return ResolveFromProjectTools( - commandResolverArguments.CommandName, - commandResolverArguments.CommandArguments.OrEmptyIfNull(), - commandResolverArguments.ProjectDirectory); + return ResolveFromProjectTools(commandResolverArguments); } - private CommandSpec ResolveFromProjectTools( - string commandName, - IEnumerable args, - string projectDirectory) + private CommandSpec ResolveFromProjectTools(CommandResolverArguments commandResolverArguments) { - var projectFactory = new ProjectFactory(); - var project = projectFactory.GetProject(projectDirectory); + var projectFactory = new ProjectFactory(_environment); + var project = projectFactory.GetProject( + commandResolverArguments.ProjectDirectory, + commandResolverArguments.Framework, + commandResolverArguments.Configuration, + commandResolverArguments.BuildBasePath, + commandResolverArguments.OutputPath); var tools = project.GetTools(); return ResolveCommandSpecFromAllToolLibraries( tools, - commandName, - args, - project.GetLockFile()); + commandResolverArguments.CommandName, + commandResolverArguments.CommandArguments.OrEmptyIfNull(), + project.GetLockFile(), + project); } private CommandSpec ResolveCommandSpecFromAllToolLibraries( IEnumerable toolsLibraries, string commandName, IEnumerable args, - LockFile lockFile) + LockFile lockFile, + IProject project) { foreach (var toolLibrary in toolsLibraries) { - var commandSpec = ResolveCommandSpecFromToolLibrary(toolLibrary, commandName, args, lockFile); + var commandSpec = ResolveCommandSpecFromToolLibrary( + toolLibrary, + commandName, + args, + lockFile, + project); if (commandSpec != null) { @@ -90,7 +99,8 @@ namespace Microsoft.DotNet.Cli.CommandResolution SingleProjectInfo toolLibraryRange, string commandName, IEnumerable args, - LockFile lockFile) + LockFile lockFile, + IProject project) { var nugetPackagesRoot = lockFile.PackageFolders.First().Path; @@ -111,7 +121,7 @@ namespace Microsoft.DotNet.Cli.CommandResolution var normalizedNugetPackagesRoot = PathUtility.EnsureNoTrailingDirectorySeparator(nugetPackagesRoot); - return _packagedCommandSpecFactory.CreateCommandSpecFromLibrary( + var commandSpec = _packagedCommandSpecFactory.CreateCommandSpecFromLibrary( toolLibrary, commandName, args, @@ -120,6 +130,10 @@ namespace Microsoft.DotNet.Cli.CommandResolution s_commandResolutionStrategy, depsFilePath, null); + + commandSpec?.AddEnvironmentVariablesFromProject(project); + + return commandSpec; } private LockFile GetToolLockFile( @@ -155,7 +169,7 @@ namespace Microsoft.DotNet.Cli.CommandResolution return toolPathCalculator.GetBestLockFilePath( toolLibrary.Name, - new VersionRange(new NuGetVersion(toolLibrary.Version)), + VersionRange.Parse(toolLibrary.Version), s_toolPackageFramework); } @@ -184,7 +198,6 @@ namespace Microsoft.DotNet.Cli.CommandResolution } } - // Need to unit test this, so public internal void GenerateDepsJsonFile( LockFile toolLockFile, string depsPath, diff --git a/src/dotnet/CommandResolution/ResourceAssemblyInfo.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ResourceAssemblyInfo.cs similarity index 90% rename from src/dotnet/CommandResolution/ResourceAssemblyInfo.cs rename to src/Microsoft.DotNet.Cli.Utils/CommandResolution/ResourceAssemblyInfo.cs index c69b0a037..80346aae2 100644 --- a/src/dotnet/CommandResolution/ResourceAssemblyInfo.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/ResourceAssemblyInfo.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -namespace Microsoft.DotNet.Cli.CommandResolution +namespace Microsoft.DotNet.Cli.Utils { internal class ResourceAssemblyInfo { diff --git a/src/dotnet/CommandResolution/SingleProjectInfo.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/SingleProjectInfo.cs similarity index 93% rename from src/dotnet/CommandResolution/SingleProjectInfo.cs rename to src/Microsoft.DotNet.Cli.Utils/CommandResolution/SingleProjectInfo.cs index 917680abb..fa15442d6 100644 --- a/src/dotnet/CommandResolution/SingleProjectInfo.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolution/SingleProjectInfo.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -namespace Microsoft.DotNet.Cli.CommandResolution +namespace Microsoft.DotNet.Cli.Utils { internal class SingleProjectInfo { diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandSpec.cs b/src/Microsoft.DotNet.Cli.Utils/CommandSpec.cs index 5b941515c..bf7d7fb3b 100644 --- a/src/Microsoft.DotNet.Cli.Utils/CommandSpec.cs +++ b/src/Microsoft.DotNet.Cli.Utils/CommandSpec.cs @@ -1,18 +1,35 @@ +using System.Collections.Generic; + namespace Microsoft.DotNet.Cli.Utils { public class CommandSpec { - public CommandSpec(string path, string args, CommandResolutionStrategy resolutionStrategy) + public CommandSpec( + string path, + string args, + CommandResolutionStrategy resolutionStrategy, + Dictionary environmentVariables = null) { Path = path; Args = args; ResolutionStrategy = resolutionStrategy; + EnvironmentVariables = environmentVariables ?? new Dictionary(); } public string Path { get; } public string Args { get; } + public Dictionary EnvironmentVariables { get; } + public CommandResolutionStrategy ResolutionStrategy { get; } + + internal void AddEnvironmentVariablesFromProject(IProject project) + { + foreach (var environmentVariable in project.EnvironmentVariables) + { + EnvironmentVariables.Add(environmentVariable.Key, environmentVariable.Value); + } + } } } \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/Constants.cs b/src/Microsoft.DotNet.Cli.Utils/Constants.cs index e3f6c195a..444693f8f 100644 --- a/src/Microsoft.DotNet.Cli.Utils/Constants.cs +++ b/src/Microsoft.DotNet.Cli.Utils/Constants.cs @@ -47,5 +47,6 @@ namespace Microsoft.DotNet.Cli.Utils "libcoreclr.dylib" }; + public static readonly string MSBUILD_EXE_PATH = "MSBUILD_EXE_PATH"; } } diff --git a/src/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs b/src/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs index e3f0ba663..2280f8e8b 100644 --- a/src/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs +++ b/src/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs @@ -93,6 +93,11 @@ namespace Microsoft.DotNet.Cli.Utils return GetCommandPathFromRootPath(rootPath, commandName, extensionsArr); } + public string GetEnvironmentVariable(string name) + { + return Environment.GetEnvironmentVariable(name); + } + public bool GetEnvironmentVariableAsBool(string name, bool defaultValue) { var str = Environment.GetEnvironmentVariable(name); diff --git a/src/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs b/src/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs index e9cc613c7..25124153e 100644 --- a/src/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs +++ b/src/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs @@ -13,5 +13,7 @@ namespace Microsoft.DotNet.Cli.Utils string GetCommandPathFromRootPath(string rootPath, string commandName, IEnumerable extensions); bool GetEnvironmentVariableAsBool(string name, bool defaultValue); + + string GetEnvironmentVariable(string name); } } diff --git a/src/Microsoft.DotNet.Cli.Utils/project.json b/src/Microsoft.DotNet.Cli.Utils/project.json index 8f8450bab..91bdf7f93 100644 --- a/src/Microsoft.DotNet.Cli.Utils/project.json +++ b/src/Microsoft.DotNet.Cli.Utils/project.json @@ -9,19 +9,25 @@ "target": "project" }, "Microsoft.DotNet.PlatformAbstractions": "1.0.1-beta-000933", + "NuGet.Versioning": "3.6.0-rc-1984", "NuGet.Packaging": "3.6.0-rc-1984", "NuGet.Frameworks": "3.6.0-rc-1984", - "NuGet.ProjectModel": "3.6.0-rc-1984" + "NuGet.ProjectModel": "3.6.0-rc-1984", + + "Microsoft.Build": "15.1.316-preview5", + "Microsoft.Build.Utilities.Core": "15.1.316-preview5" }, "frameworks": { - "netstandard1.3": { + "netstandard1.5": { "imports": [ "portable-net45+wp80+win8+wpa81+dnxcore50" ], "dependencies": { "System.Diagnostics.Process": "4.1.0" } + }, + "net46": { } } } diff --git a/src/Microsoft.DotNet.Compiler.Common/project.json b/src/Microsoft.DotNet.Compiler.Common/project.json index 168364905..c740966e1 100644 --- a/src/Microsoft.DotNet.Compiler.Common/project.json +++ b/src/Microsoft.DotNet.Compiler.Common/project.json @@ -17,7 +17,7 @@ "NuGet.ProjectModel": "3.6.0-rc-1984" }, "frameworks": { - "netstandard1.3": { + "netstandard1.5": { "imports": [ "portable-net45+wp80+win8+wpa81+dnxcore50" ] diff --git a/src/Microsoft.DotNet.Configurer/project.json b/src/Microsoft.DotNet.Configurer/project.json index cc3e093c3..2479637f5 100644 --- a/src/Microsoft.DotNet.Configurer/project.json +++ b/src/Microsoft.DotNet.Configurer/project.json @@ -19,7 +19,7 @@ } }, "frameworks": { - "netstandard1.6": { + "netstandard1.5": { "imports": [ "portable-net45+wp80+win8+wpa81+dnxcore50" ] diff --git a/src/Microsoft.DotNet.Files/project.json b/src/Microsoft.DotNet.Files/project.json index 11b38863b..a78ab145f 100644 --- a/src/Microsoft.DotNet.Files/project.json +++ b/src/Microsoft.DotNet.Files/project.json @@ -14,7 +14,7 @@ "System.Linq.Expressions": "4.1.0" }, "frameworks": { - "netstandard1.3": { + "netstandard1.5": { "imports": [ "portable-net45+wp80+win8+wpa81+dnxcore50" ] diff --git a/src/Microsoft.DotNet.ProjectJsonMigration/project.json b/src/Microsoft.DotNet.ProjectJsonMigration/project.json index 4132eb784..596c51c48 100644 --- a/src/Microsoft.DotNet.ProjectJsonMigration/project.json +++ b/src/Microsoft.DotNet.ProjectJsonMigration/project.json @@ -14,7 +14,7 @@ "Microsoft.DotNet.ProjectModel": { "target": "project" }, - "Microsoft.Build": "0.1.0-preview-00034-160909" + "Microsoft.Build": "15.1.316-preview5" }, "frameworks": { "netcoreapp1.0": { diff --git a/src/Microsoft.DotNet.ProjectModel.Workspaces/project.json b/src/Microsoft.DotNet.ProjectModel.Workspaces/project.json index f82f0fa23..ef434e125 100644 --- a/src/Microsoft.DotNet.ProjectModel.Workspaces/project.json +++ b/src/Microsoft.DotNet.ProjectModel.Workspaces/project.json @@ -14,7 +14,7 @@ "Microsoft.DotNet.PlatformAbstractions": "1.0.1-beta-000933" }, "frameworks": { - "netstandard1.3": { + "netstandard1.5": { "imports": [ "portable-net45+wp80+win8+wpa81+dnxcore50", "portable-net45+win8" diff --git a/src/Microsoft.DotNet.ProjectModel/Utilities/VersionUtility.cs b/src/Microsoft.DotNet.ProjectModel/Utilities/VersionUtility.cs index e64709546..69072d3b3 100644 --- a/src/Microsoft.DotNet.ProjectModel/Utilities/VersionUtility.cs +++ b/src/Microsoft.DotNet.ProjectModel/Utilities/VersionUtility.cs @@ -120,4 +120,4 @@ namespace Microsoft.DotNet.ProjectModel.Utilities return sb.ToString(); } } -} +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.TestFramework/project.json b/src/Microsoft.DotNet.TestFramework/project.json index be391ed6e..d2fc22f88 100644 --- a/src/Microsoft.DotNet.TestFramework/project.json +++ b/src/Microsoft.DotNet.TestFramework/project.json @@ -10,10 +10,12 @@ } }, "frameworks": { - "netstandard1.3": { + "netstandard1.5": { "imports": [ "portable-net45+wp80+win8+wpa81+dnxcore50" ] + }, + "net46": { } }, "packOptions": { diff --git a/src/dotnet/CommandResolution/MSBuildProject.cs b/src/dotnet/CommandResolution/MSBuildProject.cs deleted file mode 100644 index 459dc4081..000000000 --- a/src/dotnet/CommandResolution/MSBuildProject.cs +++ /dev/null @@ -1,48 +0,0 @@ -// 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() - { - { "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 GetTools() - { - var toolsReferences = _project.AllEvaluatedItems.Where(i => i.ItemType.Equals("DotNetCliToolReference")); - var tools = toolsReferences.Select(t => new SingleProjectInfo( - t.EvaluatedInclude, - t.GetMetadataValue("Version"), - Enumerable.Empty())); - - return tools; - } - } -} \ No newline at end of file diff --git a/src/dotnet/CommandResolution/ProjectFactory.cs b/src/dotnet/CommandResolution/ProjectFactory.cs deleted file mode 100644 index 4021d042f..000000000 --- a/src/dotnet/CommandResolution/ProjectFactory.cs +++ /dev/null @@ -1,50 +0,0 @@ -// 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]; - } - } -} \ No newline at end of file diff --git a/src/dotnet/CommandResolution/ProjectJsonProject.cs b/src/dotnet/CommandResolution/ProjectJsonProject.cs deleted file mode 100644 index 5b97413bc..000000000 --- a/src/dotnet/CommandResolution/ProjectJsonProject.cs +++ /dev/null @@ -1,37 +0,0 @@ -// 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 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())); - } - } -} \ No newline at end of file diff --git a/src/dotnet/CommandResolution/ProjectToolsCommandResolverPolicy.cs b/src/dotnet/CommandResolution/ProjectToolsCommandResolverPolicy.cs deleted file mode 100644 index 2afef5ffa..000000000 --- a/src/dotnet/CommandResolution/ProjectToolsCommandResolverPolicy.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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; - } - } -} \ No newline at end of file diff --git a/src/dotnet/Program.cs b/src/dotnet/Program.cs index 27582cea7..9117e2fd0 100644 --- a/src/dotnet/Program.cs +++ b/src/dotnet/Program.cs @@ -6,7 +6,6 @@ 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; @@ -193,9 +192,7 @@ namespace Microsoft.DotNet.Cli } else { - var projectToolsCommandResolver = new ProjectToolsCommandResolverPolicy(); CommandResult result = Command.Create( - projectToolsCommandResolver, "dotnet-" + command, appArgs, FrameworkConstants.CommonFrameworks.NetStandardApp15) diff --git a/test/EndToEnd/GivenDotNetUsesMSBuild.cs b/test/EndToEnd/GivenDotNetUsesMSBuild.cs index 393538592..e1c4a7463 100644 --- a/test/EndToEnd/GivenDotNetUsesMSBuild.cs +++ b/test/EndToEnd/GivenDotNetUsesMSBuild.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; using System.IO; using Microsoft.DotNet.Tools.Test.Utilities; using Xunit; @@ -83,5 +84,39 @@ namespace Microsoft.DotNet.Tests.EndToEnd .And .HaveStdOutContaining("Hello Portable World!");; } + + [Fact] + public void ItCanRunAToolThatInvokesADependencyToolInACSProj() + { + var repoDirectoriesProvider = new RepoDirectoriesProvider(); + var testAppName = "MSBuildTestAppWithToolInDependencies"; + var testInstance = TestAssetsManager + .CreateTestInstance(testAppName); + + var configuration = "Debug"; + + var testProjectDirectory = testInstance.TestRoot; + + new Restore3Command() + .WithWorkingDirectory(testProjectDirectory) + .Execute($"-s {repoDirectoriesProvider.TestPackages}") + .Should() + .Pass(); + + new Build3Command() + .WithWorkingDirectory(testProjectDirectory) + .Execute($"-c {configuration}") + .Should() + .Pass(); + + new DotnetCommand() + .WithWorkingDirectory(testProjectDirectory) + .ExecuteWithCapturedOutput( + $"-v dependency-tool-invoker -c {configuration} -f netcoreapp1.0 portable") + .Should() + .Pass() + .And + .HaveStdOutContaining("Hello Portable World!");; + } } } diff --git a/test/EndToEnd/project.json b/test/EndToEnd/project.json index 6fe000820..d11d3d249 100644 --- a/test/EndToEnd/project.json +++ b/test/EndToEnd/project.json @@ -1,7 +1,15 @@ { "version": "1.0.0-*", "buildOptions": { - "emitEntryPoint": true + "emitEntryPoint": true, + "copyToOutput": { + "include": [ + "../../artifacts/*/stage2/sdk/*/.version" + ], + "mappings": { + ".version": "../../artifacts/*/stage2/sdk/*/.version" + } + } }, "dependencies": { "Microsoft.NETCore.App": { diff --git a/test/Microsoft.DotNet.Cli.Utils.Tests/GivenADefaultCommandResolver.cs b/test/Microsoft.DotNet.Cli.Utils.Tests/GivenADefaultCommandResolver.cs index 2a09e9d47..b2e354e30 100644 --- a/test/Microsoft.DotNet.Cli.Utils.Tests/GivenADefaultCommandResolver.cs +++ b/test/Microsoft.DotNet.Cli.Utils.Tests/GivenADefaultCommandResolver.cs @@ -17,7 +17,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests var resolvers = defaultCommandResolver.OrderedCommandResolvers; - resolvers.Should().HaveCount(6); + resolvers.Should().HaveCount(7); resolvers.Select(r => r.GetType()) .Should() @@ -25,6 +25,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests new []{ typeof(MuxerCommandResolver), typeof(RootedCommandResolver), + typeof(ProjectToolsCommandResolver), typeof(AppBaseDllCommandResolver), typeof(AppBaseCommandResolver), typeof(PathCommandResolver), diff --git a/test/Microsoft.DotNet.Cli.Utils.Tests/GivenAProjectDependencyCommandResolver.cs b/test/Microsoft.DotNet.Cli.Utils.Tests/GivenAProjectDependencyCommandResolver.cs index ebf2362f5..8582826bd 100644 --- a/test/Microsoft.DotNet.Cli.Utils.Tests/GivenAProjectDependencyCommandResolver.cs +++ b/test/Microsoft.DotNet.Cli.Utils.Tests/GivenAProjectDependencyCommandResolver.cs @@ -8,6 +8,7 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.InternalAbstractions; using Microsoft.DotNet.ProjectModel; +using Microsoft.DotNet.TestFramework; using Microsoft.DotNet.Tools.Test.Utilities; using NuGet.Frameworks; using Xunit; @@ -17,7 +18,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests public class GivenAProjectDependenciesCommandResolver : TestBase { - private const string TestProjectName = "AppWithDirectDep"; + private const string ProjectJsonTestProjectName = "AppWithDirectDep"; [Fact] public void It_returns_null_when_CommandName_is_null() @@ -62,7 +63,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests { var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); - var testInstance = TestAssetsManager.CreateTestInstance(TestProjectName) + var testInstance = TestAssetsManager.CreateTestInstance(ProjectJsonTestProjectName) .WithLockFiles(); var commandResolverArguments = new CommandResolverArguments() @@ -84,7 +85,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests { var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); - var testInstance = TestAssetsManager.CreateTestInstance(TestProjectName) + var testInstance = TestAssetsManager.CreateTestInstance(ProjectJsonTestProjectName) .WithLockFiles(); var commandResolverArguments = new CommandResolverArguments() @@ -106,7 +107,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests { var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); - var testInstance = TestAssetsManager.CreateTestInstance(TestProjectName) + var testInstance = TestAssetsManager.CreateTestInstance(ProjectJsonTestProjectName) .WithLockFiles(); var commandResolverArguments = new CommandResolverArguments() @@ -128,7 +129,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests { var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); - var testInstance = TestAssetsManager.CreateTestInstance(TestProjectName) + var testInstance = TestAssetsManager.CreateTestInstance(ProjectJsonTestProjectName) .WithBuildArtifacts() .WithLockFiles(); @@ -157,7 +158,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests { var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); - var testInstance = TestAssetsManager.CreateTestInstance(TestProjectName) + var testInstance = TestAssetsManager.CreateTestInstance(ProjectJsonTestProjectName) .WithBuildArtifacts() .WithLockFiles(); @@ -181,7 +182,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests { var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); - var testInstance = TestAssetsManager.CreateTestInstance(TestProjectName) + var testInstance = TestAssetsManager.CreateTestInstance(ProjectJsonTestProjectName) .WithBuildArtifacts() .WithLockFiles(); @@ -205,7 +206,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests { var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); - var testInstance = TestAssetsManager.CreateTestInstance(TestProjectName) + var testInstance = TestAssetsManager.CreateTestInstance(ProjectJsonTestProjectName) .WithLockFiles(); var outputDir = Path.Combine(testInstance.Path, "outdir"); @@ -245,7 +246,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests { var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); - var testInstance = TestAssetsManager.CreateTestInstance(TestProjectName) + var testInstance = TestAssetsManager.CreateTestInstance(ProjectJsonTestProjectName) .WithLockFiles(); var buildBasePath = Path.Combine(testInstance.Path, "basedir"); @@ -285,7 +286,7 @@ namespace Microsoft.DotNet.Cli.Utils.Tests { var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); - var testInstance = TestAssetsManager.CreateTestInstance(TestProjectName) + var testInstance = TestAssetsManager.CreateTestInstance(ProjectJsonTestProjectName) .WithBuildArtifacts() .WithLockFiles(); diff --git a/test/Microsoft.DotNet.Cli.Utils.Tests/GivenAProjectDependencyCommandResolverBeingUsedWithMSBuild.cs b/test/Microsoft.DotNet.Cli.Utils.Tests/GivenAProjectDependencyCommandResolverBeingUsedWithMSBuild.cs new file mode 100644 index 000000000..8cf2fd547 --- /dev/null +++ b/test/Microsoft.DotNet.Cli.Utils.Tests/GivenAProjectDependencyCommandResolverBeingUsedWithMSBuild.cs @@ -0,0 +1,163 @@ +// 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 FluentAssertions; +using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.InternalAbstractions; +using Microsoft.DotNet.ProjectModel; +using Microsoft.DotNet.TestFramework; +using Microsoft.DotNet.Tools.Test.Utilities; +using NuGet.Frameworks; +using Xunit; + +namespace Microsoft.DotNet.Cli.Utils.Tests +{ + public class GivenAProjectDependencyCommandResolverBeingUsedWithMSBuild : TestBase + { + private TestInstance MSBuildTestProjectInstance; + private RepoDirectoriesProvider _repoDirectoriesProvider; + private string _configuration; + + public GivenAProjectDependencyCommandResolverBeingUsedWithMSBuild() + { + MSBuildTestProjectInstance = + TestAssetsManager.CreateTestInstance("MSBuildTestAppWithToolInDependencies"); + _repoDirectoriesProvider = new RepoDirectoriesProvider(); + _configuration = "Debug"; + + new Restore3Command() + .WithWorkingDirectory(MSBuildTestProjectInstance.Path) + .Execute($"-s {_repoDirectoriesProvider.TestPackages}") + .Should() + .Pass(); + + new Build3Command() + .WithWorkingDirectory(MSBuildTestProjectInstance.Path) + .Execute($"-c {_configuration}") + .Should() + .Pass(); + + Environment.SetEnvironmentVariable( + Constants.MSBUILD_EXE_PATH, + Path.Combine(_repoDirectoriesProvider.Stage2Sdk, "MSBuild.exe")); + } + + [Fact] + public void ItReturnsACommandSpecWithDotnetAsFileNameAndCommandNameInArgsWhenCommandNameExistsInMSBuildProjectDependencies() + { + var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); + + var commandResolverArguments = new CommandResolverArguments() + { + CommandName = "dotnet-portable", + CommandArguments = null, + ProjectDirectory = MSBuildTestProjectInstance.Path, + Configuration = _configuration, + Framework = FrameworkConstants.CommonFrameworks.NetCoreApp10 + }; + + var result = projectDependenciesCommandResolver.Resolve(commandResolverArguments); + + result.Should().NotBeNull(); + + var commandFile = Path.GetFileNameWithoutExtension(result.Path); + + commandFile.Should().Be("dotnet"); + + result.Args.Should().Contain(commandResolverArguments.CommandName); + } + + [Fact] + public void ItPassesDepsfileArgToHostWhenReturningACommandSpecForMSBuildProject() + { + var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); + + var commandResolverArguments = new CommandResolverArguments() + { + CommandName = "dotnet-portable", + CommandArguments = null, + ProjectDirectory = MSBuildTestProjectInstance.Path, + Configuration = _configuration, + Framework = FrameworkConstants.CommonFrameworks.NetCoreApp10 + }; + + var result = projectDependenciesCommandResolver.Resolve(commandResolverArguments); + + result.Should().NotBeNull(); + result.Args.Should().Contain("--depsfile"); + } + + [Fact] + public void ItReturnsNullWhenCommandNameDoesNotExistInProjectDependenciesForMSBuildProject() + { + var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); + + var commandResolverArguments = new CommandResolverArguments() + { + CommandName = "nonexistent-command", + CommandArguments = null, + ProjectDirectory = MSBuildTestProjectInstance.Path, + Configuration = _configuration, + Framework = FrameworkConstants.CommonFrameworks.NetCoreApp10 + }; + + var result = projectDependenciesCommandResolver.Resolve(commandResolverArguments); + + result.Should().BeNull(); + } + + [Fact] + public void ItSetsDepsfileToOutputInCommandspecForMSBuild() + { + var projectDependenciesCommandResolver = SetupProjectDependenciesCommandResolver(); + + var testInstance = TestAssetsManager.CreateTestInstance("MSBuildTestAppWithToolInDependencies"); + + var outputDir = Path.Combine(testInstance.Path, "out"); + + var commandResolverArguments = new CommandResolverArguments() + { + CommandName = "dotnet-portable", + CommandArguments = null, + ProjectDirectory = testInstance.Path, + Configuration = _configuration, + Framework = FrameworkConstants.CommonFrameworks.NetCoreApp10, + OutputPath = outputDir + }; + + new Restore3Command() + .WithWorkingDirectory(testInstance.Path) + .Execute($"-s {_repoDirectoriesProvider.TestPackages}") + .Should() + .Pass(); + + new Build3Command() + .WithWorkingDirectory(testInstance.Path) + .Execute($"-c {_configuration} -o {outputDir}") + .Should() + .Pass(); + + var result = projectDependenciesCommandResolver.Resolve(commandResolverArguments); + + var depsFilePath = Path.Combine(outputDir, "MSBuildTestAppWithToolInDependencies.deps.json"); + + result.Should().NotBeNull(); + result.Args.Should().Contain($"--depsfile {depsFilePath}"); + } + + private ProjectDependenciesCommandResolver SetupProjectDependenciesCommandResolver( + IEnvironmentProvider environment = null, + IPackagedCommandSpecFactory packagedCommandSpecFactory = null) + { + environment = environment ?? new EnvironmentProvider(); + packagedCommandSpecFactory = packagedCommandSpecFactory ?? new PackagedCommandSpecFactory(); + + var projectDependenciesCommandResolver = new ProjectDependenciesCommandResolver(environment, packagedCommandSpecFactory); + + return projectDependenciesCommandResolver; + } + } +} diff --git a/test/dotnet.Tests/GivenAProjectToolsCommandResolver.cs b/test/Microsoft.DotNet.Cli.Utils.Tests/GivenAProjectToolsCommandResolver.cs similarity index 99% rename from test/dotnet.Tests/GivenAProjectToolsCommandResolver.cs rename to test/Microsoft.DotNet.Cli.Utils.Tests/GivenAProjectToolsCommandResolver.cs index 78fb78c11..bd9cb98cc 100644 --- a/test/dotnet.Tests/GivenAProjectToolsCommandResolver.cs +++ b/test/Microsoft.DotNet.Cli.Utils.Tests/GivenAProjectToolsCommandResolver.cs @@ -4,7 +4,6 @@ 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; @@ -236,7 +235,7 @@ namespace Microsoft.DotNet.Tests { packagedCommandSpecFactory = packagedCommandSpecFactory ?? new PackagedCommandSpecFactory(); - var projectToolsCommandResolver = new ProjectToolsCommandResolver(packagedCommandSpecFactory); + var projectToolsCommandResolver = new ProjectToolsCommandResolver(packagedCommandSpecFactory, new EnvironmentProvider()); return projectToolsCommandResolver; } diff --git a/test/Microsoft.DotNet.Cli.Utils.Tests/project.json b/test/Microsoft.DotNet.Cli.Utils.Tests/project.json index 9a96903d8..1ded66215 100644 --- a/test/Microsoft.DotNet.Cli.Utils.Tests/project.json +++ b/test/Microsoft.DotNet.Cli.Utils.Tests/project.json @@ -46,7 +46,8 @@ "moq.netcore": "4.4.0-beta8", "xunit": "2.2.0-beta3-build3330", "dotnet-test-xunit": "1.0.0-rc2-350904-49", - "Microsoft.DotNet.PlatformAbstractions": "1.0.1-beta-000933" + "Microsoft.DotNet.PlatformAbstractions": "1.0.1-beta-000933", + "Microsoft.Build.Runtime": "15.1.316-preview5" }, "frameworks": { "netcoreapp1.0": { diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/RepoDirectoriesProvider.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/RepoDirectoriesProvider.cs new file mode 100644 index 000000000..038c6cfad --- /dev/null +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/RepoDirectoriesProvider.cs @@ -0,0 +1,70 @@ +using System; +using System.IO; +using System.Linq; +using Microsoft.DotNet.PlatformAbstractions; + +namespace Microsoft.DotNet.Tools.Test.Utilities +{ + public class RepoDirectoriesProvider + { + private static string s_repoRoot; + + private string _artifacts; + private string _builtDotnet; + private string _nugetPackages; + private string _stage2Sdk; + private string _testPackages; + + public static string RepoRoot + { + get + { + if (!string.IsNullOrEmpty(s_repoRoot)) + { + return s_repoRoot; + } + +#if NET451 + string directory = AppDomain.CurrentDomain.BaseDirectory; +#else + string directory = AppContext.BaseDirectory; +#endif + + while (!Directory.Exists(Path.Combine(directory, ".git")) && directory != null) + { + directory = Directory.GetParent(directory).FullName; + } + + if (directory == null) + { + throw new Exception("Cannot find the git repository root"); + } + + s_repoRoot = directory; + return s_repoRoot; + } + } + + public string Artifacts => _artifacts; + public string BuiltDotnet => _builtDotnet; + public string NugetPackages => _nugetPackages; + public string Stage2Sdk => _stage2Sdk; + public string TestPackages => _testPackages; + + public RepoDirectoriesProvider( + string artifacts = null, + string builtDotnet = null, + string nugetPackages = null, + string corehostPackages = null, + string corehostDummyPackages = null) + { + var currentRid = RuntimeEnvironment.GetRuntimeIdentifier(); + + _artifacts = artifacts ?? Path.Combine(RepoRoot, "artifacts", currentRid); + _nugetPackages = nugetPackages ?? Path.Combine(RepoRoot, ".nuget", "packages"); + _builtDotnet = builtDotnet ?? Path.Combine(_artifacts, "intermediate", "sharedFrameworkPublish"); + _stage2Sdk = Directory.EnumerateDirectories(Path.Combine(_artifacts, "stage2", "sdk")).First(); + _testPackages = Path.Combine(_artifacts, "tests", "packages"); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs index dce3e7e54..bc134816d 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs @@ -22,35 +22,13 @@ namespace Microsoft.DotNet.Tools.Test.Utilities protected const string DefaultLibraryFramework = "netstandard1.5"; private TempRoot _temp; private static TestAssetsManager s_testsAssetsMgr; - private static string s_repoRoot; + protected static string RepoRoot { get { - if (!string.IsNullOrEmpty(s_repoRoot)) - { - return s_repoRoot; - } - -#if NET451 - string directory = AppDomain.CurrentDomain.BaseDirectory; -#else - string directory = AppContext.BaseDirectory; -#endif - - while (!Directory.Exists(Path.Combine(directory, ".git")) && directory != null) - { - directory = Directory.GetParent(directory).FullName; - } - - if (directory == null) - { - throw new Exception("Cannot find the git repository root"); - } - - s_repoRoot = directory; - return s_repoRoot; + return RepoDirectoriesProvider.RepoRoot; } } diff --git a/test/dotnet.Tests/GivenAProjectToolsCommandResolverPolicy.cs b/test/dotnet.Tests/GivenAProjectToolsCommandResolverPolicy.cs deleted file mode 100644 index 915be7f2f..000000000 --- a/test/dotnet.Tests/GivenAProjectToolsCommandResolverPolicy.cs +++ /dev/null @@ -1,39 +0,0 @@ -// 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) - }); - } - } -}