Migrate P2P references
- Given a project migrate all the project references recursively. - Add a boolean option '--skip-project-references' to skip migrating P2P references. By default P2P references are migrated. - Remove the '--output' since it is not used heavily and makes migrating multiple projects easier. - Add tests
This commit is contained in:
parent
13d18f198e
commit
46ab621422
21 changed files with 362 additions and 12 deletions
|
@ -0,0 +1,17 @@
|
|||
// 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.Diagnostics;
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("This string came from ProjectA");
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"buildOptions": {
|
||||
"emitEntryPoint": true,
|
||||
"preserveCompilationContext": true
|
||||
},
|
||||
"dependencies": {
|
||||
"ProjectB": {
|
||||
"target": "project",
|
||||
"version": "1.0.0-*"
|
||||
},
|
||||
"ProjectC": {
|
||||
"target": "project",
|
||||
"version": "1.0.0-*"
|
||||
},
|
||||
"Microsoft.NETCore.App": "1.0.1"
|
||||
},
|
||||
"frameworks": {
|
||||
"netcoreapp1.0": {}
|
||||
},
|
||||
"runtimes": {
|
||||
"win7-x64": {},
|
||||
"win7-x86": {},
|
||||
"osx.10.10-x64": {},
|
||||
"osx.10.11-x64": {},
|
||||
"ubuntu.14.04-x64": {},
|
||||
"ubuntu.16.04-x64": {},
|
||||
"centos.7-x64": {},
|
||||
"rhel.7.2-x64": {},
|
||||
"debian.8-x64": {},
|
||||
"fedora.23-x64": {},
|
||||
"opensuse.13.2-x64": {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// 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;
|
||||
|
||||
namespace TestLibrary
|
||||
{
|
||||
public static class ProjectB
|
||||
{
|
||||
public static string GetMessage()
|
||||
{
|
||||
return "This string came from ProjectB";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"buildOptions": {
|
||||
"nowarn": [
|
||||
"CS1591"
|
||||
],
|
||||
"xmlDoc": true,
|
||||
"additionalArguments": [
|
||||
"-highentropyva+"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"ProjectC": {
|
||||
"target": "project",
|
||||
"version": "1.0.0-*"
|
||||
},
|
||||
"ProjectD": {
|
||||
"target": "project",
|
||||
"version": "1.0.0-*"
|
||||
},
|
||||
"NETStandard.Library": "1.6.0"
|
||||
},
|
||||
"frameworks": {
|
||||
"netstandard1.5": {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// 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;
|
||||
|
||||
namespace TestLibrary
|
||||
{
|
||||
public static class ProjectC
|
||||
{
|
||||
public static string GetMessage()
|
||||
{
|
||||
return "This string came from ProjectC";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"buildOptions": {
|
||||
"nowarn": [
|
||||
"CS1591"
|
||||
],
|
||||
"xmlDoc": true,
|
||||
"additionalArguments": [
|
||||
"-highentropyva+"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"ProjectD": {
|
||||
"target": "project",
|
||||
"version": "1.0.0-*"
|
||||
},
|
||||
"ProjectE": {
|
||||
"target": "project",
|
||||
"version": "1.0.0-*"
|
||||
},
|
||||
"NETStandard.Library": "1.6.0"
|
||||
},
|
||||
"frameworks": {
|
||||
"netstandard1.5": {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// 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;
|
||||
|
||||
namespace TestLibrary
|
||||
{
|
||||
public static class ProjectD
|
||||
{
|
||||
public static string GetMessage()
|
||||
{
|
||||
return "This string came from ProjectD";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"buildOptions": {
|
||||
"nowarn": [
|
||||
"CS1591"
|
||||
],
|
||||
"xmlDoc": true,
|
||||
"additionalArguments": [
|
||||
"-highentropyva+"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "1.6.0"
|
||||
},
|
||||
"frameworks": {
|
||||
"netstandard1.5": {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// 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;
|
||||
|
||||
namespace TestLibrary
|
||||
{
|
||||
public static class ProjectE
|
||||
{
|
||||
public static string GetMessage()
|
||||
{
|
||||
return "This string came from ProjectE";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"buildOptions": {
|
||||
"nowarn": [
|
||||
"CS1591"
|
||||
],
|
||||
"xmlDoc": true,
|
||||
"additionalArguments": [
|
||||
"-highentropyva+"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "1.6.0"
|
||||
},
|
||||
"frameworks": {
|
||||
"netstandard1.5": {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectJsonMigration
|
||||
{
|
||||
public class ProjectDependencyComparer : IEqualityComparer<ProjectDependency>
|
||||
{
|
||||
public bool Equals(ProjectDependency one, ProjectDependency two)
|
||||
{
|
||||
return StringComparer.OrdinalIgnoreCase
|
||||
.Equals(one.ProjectFilePath, two.ProjectFilePath);
|
||||
}
|
||||
|
||||
public int GetHashCode(ProjectDependency item)
|
||||
{
|
||||
return StringComparer.OrdinalIgnoreCase
|
||||
.GetHashCode(item.ProjectFilePath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,17 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
|||
{
|
||||
public class ProjectDependencyFinder
|
||||
{
|
||||
public IEnumerable<ProjectDependency> ResolveProjectDependencies(IEnumerable<ProjectContext> projectContexts)
|
||||
{
|
||||
foreach(var projectContext in projectContexts)
|
||||
{
|
||||
foreach(var projectDependency in ResolveProjectDependencies(projectContext))
|
||||
{
|
||||
yield return projectDependency;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ProjectDependency> ResolveProjectDependencies(ProjectContext projectContext, HashSet<string> preResolvedProjects=null)
|
||||
{
|
||||
preResolvedProjects = preResolvedProjects ?? new HashSet<string>();
|
||||
|
@ -54,6 +65,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
|||
|
||||
var projectSearchPaths = new List<string>();
|
||||
projectSearchPaths.Add(projectDirectory);
|
||||
projectSearchPaths.Add(Path.GetDirectoryName(projectDirectory));
|
||||
|
||||
var globalPaths = GetGlobalPaths(projectDirectory);
|
||||
projectSearchPaths = projectSearchPaths.Union(globalPaths).ToList();
|
||||
|
|
|
@ -5,6 +5,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.Build.Construction;
|
||||
using Microsoft.DotNet.ProjectModel;
|
||||
using Microsoft.DotNet.ProjectModel.Graph;
|
||||
using Microsoft.DotNet.Cli;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using Microsoft.DotNet.ProjectJsonMigration.Rules;
|
||||
|
@ -22,6 +24,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
|||
// - Migrating Deprecated project.jsons
|
||||
|
||||
private readonly IMigrationRule _ruleSet;
|
||||
private readonly ProjectDependencyFinder _projectDependencyFinder = new ProjectDependencyFinder();
|
||||
|
||||
public ProjectMigrator() : this(new DefaultMigrationRuleSet()) { }
|
||||
|
||||
|
@ -30,9 +33,73 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
|||
_ruleSet = ruleSet;
|
||||
}
|
||||
|
||||
public void Migrate(MigrationSettings migrationSettings)
|
||||
public void Migrate(MigrationSettings rootSettings, bool skipProjectReferences = false)
|
||||
{
|
||||
if (rootSettings == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
MigrateHelper(rootSettings);
|
||||
|
||||
if (skipProjectReferences)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var projectDependencies = ResolveTransitiveClosureProjectDependencies(rootSettings.ProjectDirectory);
|
||||
|
||||
foreach(var project in projectDependencies)
|
||||
{
|
||||
var projectDir = Path.GetDirectoryName(project.ProjectFilePath);
|
||||
var settings = new MigrationSettings(projectDir,
|
||||
projectDir,
|
||||
rootSettings.SdkPackageVersion,
|
||||
rootSettings.MSBuildProjectTemplate);
|
||||
MigrateHelper(settings);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<ProjectDependency> ResolveTransitiveClosureProjectDependencies(string rootProject)
|
||||
{
|
||||
HashSet<ProjectDependency> projectsMap = new HashSet<ProjectDependency>(new ProjectDependencyComparer());
|
||||
var projectContexts = ProjectContext.CreateContextForEachFramework(rootProject);
|
||||
var projectDependencies = _projectDependencyFinder.ResolveProjectDependencies(projectContexts);
|
||||
Queue<ProjectDependency> projectsQueue = new Queue<ProjectDependency>(projectDependencies);
|
||||
|
||||
while(projectsQueue.Count() != 0)
|
||||
{
|
||||
var projectDependency = projectsQueue.Dequeue();
|
||||
|
||||
if (projectsMap.Contains(projectDependency))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
projectsMap.Add(projectDependency);
|
||||
|
||||
projectContexts = ProjectContext.CreateContextForEachFramework(projectDependency.ProjectFilePath);
|
||||
projectDependencies = _projectDependencyFinder.ResolveProjectDependencies(projectContexts);
|
||||
|
||||
foreach(var project in projectDependencies)
|
||||
{
|
||||
projectsQueue.Enqueue(project);
|
||||
}
|
||||
}
|
||||
|
||||
return projectsMap;
|
||||
}
|
||||
|
||||
private void MigrateHelper(MigrationSettings migrationSettings)
|
||||
{
|
||||
var migrationRuleInputs = ComputeMigrationRuleInputs(migrationSettings);
|
||||
|
||||
if (IsMigrated(migrationSettings, migrationRuleInputs))
|
||||
{
|
||||
// TODO : Adding user-visible logging
|
||||
return;
|
||||
}
|
||||
|
||||
VerifyInputs(migrationRuleInputs, migrationSettings);
|
||||
|
||||
SetupOutputDirectory(migrationSettings.ProjectDirectory, migrationSettings.OutputDirectory);
|
||||
|
@ -146,5 +213,14 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
|||
}
|
||||
}
|
||||
|
||||
public bool IsMigrated(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
|
||||
{
|
||||
var outputName = Path.GetFileNameWithoutExtension(
|
||||
migrationRuleInputs.DefaultProjectContext.GetOutputPaths("_").CompilationFiles.Assembly);
|
||||
|
||||
var outputProject = Path.Combine(migrationSettings.OutputDirectory, outputName + ".csproj");
|
||||
return File.Exists(outputProject);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,21 +13,20 @@ namespace Microsoft.DotNet.Tools.Migrate
|
|||
public partial class MigrateCommand
|
||||
{
|
||||
private readonly string _templateFile;
|
||||
private readonly string _outputDirectory;
|
||||
private readonly string _projectJson;
|
||||
private readonly string _sdkVersion;
|
||||
private readonly string _xprojFilePath;
|
||||
private readonly bool _skipProjectReferences;
|
||||
|
||||
private readonly TemporaryDotnetNewTemplateProject _temporaryDotnetNewProject;
|
||||
|
||||
public MigrateCommand(string templateFile, string outputDirectory, string projectJson, string sdkVersion, string xprojFilePath)
|
||||
public MigrateCommand(string templateFile, string projectJson, string sdkVersion, string xprojFilePath, bool skipProjectReferences)
|
||||
{
|
||||
_templateFile = templateFile;
|
||||
_outputDirectory = outputDirectory;
|
||||
_projectJson = projectJson;
|
||||
_sdkVersion = sdkVersion;
|
||||
_xprojFilePath = xprojFilePath;
|
||||
|
||||
_skipProjectReferences = skipProjectReferences;
|
||||
_temporaryDotnetNewProject = new TemporaryDotnetNewTemplateProject();
|
||||
}
|
||||
|
||||
|
@ -40,14 +39,13 @@ namespace Microsoft.DotNet.Tools.Migrate
|
|||
var msBuildTemplate = _templateFile != null ?
|
||||
ProjectRootElement.TryOpen(_templateFile) : _temporaryDotnetNewProject.MSBuildProject;
|
||||
|
||||
var outputDirectory = _outputDirectory ?? projectDirectory;
|
||||
EnsureNotNull(outputDirectory, "Null output directory");
|
||||
var outputDirectory = projectDirectory;
|
||||
|
||||
var sdkVersion = _sdkVersion ?? new ProjectJsonParser(_temporaryDotnetNewProject.ProjectJson).SdkPackageVersion;
|
||||
EnsureNotNull(sdkVersion, "Null Sdk Version");
|
||||
|
||||
var migrationSettings = new MigrationSettings(projectDirectory, outputDirectory, sdkVersion, msBuildTemplate, _xprojFilePath);
|
||||
new ProjectMigrator().Migrate(migrationSettings);
|
||||
new ProjectMigrator().Migrate(migrationSettings, _skipProjectReferences);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,19 +21,19 @@ namespace Microsoft.DotNet.Tools.Migrate
|
|||
app.HelpOption("-h|--help");
|
||||
|
||||
CommandOption template = app.Option("-t|--template-file", "Base MSBuild template to use for migrated app. The default is the project included in dotnet new -t msbuild", CommandOptionType.SingleValue);
|
||||
CommandOption output = app.Option("-o|--output", "Directory to output migrated project to. The default is the project directory", CommandOptionType.SingleValue);
|
||||
CommandOption project = app.Option("-p|--project", "The path to the project to run (defaults to the current directory). Can be a path to a project.json or a project directory", CommandOptionType.SingleValue);
|
||||
CommandOption sdkVersion = app.Option("-v|--sdk-package-version", "The version of the sdk package that will be referenced in the migrated app. The default is the version of the sdk in dotnet new -t msbuild", CommandOptionType.SingleValue);
|
||||
CommandOption xprojFile = app.Option("-x|--xproj-file", "The path to the xproj file to use. Required when there is more than one xproj in a project directory.", CommandOptionType.SingleValue);
|
||||
CommandOption skipProjectReferences = app.Option("-s|--skip-project-references", "Skip migrating project references. By default project references are migrated recursively", CommandOptionType.BoolValue);
|
||||
|
||||
app.OnExecute(() =>
|
||||
{
|
||||
MigrateCommand migrateCommand = new MigrateCommand(
|
||||
template.Value(),
|
||||
output.Value(),
|
||||
template.Value(),
|
||||
project.Value(),
|
||||
sdkVersion.Value(),
|
||||
xprojFile.Value());
|
||||
xprojFile.Value(),
|
||||
skipProjectReferences.BoolValue.HasValue ? skipProjectReferences.BoolValue.Value : false);
|
||||
|
||||
return migrateCommand.Execute();
|
||||
});
|
||||
|
|
|
@ -113,6 +113,48 @@ namespace Microsoft.DotNet.Migration.Tests
|
|||
outputsIdentical.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ProjectA", "ProjectA,ProjectB,ProjectC,ProjectD,ProjectE")]
|
||||
[InlineData("ProjectB", "ProjectB,ProjectC,ProjectD,ProjectE")]
|
||||
[InlineData("ProjectC", "ProjectC,ProjectD,ProjectE")]
|
||||
[InlineData("ProjectD", "ProjectD")]
|
||||
[InlineData("ProjectE", "ProjectE")]
|
||||
public void It_migrates_root_project_and_references(string projectName, string expectedProjects)
|
||||
{
|
||||
var projectDirectory =
|
||||
TestAssetsManager.CreateTestInstance("TestAppDependencyGraph", callingMethod: $"{projectName}.RefsTest").Path;
|
||||
|
||||
MigrateProject(Path.Combine(projectDirectory, projectName));
|
||||
|
||||
string[] migratedProjects = expectedProjects.Split(new char[] { ',' });
|
||||
foreach(var migratedProject in migratedProjects)
|
||||
{
|
||||
var dirInfo = new DirectoryInfo(Path.Combine(projectDirectory, migratedProject));
|
||||
var csproj = $"{migratedProject}.csproj";
|
||||
dirInfo.Should().HaveFile(csproj);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ProjectA")]
|
||||
[InlineData("ProjectB")]
|
||||
[InlineData("ProjectC")]
|
||||
[InlineData("ProjectD")]
|
||||
[InlineData("ProjectE")]
|
||||
public void It_migrates_root_project_and_skips_references(string projectName)
|
||||
{
|
||||
var projectDirectory =
|
||||
TestAssetsManager.CreateTestInstance("TestAppDependencyGraph", callingMethod: $"{projectName}.SkipRefsTest").Path;
|
||||
|
||||
MigrateCommand.Run(new [] { "-p", Path.Combine(projectDirectory, projectName), "--skip-project-references" }).Should().Be(0);
|
||||
|
||||
var migratedProjects = Directory.EnumerateFiles(projectDirectory, "*.csproj", SearchOption.AllDirectories);
|
||||
migratedProjects.Count().Should().Be(1, "Only the root project must be migrated");
|
||||
|
||||
var migratedProject = Path.GetFileName(migratedProjects.First());
|
||||
migratedProject.Should().Be($"{projectName}.csproj");
|
||||
}
|
||||
|
||||
private MigratedBuildComparisonData GetDotnetNewComparisonData(string projectDirectory, string dotnetNewType)
|
||||
{
|
||||
DotnetNew(projectDirectory, dotnetNewType);
|
||||
|
|
Loading…
Reference in a new issue