From f2d917ed2eec56c18ed0516ee00c5672743d60c3 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Fri, 29 Apr 2016 22:19:35 -0500 Subject: [PATCH 1/3] Execute 'csc' with working directory set to the project directory. When using a ruleset with a relative path in buildOptions, csc can't find the file because it is not working in the same directory as the project. Fix #2710 --- .../TestLibraryWithRuleSet/.noautobuild | 0 .../TestLibraryWithRuleSet/Violations.cs | 26 +++++++++++++++++++ .../TestLibraryWithRuleSet/project.json | 16 ++++++++++++ .../TestProjects/TestRuleSet/global.ruleset | 6 +++++ .../BuiltInCommand.cs | 20 ++++++++++---- .../commands/dotnet-compile-csc/Program.cs | 1 + .../dotnet-compile/ManagedCompiler.cs | 1 + .../Assertions/CommandResultAssertions.cs | 7 +++++ .../Commands/BuildCommand.cs | 9 +++++-- .../GivenDotnetBuildBuildsProjects.cs | 20 ++++++++++++++ 10 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 TestAssets/TestProjects/TestRuleSet/TestLibraryWithRuleSet/.noautobuild create mode 100644 TestAssets/TestProjects/TestRuleSet/TestLibraryWithRuleSet/Violations.cs create mode 100644 TestAssets/TestProjects/TestRuleSet/TestLibraryWithRuleSet/project.json create mode 100644 TestAssets/TestProjects/TestRuleSet/global.ruleset diff --git a/TestAssets/TestProjects/TestRuleSet/TestLibraryWithRuleSet/.noautobuild b/TestAssets/TestProjects/TestRuleSet/TestLibraryWithRuleSet/.noautobuild new file mode 100644 index 000000000..e69de29bb diff --git a/TestAssets/TestProjects/TestRuleSet/TestLibraryWithRuleSet/Violations.cs b/TestAssets/TestProjects/TestRuleSet/TestLibraryWithRuleSet/Violations.cs new file mode 100644 index 000000000..d8e16e1c6 --- /dev/null +++ b/TestAssets/TestProjects/TestRuleSet/TestLibraryWithRuleSet/Violations.cs @@ -0,0 +1,26 @@ +// 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 TestLibrary +{ + public class AttributeWithoutUsage : Attribute + { + } + + public class ClassWithUndisposedStream + { + private Stream _nonDisposedStream = new MemoryStream(); + + public ClassWithUndisposedStream() + { + } + + public Stream GetStream() + { + return _nonDisposedStream; + } + } +} diff --git a/TestAssets/TestProjects/TestRuleSet/TestLibraryWithRuleSet/project.json b/TestAssets/TestProjects/TestRuleSet/TestLibraryWithRuleSet/project.json new file mode 100644 index 000000000..25e3b31ed --- /dev/null +++ b/TestAssets/TestProjects/TestRuleSet/TestLibraryWithRuleSet/project.json @@ -0,0 +1,16 @@ +{ + "version": "1.0.0-*", + "buildOptions": { + "additionalArguments": [ "/ruleset:../global.ruleset" ] + }, + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-24027", + "System.Runtime.Analyzers": { + "version": "1.1.0", + "type": "build" + } + }, + "frameworks": { + "netstandard1.5": {} + } +} diff --git a/TestAssets/TestProjects/TestRuleSet/global.ruleset b/TestAssets/TestProjects/TestRuleSet/global.ruleset new file mode 100644 index 000000000..19e8b09b9 --- /dev/null +++ b/TestAssets/TestProjects/TestRuleSet/global.ruleset @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs b/src/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs index 6fbabe352..55db7cb73 100644 --- a/src/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs +++ b/src/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs @@ -20,6 +20,7 @@ namespace Microsoft.DotNet.Cli.Utils private readonly Func _builtInCommand; private readonly StreamForwarder _stdOut; private readonly StreamForwarder _stdErr; + private string _workingDirectory; public string CommandName { get; } public string CommandArgs => string.Join(" ", _commandArgs); @@ -38,6 +39,7 @@ namespace Microsoft.DotNet.Cli.Utils { TextWriter originalConsoleOut = Console.Out; TextWriter originalConsoleError = Console.Error; + string originalWorkingDirectory = Directory.GetCurrentDirectory(); try { @@ -52,6 +54,11 @@ namespace Microsoft.DotNet.Cli.Utils // Reset the Reporters to the new Console Out and Error. Reporter.Reset(); + if (!string.IsNullOrEmpty(_workingDirectory)) + { + Directory.SetCurrentDirectory(_workingDirectory); + } + var taskOut = _stdOut.BeginRead(new StreamReader(outStream)); var taskErr = _stdErr.BeginRead(new StreamReader(errorStream)); @@ -71,6 +78,7 @@ namespace Microsoft.DotNet.Cli.Utils { Console.SetOut(originalConsoleOut); Console.SetError(originalConsoleError); + Directory.SetCurrentDirectory(originalWorkingDirectory); Reporter.Reset(); } @@ -100,6 +108,13 @@ namespace Microsoft.DotNet.Cli.Utils return this; } + public ICommand WorkingDirectory(string workingDirectory) + { + _workingDirectory = workingDirectory; + + return this; + } + public CommandResolutionStrategy ResolutionStrategy { get @@ -132,10 +147,5 @@ namespace Microsoft.DotNet.Cli.Utils { throw new NotImplementedException(); } - - public ICommand WorkingDirectory(string projectDirectory) - { - throw new NotImplementedException(); - } } } diff --git a/src/dotnet/commands/dotnet-compile-csc/Program.cs b/src/dotnet/commands/dotnet-compile-csc/Program.cs index 528b8f8b9..5f6c24784 100644 --- a/src/dotnet/commands/dotnet-compile-csc/Program.cs +++ b/src/dotnet/commands/dotnet-compile-csc/Program.cs @@ -84,6 +84,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Csc // Execute CSC! var result = RunCsc(new string[] { $"-noconfig", "@" + $"{rsp}" }) + .WorkingDirectory(Directory.GetCurrentDirectory()) .ForwardStdErr() .ForwardStdOut() .Execute(); diff --git a/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs b/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs index 332ea231e..0b80bfef5 100644 --- a/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs +++ b/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs @@ -189,6 +189,7 @@ namespace Microsoft.DotNet.Tools.Compiler Reporter outputReporter = Reporter.Output; CommandResult result = _commandFactory.Create($"compile-{compilerName}", new[] { $"@{rsp}" }) + .WorkingDirectory(context.ProjectDirectory) .OnErrorLine(line => HandleCompilerOutputLine(line, context, diagnostics, errorReporter)) .OnOutputLine(line => HandleCompilerOutputLine(line, context, diagnostics, outputReporter)) .Execute(); diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/CommandResultAssertions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/CommandResultAssertions.cs index 676aaa410..5a04d82dd 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/CommandResultAssertions.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Assertions/CommandResultAssertions.cs @@ -81,6 +81,13 @@ namespace Microsoft.DotNet.Tools.Test.Utilities return new AndConstraint(this); } + public AndConstraint NotHaveStdErrContaining(string pattern) + { + Execute.Assertion.ForCondition(!_commandResult.StdErr.Contains(pattern)) + .FailWith(AppendDiagnosticsTo($"The command error output contained a result it should not have contained: {pattern}{Environment.NewLine}")); + return new AndConstraint(this); + } + public AndConstraint HaveStdErrMatching(string pattern, RegexOptions options = RegexOptions.None) { Execute.Assertion.ForCondition(Regex.Match(_commandResult.StdErr, pattern, options).Success) diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildCommand.cs index b427bd0de..10e138841 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildCommand.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildCommand.cs @@ -217,11 +217,16 @@ namespace Microsoft.DotNet.Tools.Test.Utilities string cppCompilerFlags="", bool buildProfile=true, bool noIncremental=false, - bool noDependencies=false) + bool noDependencies=false, + bool skipLoadProject=false) : base("dotnet") { _projectPath = projectPath; - _project = ProjectReader.GetProject(projectPath); + + if (!skipLoadProject) + { + _project = ProjectReader.GetProject(projectPath); + } _outputDirectory = output; _buildBasePathDirectory = buildBasePath; diff --git a/test/dotnet-build.Tests/GivenDotnetBuildBuildsProjects.cs b/test/dotnet-build.Tests/GivenDotnetBuildBuildsProjects.cs index 307a62b99..a4ef1f97b 100644 --- a/test/dotnet-build.Tests/GivenDotnetBuildBuildsProjects.cs +++ b/test/dotnet-build.Tests/GivenDotnetBuildBuildsProjects.cs @@ -43,6 +43,26 @@ namespace Microsoft.DotNet.Tools.Builder.Tests .Pass(); } + [Fact] + public void It_builds_projects_with_ruleset_relative_path() + { + var testInstance = TestAssetsManager + .CreateTestInstance("TestRuleSet") + .WithLockFiles(); + + new BuildCommand(Path.Combine("TestLibraryWithRuleSet", "project.json"), skipLoadProject: true) + .WithWorkingDirectory(testInstance.TestRoot) + .ExecuteWithCapturedOutput() + .Should() + .Pass() + .And + .HaveStdErrContaining("CA1001") + .And + .HaveStdErrContaining("CA2213") + .And + .NotHaveStdErrContaining("CA1018"); // this violation is hidden in the ruleset + } + [Fact] public void It_builds_projects_with_a_local_project_json_path() { From 6249829e68424aa3eb01bac1596b9d4c32d70bff Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Mon, 2 May 2016 12:21:12 -0500 Subject: [PATCH 2/3] Fix build-base-path to be converted to a full path. This allows commands to run in different working directories and pass around the full path to the build-base-path, instead of a relative path. --- src/Microsoft.DotNet.Cli.Utils/PathUtility.cs | 14 ++++++++++++++ .../commands/dotnet-build/BuildCommandApp.cs | 3 ++- src/dotnet/commands/dotnet-pack/Program.cs | 3 ++- src/dotnet/commands/dotnet-publish/Program.cs | 3 ++- .../commands/dotnet-test/DotnetTestParams.cs | 3 ++- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs b/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs index 5be91d7a4..483c7c38b 100644 --- a/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs +++ b/src/Microsoft.DotNet.Cli.Utils/PathUtility.cs @@ -223,5 +223,19 @@ namespace Microsoft.DotNet.Tools.Common return Path.GetExtension(filePath).Equals(extension, comparison); } + + /// + /// Gets the fully-qualified path without failing if the + /// path is empty. + /// + public static string GetFullPath(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + return path; + } + + return Path.GetFullPath(path); + } } } \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-build/BuildCommandApp.cs b/src/dotnet/commands/dotnet-build/BuildCommandApp.cs index 758ce8420..02ca1f3d5 100644 --- a/src/dotnet/commands/dotnet-build/BuildCommandApp.cs +++ b/src/dotnet/commands/dotnet-build/BuildCommandApp.cs @@ -6,6 +6,7 @@ using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.InternalAbstractions; using Microsoft.DotNet.ProjectModel; +using Microsoft.DotNet.Tools.Common; using NuGet.Frameworks; // This class is responsible with defining the arguments for the Compile verb. @@ -97,7 +98,7 @@ namespace Microsoft.DotNet.Tools.Compiler } OutputValue = _outputOption.Value(); - BuildBasePathValue = _buildBasePath.Value(); + BuildBasePathValue = PathUtility.GetFullPath(_buildBasePath.Value()); ConfigValue = _configurationOption.Value() ?? Constants.DefaultConfiguration; RuntimeValue = _runtimeOption.Value(); VersionSuffixValue = _versionSuffixOption.Value(); diff --git a/src/dotnet/commands/dotnet-pack/Program.cs b/src/dotnet/commands/dotnet-pack/Program.cs index 4f0bfd723..14ca629b6 100644 --- a/src/dotnet/commands/dotnet-pack/Program.cs +++ b/src/dotnet/commands/dotnet-pack/Program.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.ProjectModel; +using Microsoft.DotNet.Tools.Common; using Microsoft.DotNet.Tools.Pack; namespace Microsoft.DotNet.Tools.Compiler @@ -61,7 +62,7 @@ namespace Microsoft.DotNet.Tools.Compiler var configValue = configuration.Value() ?? Cli.Utils.Constants.DefaultConfiguration; var outputValue = output.Value(); - var buildBasePathValue = buildBasePath.Value(); + var buildBasePathValue = PathUtility.GetFullPath(buildBasePath.Value()); var contexts = ProjectContext.CreateContextForEachFramework(pathValue, settings); var project = contexts.First().ProjectFile; diff --git a/src/dotnet/commands/dotnet-publish/Program.cs b/src/dotnet/commands/dotnet-publish/Program.cs index 7d0a07804..6516098c7 100644 --- a/src/dotnet/commands/dotnet-publish/Program.cs +++ b/src/dotnet/commands/dotnet-publish/Program.cs @@ -6,6 +6,7 @@ using System.IO; using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.ProjectModel; +using Microsoft.DotNet.Tools.Common; namespace Microsoft.DotNet.Tools.Publish { @@ -37,7 +38,7 @@ namespace Microsoft.DotNet.Tools.Publish publish.Framework = framework.Value(); publish.Runtime = runtime.Value(); - publish.BuildBasePath = buildBasePath.Value(); + publish.BuildBasePath = PathUtility.GetFullPath(buildBasePath.Value()); publish.OutputPath = output.Value(); publish.Configuration = configuration.Value() ?? Constants.DefaultConfiguration; publish.NativeSubdirectories = nativeSubdirectories.HasValue(); diff --git a/src/dotnet/commands/dotnet-test/DotnetTestParams.cs b/src/dotnet/commands/dotnet-test/DotnetTestParams.cs index a06c1ab56..d46649cae 100644 --- a/src/dotnet/commands/dotnet-test/DotnetTestParams.cs +++ b/src/dotnet/commands/dotnet-test/DotnetTestParams.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using Microsoft.DotNet.Cli.CommandLine; using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Tools.Common; using NuGet.Frameworks; using static System.Int32; @@ -103,7 +104,7 @@ namespace Microsoft.DotNet.Tools.Test } Output = _outputOption.Value(); - BuildBasePath = _buildBasePath.Value(); + BuildBasePath = PathUtility.GetFullPath(_buildBasePath.Value()); Config = _configurationOption.Value() ?? Constants.DefaultConfiguration; Runtime = _runtimeOption.Value(); NoBuild = _noBuildOption.HasValue(); From d52ea0793462ac8e264de0f06092a1330f99e29e Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Mon, 2 May 2016 14:39:09 -0500 Subject: [PATCH 3/3] Fixing dotnet-compile and dotnet-test unit tests. Also, fixing a potential NullRef in ProjectContext. The compile unit test needed to be updated to mock out a new call to ICommand.WorkingDirectory. The test unit test needed to account for build-base-path getting fully qualified. --- src/Microsoft.DotNet.ProjectModel/ProjectContext.cs | 2 +- .../GivenThatICareAboutScriptVariablesFromAManagedCompiler.cs | 1 + .../GivenThatWeWantToParseArgumentsForDotnetTest.cs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs b/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs index cf7ba5a64..25293755b 100644 --- a/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs +++ b/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs @@ -36,7 +36,7 @@ namespace Microsoft.DotNet.ProjectModel public string RootDirectory => GlobalSettings?.DirectoryPath; - public string ProjectDirectory => ProjectFile.ProjectDirectory; + public string ProjectDirectory => ProjectFile?.ProjectDirectory; public string PackagesDirectory { get; } diff --git a/test/dotnet-compile.UnitTests/GivenThatICareAboutScriptVariablesFromAManagedCompiler.cs b/test/dotnet-compile.UnitTests/GivenThatICareAboutScriptVariablesFromAManagedCompiler.cs index bd0a9090a..829f4a928 100644 --- a/test/dotnet-compile.UnitTests/GivenThatICareAboutScriptVariablesFromAManagedCompiler.cs +++ b/test/dotnet-compile.UnitTests/GivenThatICareAboutScriptVariablesFromAManagedCompiler.cs @@ -176,6 +176,7 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests var projectJson = Path.Combine(TestAssetPath, "project.json"); var command = new Mock(); command.Setup(c => c.Execute()).Returns(new CommandResult()); + command.Setup(c => c.WorkingDirectory(It.IsAny())).Returns(() => command.Object); command.Setup(c => c.OnErrorLine(It.IsAny>())).Returns(() => command.Object); command.Setup(c => c.OnOutputLine(It.IsAny>())).Returns(() => command.Object); var commandFactory = new Mock(); diff --git a/test/dotnet-test.UnitTests/GivenThatWeWantToParseArgumentsForDotnetTest.cs b/test/dotnet-test.UnitTests/GivenThatWeWantToParseArgumentsForDotnetTest.cs index 263db144c..f440b618e 100644 --- a/test/dotnet-test.UnitTests/GivenThatWeWantToParseArgumentsForDotnetTest.cs +++ b/test/dotnet-test.UnitTests/GivenThatWeWantToParseArgumentsForDotnetTest.cs @@ -141,7 +141,7 @@ namespace Microsoft.Dotnet.Tools.Test.Tests [Fact] public void It_sets_BuildBasePath_when_one_is_passed_in() { - _dotnetTestFullParams.BuildBasePath.Should().Be(BuildBasePath); + _dotnetTestFullParams.BuildBasePath.Should().Be(Path.GetFullPath(BuildBasePath)); } [Fact]