diff --git a/build/Nuget/Microsoft.NET.Sdk.Nuget.targets b/build/Nuget/Microsoft.NET.Sdk.Nuget.targets index f4da317fe41b..a2a7a38b8fd2 100644 --- a/build/Nuget/Microsoft.NET.Sdk.Nuget.targets +++ b/build/Nuget/Microsoft.NET.Sdk.Nuget.targets @@ -48,6 +48,10 @@ vs.localizedResources + + + + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder.cs b/src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder.cs index 58f5c0e1980f..74e9659d3a3b 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/DependencyContextBuilder.cs @@ -27,6 +27,7 @@ internal class DependencyContextBuilder private CompilationOptions _compilationOptions; private string _referenceAssembliesPath; private Dictionary _filteredPackages; + private bool _includeMainProjectInDepsFile = true; public DependencyContextBuilder(SingleProjectInfo mainProjectInfo, ProjectContext projectContext) { @@ -37,6 +38,12 @@ public DependencyContextBuilder(SingleProjectInfo mainProjectInfo, ProjectContex _versionFolderPathResolver = new VersionFolderPathResolver(rootPath: null); } + public DependencyContextBuilder WithMainProjectInDepsFile(bool includeMainProjectInDepsFile) + { + _includeMainProjectInDepsFile = includeMainProjectInDepsFile; + return this; + } + public DependencyContextBuilder WithFrameworkReferences(IEnumerable frameworkReferences) { // note: Framework libraries only export compile-time stuff @@ -101,32 +108,40 @@ public DependencyContext Build() var runtimeSignature = GenerateRuntimeSignature(runtimeExports); - RuntimeLibrary projectRuntimeLibrary = GetProjectRuntimeLibrary( - _mainProjectInfo, - _projectContext, - dependencyLookup); - IEnumerable runtimeLibraries = - new[] { projectRuntimeLibrary } + IEnumerable runtimeLibraries = Enumerable.Empty(); + if (_includeMainProjectInDepsFile) + { + runtimeLibraries = runtimeLibraries.Concat(new[] + { + GetProjectRuntimeLibrary( + _mainProjectInfo, + _projectContext, + dependencyLookup) + }); + } + runtimeLibraries = runtimeLibraries .Concat(GetLibraries(runtimeExports, libraryLookup, dependencyLookup, runtime: true).Cast()) .Concat(GetDirectReferenceRuntimeLibraries()); - IEnumerable compilationLibraries; + IEnumerable compilationLibraries = Enumerable.Empty(); if (includeCompilationLibraries) { - CompilationLibrary projectCompilationLibrary = GetProjectCompilationLibrary( - _mainProjectInfo, - _projectContext, - dependencyLookup); - compilationLibraries = - new[] { projectCompilationLibrary } + if (_includeMainProjectInDepsFile) + { + compilationLibraries = compilationLibraries.Concat(new[] + { + GetProjectCompilationLibrary( + _mainProjectInfo, + _projectContext, + dependencyLookup) + }); + } + + compilationLibraries = compilationLibraries .Concat(GetFrameworkLibraries()) .Concat(GetLibraries(compilationExports, libraryLookup, dependencyLookup, runtime: false).Cast()) .Concat(GetDirectReferenceCompilationLibraries()); } - else - { - compilationLibraries = Enumerable.Empty(); - } var targetInfo = new TargetInfo( _projectContext.LockFileTarget.TargetFramework.DotNetFrameworkName, diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs index 9db941e80a6a..5d6d39a76290 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/GenerateDepsFile.cs @@ -49,6 +49,9 @@ public class GenerateDepsFile : TaskBase [Required] public ITaskItem[] AssemblySatelliteAssemblies { get; set; } + [Required] + public bool IncludeMainProject { get; set; } + [Required] public ITaskItem[] ReferencePaths { get; set; } @@ -117,11 +120,11 @@ protected override void ExecuteCore() CompilationOptions compilationOptions = CompilationOptionsConverter.ConvertFrom(CompilerOptions); SingleProjectInfo mainProject = SingleProjectInfo.Create( - ProjectPath, - AssemblyName, - AssemblyExtension, - AssemblyVersion, - AssemblySatelliteAssemblies); + ProjectPath, + AssemblyName, + AssemblyExtension, + AssemblyVersion, + AssemblySatelliteAssemblies); IEnumerable frameworkReferences = ReferenceInfo.CreateFrameworkReferenceInfos(ReferencePaths); @@ -142,6 +145,7 @@ protected override void ExecuteCore() IsSelfContained); DependencyContext dependencyContext = new DependencyContextBuilder(mainProject, projectContext) + .WithMainProjectInDepsFile(IncludeMainProject) .WithFrameworkReferences(frameworkReferences) .WithDirectReferences(directReferences) .WithReferenceProjectInfos(referenceProjects) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj index 131c60ecb85f..9f0a4b2eb4fa 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj @@ -188,6 +188,11 @@ Strings.resx + + + PreserveNewest + + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/GenerateDeps/GenerateDeps.proj b/src/Tasks/Microsoft.NET.Build.Tasks/build/GenerateDeps/GenerateDeps.proj new file mode 100644 index 000000000000..9a6ba50b67f6 --- /dev/null +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/GenerateDeps/GenerateDeps.proj @@ -0,0 +1,42 @@ + + + + + + + + + $([System.IO.Path]::GetDirectoryName($(ProjectAssetsFile))) + $(ToolFolder)\$(ToolName).deps.json + + Exe + false + + + + + + + + + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets index 7eb92b5e6378..fa42fc1ae9ba 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Publish.targets @@ -480,6 +480,7 @@ Copyright (c) .NET Foundation. All rights reserved. AssemblySatelliteAssemblies="@(IntermediateSatelliteAssembliesWithTargetPath)" ReferencePaths="@(ReferencePath)" ReferenceSatellitePaths="@(ReferenceSatellitePaths)" + IncludeMainProject="$(IncludeMainProjectInDepsFile)" RuntimeIdentifier="$(RuntimeIdentifier)" PlatformLibraryName="$(MicrosoftNETPlatformLibrary)" FilesToSkip="@(_ConflictPackageFiles);@(_PublishConflictPackageFiles)" diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.Common.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.Common.targets index 1cdc34e1eca1..fe9b99e822ea 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.Common.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.Common.targets @@ -28,6 +28,10 @@ Copyright (c) .NET Foundation. All rights reserved. Microsoft.NETCore.App;NETStandard.Library + + + netcoreapp$(BundledNETCoreAppTargetFrameworkVersion) + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.props b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.props index 8333c8e1f879..fe7f2001550f 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.props +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.props @@ -103,6 +103,11 @@ Copyright (c) .NET Foundation. All rights reserved. false false + + + + $(MSBuildThisFileDirectory)GenerateDeps\GenerateDeps.proj + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets index ad3014cb878e..87fe47cbbaa7 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/build/Microsoft.NET.Sdk.targets @@ -45,6 +45,7 @@ Copyright (c) .NET Foundation. All rights reserved. $(AssemblyName).runtimeconfig.json $(TargetDir)$(ProjectRuntimeConfigFileName) $(TargetDir)$(AssemblyName).runtimeconfig.dev.json + true @@ -62,7 +63,7 @@ Copyright (c) .NET Foundation. All rights reserved. <_DotNetHostPolicyLibraryName>$(_NativeLibraryPrefix)hostpolicy$(_NativeLibraryExtension) <_DotNetHostFxrLibraryName>$(_NativeLibraryPrefix)hostfxr$(_NativeLibraryExtension) - + $(CoreBuildDependsOn); @@ -100,6 +101,7 @@ Copyright (c) .NET Foundation. All rights reserved. AssemblySatelliteAssemblies="@(IntermediateSatelliteAssembliesWithTargetPath)" ReferencePaths="@(ReferencePath)" ReferenceSatellitePaths="@(ReferenceSatellitePaths)" + IncludeMainProject="$(IncludeMainProjectInDepsFile)" RuntimeIdentifier="$(RuntimeIdentifier)" PlatformLibraryName="$(MicrosoftNETPlatformLibrary)" FilesToSkip="@(_ConflictPackageFiles)" diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToExcludeTheMainProjectFromTheDepsFile.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToExcludeTheMainProjectFromTheDepsFile.cs new file mode 100644 index 000000000000..d8f51e3be650 --- /dev/null +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToExcludeTheMainProjectFromTheDepsFile.cs @@ -0,0 +1,72 @@ +using FluentAssertions; +using Microsoft.NET.TestFramework; +using Microsoft.NET.TestFramework.Assertions; +using Microsoft.NET.TestFramework.Commands; +using Microsoft.NET.TestFramework.ProjectConstruction; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml.Linq; +using Xunit; + +using static Microsoft.NET.TestFramework.Commands.MSBuildTest; + +namespace Microsoft.NET.Build.Tests +{ + public class GivenThatWeWantToExcludeTheMainProjectFromTheDepsFile : SdkTest + { + [Fact] + public void It_builds_successfully() + { + if (UsingFullFrameworkMSBuild) + { + // Fullframework NuGet versioning on Jenkins infrastructure issue + // https://github.com/dotnet/sdk/issues/1041 + + // Disabled on full framework MSBuild until CI machines have VS with bundled .NET Core / .NET Standard versions + // See https://github.com/dotnet/sdk/issues/1077 + return; + } + + TestProject testProject = new TestProject() + { + Name = "ExcludeMainProjectFromDeps", + IsSdkProject = true, + TargetFrameworks = "netcoreapp2.0", + IsExe = true, + }; + + TestProject referencedProject = new TestProject() + { + Name = "ReferencedProject", + IsSdkProject = true, + TargetFrameworks = "netstandard2.0", + IsExe = false + }; + + testProject.ReferencedProjects.Add(referencedProject); + + var testProjectInstance = _testAssetsManager.CreateTestProject(testProject, testProject.Name) + .WithProjectChanges((path, project) => + { + if (Path.GetFileNameWithoutExtension(path) == testProject.Name) + { + var ns = project.Root.Name.Namespace; + + var propertyGroup = new XElement(ns + "PropertyGroup"); + project.Root.Add(propertyGroup); + + propertyGroup.Add(new XElement(ns + "IncludeMainProjectInDepsFile", "false")); + } + }) + .Restore(testProject.Name); + + var buildCommand = new BuildCommand(Stage0MSBuild, testProjectInstance.TestRoot, testProject.Name); + + buildCommand.Execute() + .Should() + .Pass(); + } + } +} diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToGenerateADepsFileForATool.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToGenerateADepsFileForATool.cs new file mode 100644 index 000000000000..13b62906055b --- /dev/null +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToGenerateADepsFileForATool.cs @@ -0,0 +1,259 @@ +using FluentAssertions; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.NET.TestFramework; +using Microsoft.NET.TestFramework.Assertions; +using Microsoft.NET.TestFramework.Commands; +using Microsoft.NET.TestFramework.ProjectConstruction; +using NuGet.Packaging; +using NuGet.ProjectModel; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Xml.Linq; +using Xunit; +using static Microsoft.NET.TestFramework.Commands.MSBuildTest; + +namespace Microsoft.NET.Build.Tests +{ + public class GivenThatWeWantToGenerateADepsFileForATool : SdkTest + { + [Fact] + public void It_creates_a_deps_file_for_the_tool_and_the_tool_runs() + { + if (UsingFullFrameworkMSBuild) + { + // Fullframework NuGet versioning on Jenkins infrastructure issue + // https://github.com/dotnet/sdk/issues/1041 + + // Disabled on full framework MSBuild until CI machines have VS with bundled .NET Core / .NET Standard versions + // See https://github.com/dotnet/sdk/issues/1077 + return; + } + + TestProject toolProject = new TestProject() + { + Name = "TestTool", + IsSdkProject = true, + TargetFrameworks = "netcoreapp2.0", + IsExe = true + }; + + GenerateDepsAndRunTool(toolProject) + .Should() + .Pass() + .And.HaveStdOutContaining("Hello World!"); + } + + [Fact] + public void It_handles_conflicts_when_creating_a_tool_deps_file() + { + if (UsingFullFrameworkMSBuild) + { + // Fullframework NuGet versioning on Jenkins infrastructure issue + // https://github.com/dotnet/sdk/issues/1041 + + // Disabled on full framework MSBuild until CI machines have VS with bundled .NET Core / .NET Standard versions + // See https://github.com/dotnet/sdk/issues/1077 + return; + } + + TestProject toolProject = new TestProject() + { + Name = "DependencyContextTool", + IsSdkProject = true, + TargetFrameworks = "netcoreapp2.0", + IsExe = true + }; + + toolProject.PackageReferences.Add(new TestPackageReference("Microsoft.Extensions.DependencyModel", "1.1.0", null)); + + string toolSource = @" +using System; +using System.Linq; +using Microsoft.Extensions.DependencyModel; + +class Program +{ + static void Main(string[] args) + { + if(DependencyContext.Default?.RuntimeGraph?.Any() == true) + { + Console.WriteLine(""Successfully loaded runtime graph""); + } + else + { + Console.WriteLine(""Couldn't load runtime graph""); + } + } +}"; + + toolProject.SourceFiles.Add("Program.cs", toolSource); + + GenerateDepsAndRunTool(toolProject, "ToolConflictResolution") + .Should() + .Pass() + .And.HaveStdOutContaining("Successfully loaded runtime graph"); + } + + // This method duplicates a lot of logic from the CLI in order to test generating deps files for tools in the SDK repo + private CommandResult GenerateDepsAndRunTool(TestProject toolProject, [CallerMemberName] string callingMethod = "") + { + DeleteFolder(Path.Combine(RepoInfo.NuGetCachePath, toolProject.Name.ToLowerInvariant())); + DeleteFolder(Path.Combine(RepoInfo.NuGetCachePath, ".tools", toolProject.Name.ToLowerInvariant())); + + var toolProjectInstance = _testAssetsManager.CreateTestProject(toolProject, callingMethod, identifier: toolProject.Name) + .Restore(toolProject.Name); + + var packCommand = new PackCommand(Stage0MSBuild, Path.Combine(toolProjectInstance.TestRoot, toolProject.Name)); + + packCommand.Execute() + .Should() + .Pass(); + + string nupkgPath = Path.Combine(packCommand.ProjectRootPath, "bin", "Debug"); + + TestProject toolReferencer = new TestProject() + { + Name = "ToolReferencer", + IsSdkProject = true, + TargetFrameworks = "netcoreapp2.0" + }; + + var toolReferencerInstance = _testAssetsManager.CreateTestProject(toolReferencer, callingMethod, identifier: toolReferencer.Name) + .WithProjectChanges(project => + { + var ns = project.Root.Name.Namespace; + + var itemGroup = new XElement(ns + "ItemGroup"); + project.Root.Add(itemGroup); + + itemGroup.Add(new XElement(ns + "DotNetCliToolReference", + new XAttribute("Include", toolProject.Name), + new XAttribute("Version", "1.0.0"))); + }); + + var restoreCommand = toolReferencerInstance.GetRestoreCommand(toolReferencer.Name); + restoreCommand.AddSource(nupkgPath); + restoreCommand.Execute().Should().Pass(); + + string toolAssetsFilePath = Path.Combine(RepoInfo.NuGetCachePath, ".tools", toolProject.Name.ToLowerInvariant(), "1.0.0", toolProject.TargetFrameworks, "project.assets.json"); + var toolAssetsFile = new LockFileFormat().Read(toolAssetsFilePath); + + var args = new List(); + + string generateDepsProjectPath = Path.Combine(RepoInfo.SdksPath, "Microsoft.NET.Sdk", "build", "GenerateDeps", "GenerateDeps.proj"); + args.Add(generateDepsProjectPath); + + args.Add($"/p:ProjectAssetsFile=\"{toolAssetsFilePath}\""); + + args.Add($"/p:ToolName={toolProject.Name}"); + + string depsFilePath = Path.Combine(Path.GetDirectoryName(toolAssetsFilePath), toolProject.Name + ".deps.json"); + args.Add($"/p:ProjectDepsFilePath={depsFilePath}"); + + var toolTargetFramework = toolAssetsFile.Targets.First().TargetFramework.GetShortFolderName(); + args.Add($"/p:TargetFramework={toolProject.TargetFrameworks}"); + + // Look for the .props file in the Microsoft.NETCore.App package, until NuGet + // generates .props and .targets files for tool restores (https://github.com/NuGet/Home/issues/5037) + var platformLibrary = toolAssetsFile.Targets + .Single() + .Libraries + .FirstOrDefault(e => e.Name.Equals("Microsoft.NETCore.App", StringComparison.OrdinalIgnoreCase)); + + if (platformLibrary != null) + { + string buildRelativePath = platformLibrary.Build.FirstOrDefault()?.Path; + + var platformLibraryPath = GetPackageDirectory(toolAssetsFile, platformLibrary); + + if (platformLibraryPath != null && buildRelativePath != null) + { + // Get rid of "_._" filename + buildRelativePath = Path.GetDirectoryName(buildRelativePath); + + string platformLibraryBuildFolderPath = Path.Combine(platformLibraryPath, buildRelativePath); + var platformLibraryPropsFile = Directory.GetFiles(platformLibraryBuildFolderPath, "*.props").FirstOrDefault(); + + if (platformLibraryPropsFile != null) + { + args.Add($"/p:AdditionalImport={platformLibraryPropsFile}"); + } + } + } + + var generateDepsCommand = Stage0MSBuild.CreateCommandForTarget("BuildDepsJson", args.ToArray()); + + generateDepsCommand.Execute() + .Should() + .Pass(); + + var toolLibrary = toolAssetsFile.Targets + .Single() + .Libraries.FirstOrDefault( + l => StringComparer.OrdinalIgnoreCase.Equals(l.Name, toolProject.Name)); + + var toolAssembly = toolLibrary?.RuntimeAssemblies + .FirstOrDefault(r => Path.GetFileNameWithoutExtension(r.Path) == toolProject.Name); + + var toolPackageDirectory = GetPackageDirectory(toolAssetsFile, toolLibrary); + + var toolAssemblyPath = Path.Combine( + toolPackageDirectory, + toolAssembly.Path); + + var dotnetArgs = new List(); + dotnetArgs.Add("exec"); + + dotnetArgs.Add("--depsfile"); + dotnetArgs.Add(depsFilePath); + + foreach (var packageFolder in GetNormalizedPackageFolders(toolAssetsFile)) + { + dotnetArgs.Add("--additionalprobingpath"); + dotnetArgs.Add(packageFolder); + } + + dotnetArgs.Add(toolAssemblyPath); + + ICommand toolCommand = Command.Create(RepoInfo.DotNetHostPath, dotnetArgs) + .CaptureStdOut(); + toolCommand = RepoInfo.AddTestEnvironmentVariables(toolCommand); + + + var toolResult = toolCommand.Execute(); + + return toolResult; + } + + private static void DeleteFolder(string path) + { + if (Directory.Exists(path)) + { + Directory.Delete(path, true); + } + } + + private static IEnumerable GetNormalizedPackageFolders(LockFile lockFile) + { + return lockFile.PackageFolders.Select(pf => pf.Path.TrimEnd(Path.DirectorySeparatorChar)); + } + + private static string GetPackageDirectory(LockFile lockFile, LockFileTargetLibrary library) + { + var packageFolders = GetNormalizedPackageFolders(lockFile); + + var packageFoldersCount = packageFolders.Count(); + var userPackageFolder = packageFoldersCount == 1 ? string.Empty : packageFolders.First(); + var fallbackPackageFolders = packageFoldersCount > 1 ? packageFolders.Skip(1) : packageFolders; + + var packageDirectory = new FallbackPackagePathResolver(userPackageFolder, fallbackPackageFolders) + .GetPackageDirectory(library.Name, library.Version); + + return packageDirectory; + } + } +} diff --git a/test/Microsoft.NET.TestFramework/Commands/MSBuildTest.cs b/test/Microsoft.NET.TestFramework/Commands/MSBuildTest.cs index 66b1884912d2..e53a4a7acde4 100644 --- a/test/Microsoft.NET.TestFramework/Commands/MSBuildTest.cs +++ b/test/Microsoft.NET.TestFramework/Commands/MSBuildTest.cs @@ -45,10 +45,7 @@ private ICommand CreateCommand(params string[] args) command = Command.Create(DotNetHostPath, newArgs); } - // Set NUGET_PACKAGES environment variable to match value from build.ps1 - command = command.EnvironmentVariable("NUGET_PACKAGES", Path.Combine(RepoInfo.RepoRoot, "packages")); - - command = command.EnvironmentVariable("MSBuildSDKsPath", RepoInfo.SdksPath); + command = RepoInfo.AddTestEnvironmentVariables(command); return command; } diff --git a/test/Microsoft.NET.TestFramework/RepoInfo.cs b/test/Microsoft.NET.TestFramework/RepoInfo.cs index c66df5cade78..1b13366c1f80 100644 --- a/test/Microsoft.NET.TestFramework/RepoInfo.cs +++ b/test/Microsoft.NET.TestFramework/RepoInfo.cs @@ -69,7 +69,7 @@ public static string PackagesPath public static string NuGetCachePath { - get { return Path.Combine(RepoRoot, "Packages"); } + get { return Path.Combine(RepoRoot, "packages"); } } @@ -102,5 +102,15 @@ public static string GetBaseDirectory() return directory; } + + public static ICommand AddTestEnvironmentVariables(ICommand command) + { + // Set NUGET_PACKAGES environment variable to match value from build.ps1 + command = command.EnvironmentVariable("NUGET_PACKAGES", RepoInfo.NuGetCachePath); + + command = command.EnvironmentVariable("MSBuildSDKsPath", RepoInfo.SdksPath); + + return command; + } } } \ No newline at end of file