Merge pull request #487 from brthor/project_validator
Multi Project Validation
This commit is contained in:
commit
3fd3daa51a
14 changed files with 538 additions and 1 deletions
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 14
|
# Visual Studio 14
|
||||||
VisualStudioVersion = 14.0.24720.0
|
VisualStudioVersion = 14.0.23107.0
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Cli", "src\Microsoft.DotNet.Cli\Microsoft.DotNet.Cli.xproj", "{60CF7E6C-D6C8-439D-B7B7-D8A27E29BE2C}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Cli", "src\Microsoft.DotNet.Cli\Microsoft.DotNet.Cli.xproj", "{60CF7E6C-D6C8-439D-B7B7-D8A27E29BE2C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
@ -57,6 +57,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "LoadContextTest", "test\Loa
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Tools.New", "src\Microsoft.DotNet.Tools.New\Microsoft.DotNet.Tools.New.xproj", "{BC765FBF-AD7A-4A99-9902-5540C5A74181}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Tools.New", "src\Microsoft.DotNet.Tools.New\Microsoft.DotNet.Tools.New.xproj", "{BC765FBF-AD7A-4A99-9902-5540C5A74181}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{0722D325-24C8-4E83-B5AF-0A083E7F0749}"
|
||||||
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MultiProjectValidator", "tools\MultiProjectValidator\MultiProjectValidator.xproj", "{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -421,6 +425,22 @@ Global
|
||||||
{BC765FBF-AD7A-4A99-9902-5540C5A74181}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
|
{BC765FBF-AD7A-4A99-9902-5540C5A74181}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
|
||||||
{BC765FBF-AD7A-4A99-9902-5540C5A74181}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
|
{BC765FBF-AD7A-4A99-9902-5540C5A74181}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
|
||||||
{BC765FBF-AD7A-4A99-9902-5540C5A74181}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
|
{BC765FBF-AD7A-4A99-9902-5540C5A74181}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.MinSizeRel|x64.Build.0 = Debug|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -448,5 +468,6 @@ Global
|
||||||
{947DD232-8D9B-4B78-9C6A-94F807D2DD58} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
|
{947DD232-8D9B-4B78-9C6A-94F807D2DD58} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
|
||||||
{7A75ACC4-3C2F-44E1-B492-0EC08704E9FF} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
|
{7A75ACC4-3C2F-44E1-B492-0EC08704E9FF} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
|
||||||
{BC765FBF-AD7A-4A99-9902-5540C5A74181} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
|
{BC765FBF-AD7A-4A99-9902-5540C5A74181} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
|
||||||
|
{08A68C6A-86F6-4ED2-89A7-B166D33E9F85} = {0722D325-24C8-4E83-B5AF-0A083E7F0749}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
@ -146,6 +146,15 @@ Download it from https://www.cmake.org
|
||||||
Exit 1
|
Exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Run Validation for Project.json dependencies
|
||||||
|
dotnet publish $RepoRoot\tools\MultiProjectValidator -o $Stage2Dir\..\tools
|
||||||
|
& "$Stage2Dir\..\tools\pjvalidate" "$RepoRoot\src"
|
||||||
|
# TODO For release builds, this should be uncommented and fail.
|
||||||
|
# if (!$?) {
|
||||||
|
# Write-Host "Project Validation Failed"
|
||||||
|
# Exit 1
|
||||||
|
# }
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
$env:PATH = $StartPath
|
$env:PATH = $StartPath
|
||||||
$env:DOTNET_HOME = $StartDotNetHome
|
$env:DOTNET_HOME = $StartDotNetHome
|
||||||
|
|
|
@ -118,3 +118,10 @@ DOTNET_HOME=$STAGE2_DIR DOTNET_TOOLS=$STAGE2_DIR $DIR/test/smoke-test.sh
|
||||||
# E2E test on the output
|
# E2E test on the output
|
||||||
header "Testing stage2 End to End ..."
|
header "Testing stage2 End to End ..."
|
||||||
DOTNET_HOME=$STAGE2_DIR DOTNET_TOOLS=$STAGE2_DIR $DIR/test/e2e-test.sh
|
DOTNET_HOME=$STAGE2_DIR DOTNET_TOOLS=$STAGE2_DIR $DIR/test/e2e-test.sh
|
||||||
|
|
||||||
|
# Run Validation for Project.json dependencies
|
||||||
|
dotnet publish "$REPOROOT/tools/MultiProjectValidator" -o "$STAGE2_DIR/../tools"
|
||||||
|
#TODO for release builds this should fail
|
||||||
|
set +e
|
||||||
|
"$STAGE2_DIR/../tools/pjvalidate" "$REPOROOT/src"
|
||||||
|
set -e
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace Microsoft.DotNet.Cli.Utils
|
||||||
{
|
{
|
||||||
internal static class Constants
|
internal static class Constants
|
||||||
{
|
{
|
||||||
|
public static readonly string ProjectFileName = "project.json";
|
||||||
public static readonly string ExeSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty;
|
public static readonly string ExeSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty;
|
||||||
|
|
||||||
// Priority order of runnable suffixes to look for and run
|
// Priority order of runnable suffixes to look for and run
|
||||||
|
|
33
tools/MultiProjectValidator/AnalysisResult.cs
Normal file
33
tools/MultiProjectValidator/AnalysisResult.cs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MultiProjectValidator
|
||||||
|
{
|
||||||
|
public class AnalysisResult
|
||||||
|
{
|
||||||
|
|
||||||
|
private List<string> _messages;
|
||||||
|
private bool _passed;
|
||||||
|
|
||||||
|
public AnalysisResult(List<string> messages, bool passed)
|
||||||
|
{
|
||||||
|
_messages = messages;
|
||||||
|
_passed = passed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> Messages
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _messages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Passed
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _passed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ProjectSanity.AnalysisRules.DependencyMismatch
|
||||||
|
{
|
||||||
|
internal class DependencyGroup
|
||||||
|
{
|
||||||
|
public static DependencyGroup CreateWithEntry(DependencyInfo dependencyInfo)
|
||||||
|
{
|
||||||
|
var dependencyGroup = new DependencyGroup
|
||||||
|
{
|
||||||
|
DependencyName = dependencyInfo.Name,
|
||||||
|
VersionDependencyInfoMap = new Dictionary<string, List<DependencyInfo>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
dependencyGroup.AddEntry(dependencyInfo);
|
||||||
|
|
||||||
|
return dependencyGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DependencyName { get; private set; }
|
||||||
|
public Dictionary<string, List<DependencyInfo>> VersionDependencyInfoMap { get; private set; }
|
||||||
|
|
||||||
|
public bool HasConflict
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return VersionDependencyInfoMap.Count > 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEntry(DependencyInfo dependencyInfo)
|
||||||
|
{
|
||||||
|
if (!dependencyInfo.Name.Equals(DependencyName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
throw new Exception("Added dependency does not match group");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VersionDependencyInfoMap.ContainsKey(dependencyInfo.Version))
|
||||||
|
{
|
||||||
|
VersionDependencyInfoMap[dependencyInfo.Version].Add(dependencyInfo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VersionDependencyInfoMap[dependencyInfo.Version] = new List<DependencyInfo>()
|
||||||
|
{
|
||||||
|
dependencyInfo
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
|
||||||
|
namespace ProjectSanity.AnalysisRules.DependencyMismatch
|
||||||
|
{
|
||||||
|
internal class DependencyInfo
|
||||||
|
{
|
||||||
|
public static DependencyInfo Create(ProjectContext context, LibraryDescription library)
|
||||||
|
{
|
||||||
|
return new DependencyInfo
|
||||||
|
{
|
||||||
|
ProjectPath = context.ProjectFile.ProjectFilePath,
|
||||||
|
Version = library.Identity.Version.ToString(),
|
||||||
|
Name = library.Identity.Name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ProjectPath { get; private set; }
|
||||||
|
public string Version { get; private set; }
|
||||||
|
public string Name { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
using System.Text;
|
||||||
|
using ProjectSanity.AnalysisRules.DependencyMismatch;
|
||||||
|
|
||||||
|
namespace MultiProjectValidator.AnalysisRules
|
||||||
|
{
|
||||||
|
public class DependencyMismatchRule : IAnalysisRule
|
||||||
|
{
|
||||||
|
public AnalysisResult Evaluate(List<ProjectContext> projectContexts)
|
||||||
|
{
|
||||||
|
var targetGroupedContexts = GroupContextsByTarget(projectContexts);
|
||||||
|
|
||||||
|
var failureMessages = EvaluateProjectContextTargetGroups(targetGroupedContexts);
|
||||||
|
var pass = failureMessages.Count == 0;
|
||||||
|
|
||||||
|
var result = new AnalysisResult(failureMessages, pass);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> EvaluateProjectContextTargetGroups(Dictionary<string, List<ProjectContext>> targetGroupedProjectContexts)
|
||||||
|
{
|
||||||
|
var failureMessages = new List<string>();
|
||||||
|
|
||||||
|
foreach (var target in targetGroupedProjectContexts.Keys)
|
||||||
|
{
|
||||||
|
var targetProjectContextGroup = targetGroupedProjectContexts[target];
|
||||||
|
|
||||||
|
var groupFailureMessages = EvaluateProjectContextTargetGroup(targetProjectContextGroup);
|
||||||
|
|
||||||
|
if (groupFailureMessages.Count > 0)
|
||||||
|
{
|
||||||
|
string aggregateFailureMessage = $"Failures for Target {target} {Environment.NewLine}{Environment.NewLine}"
|
||||||
|
+ string.Join("", groupFailureMessages);
|
||||||
|
|
||||||
|
failureMessages.Add(aggregateFailureMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return failureMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> EvaluateProjectContextTargetGroup(List<ProjectContext> targetProjectContextGroup)
|
||||||
|
{
|
||||||
|
var failedMessages = new List<string>();
|
||||||
|
|
||||||
|
var dependencyGroups = CreateDependencyGroups(targetProjectContextGroup);
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var dependencyGroup in dependencyGroups)
|
||||||
|
{
|
||||||
|
if(dependencyGroup.HasConflict)
|
||||||
|
{
|
||||||
|
failedMessages.Add(GetDependencyGroupConflictMessage(dependencyGroup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return failedMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetDependencyGroupConflictMessage(DependencyGroup dependencyGroup)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.Append($"Conflict for {dependencyGroup.DependencyName} in projects:{Environment.NewLine}");
|
||||||
|
|
||||||
|
foreach (var version in dependencyGroup.VersionDependencyInfoMap.Keys)
|
||||||
|
{
|
||||||
|
var dependencyInfoList = dependencyGroup.VersionDependencyInfoMap[version];
|
||||||
|
|
||||||
|
foreach (var dependencyInfo in dependencyInfoList)
|
||||||
|
{
|
||||||
|
sb.Append($"Version: {dependencyInfo.Version} Path: {dependencyInfo.ProjectPath} {Environment.NewLine}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.Append(Environment.NewLine);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, List<ProjectContext>> GroupContextsByTarget(List<ProjectContext> projectContexts)
|
||||||
|
{
|
||||||
|
var targetContextMap = new Dictionary<string, List<ProjectContext>>();
|
||||||
|
foreach (var context in projectContexts)
|
||||||
|
{
|
||||||
|
var target = context.TargetFramework + context.RuntimeIdentifier;
|
||||||
|
|
||||||
|
if (targetContextMap.ContainsKey(target))
|
||||||
|
{
|
||||||
|
targetContextMap[target].Add(context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetContextMap[target] = new List<ProjectContext>()
|
||||||
|
{
|
||||||
|
context
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetContextMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DependencyGroup> CreateDependencyGroups(List<ProjectContext> projectContexts)
|
||||||
|
{
|
||||||
|
var libraryNameDependencyGroupMap = new Dictionary<string, DependencyGroup>();
|
||||||
|
|
||||||
|
foreach (var projectContext in projectContexts)
|
||||||
|
{
|
||||||
|
var libraries = projectContext.LibraryManager.GetLibraries();
|
||||||
|
|
||||||
|
foreach (var library in libraries)
|
||||||
|
{
|
||||||
|
var dependencyInfo = DependencyInfo.Create(projectContext, library);
|
||||||
|
|
||||||
|
if (libraryNameDependencyGroupMap.ContainsKey(dependencyInfo.Name))
|
||||||
|
{
|
||||||
|
var dependencyGroup = libraryNameDependencyGroupMap[dependencyInfo.Name];
|
||||||
|
|
||||||
|
dependencyGroup.AddEntry(dependencyInfo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var dependencyGroup = DependencyGroup.CreateWithEntry(dependencyInfo);
|
||||||
|
|
||||||
|
libraryNameDependencyGroupMap[dependencyInfo.Name] = dependencyGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return libraryNameDependencyGroupMap.Values.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
10
tools/MultiProjectValidator/IAnalysisRule.cs
Normal file
10
tools/MultiProjectValidator/IAnalysisRule.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
|
||||||
|
namespace MultiProjectValidator
|
||||||
|
{
|
||||||
|
public interface IAnalysisRule
|
||||||
|
{
|
||||||
|
AnalysisResult Evaluate(List<ProjectContext> projectContexts);
|
||||||
|
}
|
||||||
|
}
|
18
tools/MultiProjectValidator/MultiProjectValidator.xproj
Normal file
18
tools/MultiProjectValidator/MultiProjectValidator.xproj
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>08a68c6a-86f6-4ed2-89a7-b166d33e9f85</ProjectGuid>
|
||||||
|
<RootNamespace>ProjectSanity</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\tools\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
126
tools/MultiProjectValidator/Program.cs
Normal file
126
tools/MultiProjectValidator/Program.cs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace MultiProjectValidator
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static int Main(string[] args)
|
||||||
|
{
|
||||||
|
|
||||||
|
string rootPath = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rootPath = ParseAndValidateArgs(args);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ProjectContext> projects = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
projects = ProjectLoader.Load(rootPath);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to load projects from path: " + rootPath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var analyzer = ProjectAnalyzer.Create(projects);
|
||||||
|
var analysisResults = analyzer.DoAnalysis();
|
||||||
|
var failed = analysisResults.Where((a) => !a.Passed).Any();
|
||||||
|
|
||||||
|
PrintAnalysisResults(analysisResults);
|
||||||
|
|
||||||
|
return failed ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintAnalysisResults(List<AnalysisResult> analysisResults)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Project Validation Results");
|
||||||
|
|
||||||
|
var failedCount = analysisResults.Where((a) => !a.Passed).Count();
|
||||||
|
var passedCount = analysisResults.Where((a) => a.Passed).Count();
|
||||||
|
|
||||||
|
Console.ForegroundColor = ConsoleColor.Green;
|
||||||
|
Console.WriteLine($"{passedCount} Successful Analysis Rules");
|
||||||
|
|
||||||
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||||
|
Console.WriteLine($"{failedCount} Failed Analysis Rules");
|
||||||
|
|
||||||
|
if (failedCount != 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failure Messages");
|
||||||
|
|
||||||
|
foreach(var result in analysisResults)
|
||||||
|
{
|
||||||
|
if (!result.Passed)
|
||||||
|
{
|
||||||
|
foreach(var message in result.Messages)
|
||||||
|
{
|
||||||
|
Console.WriteLine(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.ForegroundColor = ConsoleColor.White;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool AnyAnalysisFailed(List<AnalysisResult> analysisResults)
|
||||||
|
{
|
||||||
|
foreach (var result in analysisResults)
|
||||||
|
{
|
||||||
|
if (!result.Passed)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ParseAndValidateArgs(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
{
|
||||||
|
PrintHelp();
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
string rootPath = args[0];
|
||||||
|
|
||||||
|
if (!Directory.Exists(rootPath))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Root Directory does not exist: " + rootPath);
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PrintHelp()
|
||||||
|
{
|
||||||
|
var help = @"
|
||||||
|
Multi-Project Validator
|
||||||
|
|
||||||
|
Description:
|
||||||
|
This tool recursively searches for project.json's from the given root path.
|
||||||
|
It then applies a set of analysis rules, determines whether they pass/fail
|
||||||
|
and then sets exit code to reflect.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
Ensure all analyzed project.json have been restored prior to running this tool.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
pjvalidate [root path of recursive search]";
|
||||||
|
|
||||||
|
Console.WriteLine(help);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
tools/MultiProjectValidator/ProjectAnalyzer.cs
Normal file
43
tools/MultiProjectValidator/ProjectAnalyzer.cs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
using MultiProjectValidator.AnalysisRules;
|
||||||
|
|
||||||
|
namespace MultiProjectValidator
|
||||||
|
{
|
||||||
|
public class ProjectAnalyzer
|
||||||
|
{
|
||||||
|
|
||||||
|
public static ProjectAnalyzer Create(List<ProjectContext> projectContexts)
|
||||||
|
{
|
||||||
|
// Any Additional rules would be added here
|
||||||
|
var rules = new List<IAnalysisRule>
|
||||||
|
{
|
||||||
|
new DependencyMismatchRule()
|
||||||
|
};
|
||||||
|
|
||||||
|
return new ProjectAnalyzer(rules, projectContexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProjectContext> _projectContexts;
|
||||||
|
private List<IAnalysisRule> _rules;
|
||||||
|
|
||||||
|
private ProjectAnalyzer(List<IAnalysisRule> rules, List<ProjectContext> projectContexts)
|
||||||
|
{
|
||||||
|
_rules = rules;
|
||||||
|
_projectContexts = projectContexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AnalysisResult> DoAnalysis()
|
||||||
|
{
|
||||||
|
var results = new List<AnalysisResult>();
|
||||||
|
|
||||||
|
foreach(var rule in _rules)
|
||||||
|
{
|
||||||
|
results.Add(rule.Evaluate(_projectContexts));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
38
tools/MultiProjectValidator/ProjectLoader.cs
Normal file
38
tools/MultiProjectValidator/ProjectLoader.cs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
|
||||||
|
namespace MultiProjectValidator
|
||||||
|
{
|
||||||
|
public class ProjectLoader
|
||||||
|
{
|
||||||
|
private static readonly string PROJECT_FILENAME = "project.json";
|
||||||
|
|
||||||
|
public static List<ProjectContext> Load(string rootPath, bool recursive=true)
|
||||||
|
{
|
||||||
|
var projectFiles = DiscoverProjectFiles(rootPath);
|
||||||
|
var projectContextList = LoadProjectContexts(projectFiles);
|
||||||
|
|
||||||
|
return projectContextList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string[] DiscoverProjectFiles(string rootPath)
|
||||||
|
{
|
||||||
|
return Directory.GetFiles(rootPath, PROJECT_FILENAME, SearchOption.AllDirectories);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<ProjectContext> LoadProjectContexts(string[] projectFiles)
|
||||||
|
{
|
||||||
|
var projectContexts = new List<ProjectContext>();
|
||||||
|
|
||||||
|
foreach (var file in projectFiles)
|
||||||
|
{
|
||||||
|
var fileTargetContexts = ProjectContext.CreateContextForEachTarget(file);
|
||||||
|
|
||||||
|
projectContexts.AddRange(fileTargetContexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectContexts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
tools/MultiProjectValidator/project.json
Normal file
17
tools/MultiProjectValidator/project.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"name": "pjvalidate",
|
||||||
|
"compilationOptions": {
|
||||||
|
"emitEntryPoint": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.0.0-rc2-23614",
|
||||||
|
"Microsoft.DotNet.ProjectModel": "1.0.0-*",
|
||||||
|
"Microsoft.DotNet.Cli.Utils": "1.0.0-*",
|
||||||
|
},
|
||||||
|
|
||||||
|
"frameworks": {
|
||||||
|
"dnxcore50": { }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue