Skip to content
This repository has been archived by the owner on Oct 4, 2021. It is now read-only.

Commit

Permalink
[Ide] Consider MSBuild item conditions in Solution pad
Browse files Browse the repository at this point in the history
Creating an ASP.NET Core project when .NET Core 3.1 SDK was installed
would result in .json files being displayed twice in the Solution pad.
.NET Core 3.1 SDK defines .json files twice.

    <Content Include="**\*.json" ... Condition="'$(ExcludeConfigFilesFromBuildOutput)'!='true'" />

    <Content Include="**\*.json" ... Condition="'$(ExcludeConfigFilesFromBuildOutput)'=='true'" />

Older .NET Core SDKs did not define the Content items more than once.
The Condition was not considered when showing files in the Solution
pad.

To support conditional files the Solution pad asks the project for
its visible files. The project uses the MSBuildEvaluationContext
to evaluate the condition to see if the file is visible or not.

Note that conditions on parent ItemGroups are currently not taken into
account. Also that visible files are not updated if the active config
is changed.

Fixes VSTS #1005277 Create ASP.NET Core project, open Properties
folder, there are two launchSettings.json files.
  • Loading branch information
mrward authored and monojenkins committed Jan 3, 2020
1 parent bb0a80f commit e08e58f
Show file tree
Hide file tree
Showing 10 changed files with 391 additions and 5 deletions.
31 changes: 31 additions & 0 deletions main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
using MonoDevelop.Projects;
using System.Threading.Tasks;
using MonoDevelop.Projects.MSBuild;
using MonoDevelop.Projects.MSBuild.Conditions;
using System.Xml;
using MonoDevelop.Core.Instrumentation;
using MonoDevelop.Core.Assemblies;
Expand Down Expand Up @@ -1216,6 +1217,36 @@ public bool IsFileInProject (string fileName)
return files.GetFile (fileName) != null;
}

/// <summary>
/// Return non-hidden files based on the configuration.
/// </summary>
/// <param name="configuration">Configuration.</param>
/// <returns>Files that should be displayed in the Solution window for a project.</returns>
public IEnumerable<ProjectFile> GetVisibleFiles (ConfigurationSelector configuration)
{
MSBuildEvaluationContext ctx = null;

foreach (ProjectFile file in Files) {
if (!file.Visible || file.IsHidden) {
continue;
} else if (string.IsNullOrEmpty (file.Condition)) {
yield return file;
continue;
}

if (ctx == null) {
ctx = new MSBuildEvaluationContext ();
ctx.InitEvaluation (MSBuildProject);
var config = (ProjectConfiguration)GetConfiguration (configuration);
foreach (var prop in config.Properties.GetProperties ())
ctx.SetPropertyValue (prop.Name, prop.Value);
}

if (ConditionParser.ParseAndEvaluate (file.Condition, ctx))
yield return file;
}
}

/// <summary>
/// Gets a list of build actions supported by this project
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,9 @@ void GetFolderContent (Project project, string folder, out List<ProjectFile> fil
files = new List<ProjectFile> ();
folders = new List<string> ();

foreach (ProjectFile file in project.Files)
{
foreach (ProjectFile file in project.GetVisibleFiles (IdeApp.Workspace.ActiveConfiguration)) {
string dir;

if (!file.Visible || file.Flags.HasFlag (ProjectItemFlags.Hidden))
continue;

if (file.Subtype != Subtype.Directory) {
// If file depends on something other than a directory, continue
if ((file.DependsOnFile != null && file.DependsOnFile.Subtype != Subtype.Directory) || FileNestingService.HasParent (file))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// ProjectNodeBuilderTests.cs
//
// Author:
// Matt Ward <matt.ward@microsoft.com>
//
// Copyright (c) 2019 Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System.Linq;
using System.Threading.Tasks;
using MonoDevelop.Core;
using MonoDevelop.Ide.Gui.Pads.ProjectPad;
using MonoDevelop.Projects;
using NUnit.Framework;
using UnitTests;

namespace MonoDevelop.Ide.Projects
{
[TestFixture]
[RequireService (typeof (RootWorkspace))]
class ProjectNodeBuilderTests : IdeTestBase
{
[Test]
public async Task ConditionalFiles ()
{
FilePath solutionFile = Util.GetSampleProject ("ConditionalFiles", "ConditionalFiles.sln");

using (var solution = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solutionFile)) {
var p = solution.GetAllProjects ().OfType<DotNetProject> ().Single ();

// Debug configuration.
IdeApp.Workspace.ActiveConfigurationId = "Debug";

var treeBuilder = new TestTreeBuilder ();
treeBuilder.ParentDataItem [typeof (Project)] = p;

var nodeBuilder = new ProjectNodeBuilder ();
nodeBuilder.BuildChildNodes (treeBuilder, p);

var debugFiles = treeBuilder.ChildNodes.OfType<ProjectFile> ().ToList ();
var debugFileNames = debugFiles.Select (f => f.FilePath.FileName).ToList ();
Assert.That (debugFileNames, Has.Member ("MyClass.cs"));
Assert.That (debugFileNames, Has.Member ("MyClass-Debug.cs"));
Assert.That (debugFileNames, Has.No.Member ("MyClass-Release.cs"));
Assert.AreEqual (2, debugFiles.Count);

// Release configuration.
IdeApp.Workspace.ActiveConfigurationId = "Release";

treeBuilder.ChildNodes.Clear ();
nodeBuilder.BuildChildNodes (treeBuilder, p);
var releaseFiles = treeBuilder.ChildNodes.OfType<ProjectFile> ().ToList ();
var releaseFileNames = releaseFiles.Select (f => f.FilePath.FileName).ToList ();
Assert.That (releaseFileNames, Has.Member ("MyClass.cs"));
Assert.That (releaseFileNames, Has.Member ("MyClass-Release.cs"));
Assert.That (releaseFileNames, Has.No.Member ("MyClass-Debug.cs"));
Assert.AreEqual (2, releaseFiles.Count);
}
}
}

}
187 changes: 187 additions & 0 deletions main/tests/Ide.Tests/MonoDevelop.Ide.Projects/TestTreeBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
//
// TestTreeBuilder.cs
//
// Author:
// Matt Ward <matt.ward@microsoft.com>
//
// Copyright (c) 2019 Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.Collections;
using System.Collections.Generic;
using MonoDevelop.Ide.Gui.Components;

namespace MonoDevelop.Ide.Projects
{
class TestTreeBuilder : ITreeBuilder
{
public object DataItem { get; set; }
public string NodeName { get; set; }
public bool Selected { get; set; }
public bool Expanded { get; set; }
public ITreeOptions Options { get; }
public TypeNodeBuilder TypeNodeBuilder { get; }
public NodePosition CurrentPosition { get; }
public bool Filled { get; }

public List<object> ChildNodes = new List<object> ();

public void AddChild (object dataObject)
{
ChildNodes.Add (dataObject);
}

public void AddChild (object dataObject, bool moveToChild)
{
ChildNodes.Add (dataObject);
}

public void AddChildren (IEnumerable dataObjects)
{
foreach (object dataObject in dataObjects) {
ChildNodes.Add (dataObject);
}
}

public ITreeNavigator Clone ()
{
throw new NotImplementedException ();
}

public void ExpandToNode ()
{
}

public bool FindChild (object dataObject)
{
throw new NotImplementedException ();
}

public bool FindChild (object dataObject, bool recursive)
{
throw new NotImplementedException ();
}

public Dictionary<Type, object> ParentDataItem = new Dictionary<Type, object> ();

public object GetParentDataItem (Type type, bool includeCurrent)
{
if (ParentDataItem.TryGetValue (type, out object parent)) {
return parent;
}

return null;
}

public T GetParentDataItem<T> (bool includeCurrent)
{
throw new NotImplementedException ();
}

public bool HasChild (string name, Type dataType)
{
throw new NotImplementedException ();
}

public bool HasChildren ()
{
throw new NotImplementedException ();
}

public bool MoveNext ()
{
throw new NotImplementedException ();
}

public bool MoveToChild (string name, Type dataType)
{
throw new NotImplementedException ();
}

public bool MoveToFirstChild ()
{
throw new NotImplementedException ();
}

public bool MoveToNextObject ()
{
throw new NotImplementedException ();
}

public bool MoveToObject (object dataObject)
{
throw new NotImplementedException ();
}

public bool MoveToParent ()
{
throw new NotImplementedException ();
}

public bool MoveToParent (Type type)
{
throw new NotImplementedException ();
}

public bool MoveToPosition (NodePosition position)
{
throw new NotImplementedException ();
}

public bool MoveToRoot ()
{
throw new NotImplementedException ();
}

public void Remove ()
{
}

public void Remove (bool moveToParent)
{
}

public void RestoreState (NodeState state)
{
}

public NodeState SaveState ()
{
throw new NotImplementedException ();
}

public void ScrollToNode ()
{
}

public void Update ()
{
}

public void UpdateAll ()
{
}

public void UpdateChildren ()
{
}
}
}
2 changes: 2 additions & 0 deletions main/tests/Ide.Tests/MonoDevelop.Ide.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@
<Compile Include="MonoDevelop.Ide.Gui\GLibLoggingTests.cs" />
<Compile Include="MonoDevelop.Ide.Projects.OptionPanels\OutputOptionsPanelTests.cs" />
<Compile Include="MonoDevelop.Components.AutoTest\AppResultTests.cs" />
<Compile Include="MonoDevelop.Ide.Projects\ProjectNodeBuilderTests.cs" />
<Compile Include="MonoDevelop.Ide.Projects\TestTreeBuilder.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\core\MonoDevelop.Ide\MonoDevelop.Ide.csproj">
Expand Down
40 changes: 40 additions & 0 deletions main/tests/test-projects/ConditionalFiles/ConditionalFiles.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{200DD006-C60F-4A2F-BB12-E2496F88CA65}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>ConditionalFiles</RootNamespace>
<AssemblyName>ConditionalFiles</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<MyClassEnabled>true</MyClassEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
<MyClassDebugEnabled>true</MyClassDebugEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="MyClass-Release.cs" Condition="'$(MyClassDebugEnabled)' != 'true'" />
<Compile Include="MyClass-Debug.cs" Condition="'$(MyClassDebugEnabled)' == 'true'"/>
<Compile Include="MyClass.cs" Condition="'$(MyClassEnabled)' == 'true'"/>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
17 changes: 17 additions & 0 deletions main/tests/test-projects/ConditionalFiles/ConditionalFiles.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConditionalFiles", "ConditionalFiles.csproj", "{200DD006-C60F-4A2F-BB12-E2496F88CA65}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{200DD006-C60F-4A2F-BB12-E2496F88CA65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{200DD006-C60F-4A2F-BB12-E2496F88CA65}.Debug|Any CPU.Build.0 = Debug|Any CPU
{200DD006-C60F-4A2F-BB12-E2496F88CA65}.Release|Any CPU.ActiveCfg = Release|Any CPU
{200DD006-C60F-4A2F-BB12-E2496F88CA65}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
Loading

0 comments on commit e08e58f

Please sign in to comment.