Merge pull request #4221 from dotnet/sridhar-ms/multiple-proj-migrate
Migrate P2P references
This commit is contained in:
commit
038fb6fbae
24 changed files with 523 additions and 46 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,23 @@
|
||||||
|
<?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)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>084222f1-7909-48f4-81e8-a97398b26b1c</ProjectGuid>
|
||||||
|
<RootNamespace>ProjectC</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">bin</OutputPath>
|
||||||
|
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\ClassLibrary1\ClassLibrary1.csproj" />
|
||||||
|
<ProjectReference Include="..\..\ClassLibrary2\ClassLibrary2.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"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-*"
|
||||||
|
},
|
||||||
|
"ClassLibrary1": {
|
||||||
|
"target": "project"
|
||||||
|
},
|
||||||
|
"ClassLibrary2": {
|
||||||
|
"target": "project"
|
||||||
|
},
|
||||||
|
"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,23 @@
|
||||||
|
<?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)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>7fb8f138-ffb0-4eec-af9e-2e6ff9979593</ProjectGuid>
|
||||||
|
<RootNamespace>ProjectE</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">obj</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">bin</OutputPath>
|
||||||
|
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\ClassLibrary2\ClassLibrary2.csproj" />
|
||||||
|
<ProjectReference Include="..\..\ClassLibrary3\ClassLibrary3.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"buildOptions": {
|
||||||
|
"nowarn": [
|
||||||
|
"CS1591"
|
||||||
|
],
|
||||||
|
"xmlDoc": true,
|
||||||
|
"additionalArguments": [
|
||||||
|
"-highentropyva+"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ClassLibrary2": {
|
||||||
|
"target": "project"
|
||||||
|
},
|
||||||
|
"ClassLibrary3": {
|
||||||
|
"target": "project"
|
||||||
|
},
|
||||||
|
"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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,18 +3,45 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Build.Construction;
|
||||||
using Microsoft.DotNet.ProjectModel;
|
using Microsoft.DotNet.ProjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Microsoft.DotNet.ProjectModel.Compilation;
|
using Microsoft.DotNet.ProjectModel.Compilation;
|
||||||
using Microsoft.DotNet.ProjectModel.Graph;
|
using Microsoft.DotNet.ProjectModel.Graph;
|
||||||
|
using Microsoft.DotNet.Tools.Common;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.ProjectJsonMigration
|
namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
{
|
{
|
||||||
public class ProjectDependencyFinder
|
public class ProjectDependencyFinder
|
||||||
{
|
{
|
||||||
public IEnumerable<ProjectDependency> ResolveProjectDependencies(ProjectContext projectContext, HashSet<string> preResolvedProjects=null)
|
public IEnumerable<ProjectDependency> ResolveProjectDependencies(string projectDir, string xprojFile = null)
|
||||||
|
{
|
||||||
|
var projectContexts = ProjectContext.CreateContextForEachFramework(projectDir);
|
||||||
|
xprojFile = xprojFile ?? FindXprojFile(projectDir);
|
||||||
|
|
||||||
|
ProjectRootElement xproj = null;
|
||||||
|
if (xprojFile != null)
|
||||||
|
{
|
||||||
|
xproj = ProjectRootElement.Open(xprojFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResolveProjectDependencies(projectContexts, ResolveXProjProjectDependencyNames(xproj));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ProjectDependency> ResolveProjectDependencies(IEnumerable<ProjectContext> projectContexts, IEnumerable<string> preResolvedProjects=null)
|
||||||
|
{
|
||||||
|
foreach(var projectContext in projectContexts)
|
||||||
|
{
|
||||||
|
foreach(var projectDependency in ResolveProjectDependencies(projectContext, preResolvedProjects))
|
||||||
|
{
|
||||||
|
yield return projectDependency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ProjectDependency> ResolveProjectDependencies(ProjectContext projectContext, IEnumerable<string> preResolvedProjects=null)
|
||||||
{
|
{
|
||||||
preResolvedProjects = preResolvedProjects ?? new HashSet<string>();
|
preResolvedProjects = preResolvedProjects ?? new HashSet<string>();
|
||||||
|
|
||||||
|
@ -48,14 +75,49 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
return projectDependencies;
|
return projectDependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<string> ResolveXProjProjectDependencyNames(ProjectRootElement xproj)
|
||||||
|
{
|
||||||
|
var xprojDependencies = ResolveXProjProjectDependencies(xproj).SelectMany(r => r.Includes());
|
||||||
|
return new HashSet<string>(xprojDependencies.Select(p => Path.GetFileNameWithoutExtension(
|
||||||
|
PathUtility.GetPathWithDirectorySeparator(p))));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IEnumerable<ProjectItemElement> ResolveXProjProjectDependencies(ProjectRootElement xproj)
|
||||||
|
{
|
||||||
|
if (xproj == null)
|
||||||
|
{
|
||||||
|
MigrationTrace.Instance.WriteLine($"{nameof(ProjectDependencyFinder)}: No xproj file given.");
|
||||||
|
return Enumerable.Empty<ProjectItemElement>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return xproj.Items
|
||||||
|
.Where(i => i.ItemType == "ProjectReference")
|
||||||
|
.Where(p => p.Includes().Any(
|
||||||
|
include => string.Equals(Path.GetExtension(include), ".csproj", StringComparison.OrdinalIgnoreCase)));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string FindXprojFile(string projectDirectory)
|
||||||
|
{
|
||||||
|
var allXprojFiles = Directory.EnumerateFiles(projectDirectory, "*.xproj", SearchOption.TopDirectoryOnly);
|
||||||
|
|
||||||
|
if (allXprojFiles.Count() > 1)
|
||||||
|
{
|
||||||
|
MigrationErrorCodes
|
||||||
|
.MIGRATE1017($"Multiple xproj files found in {projectDirectory}, please specify which to use")
|
||||||
|
.Throw();
|
||||||
|
}
|
||||||
|
|
||||||
|
return allXprojFiles.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
private Dictionary<string, ProjectDependency> FindPossibleProjectDependencies(string projectJsonFilePath)
|
private Dictionary<string, ProjectDependency> FindPossibleProjectDependencies(string projectJsonFilePath)
|
||||||
{
|
{
|
||||||
var projectDirectory = Path.GetDirectoryName(projectJsonFilePath);
|
var projectRootDirectory = GetRootFromProjectJson(projectJsonFilePath);
|
||||||
|
|
||||||
var projectSearchPaths = new List<string>();
|
var projectSearchPaths = new List<string>();
|
||||||
projectSearchPaths.Add(projectDirectory);
|
projectSearchPaths.Add(projectRootDirectory);
|
||||||
|
|
||||||
var globalPaths = GetGlobalPaths(projectDirectory);
|
var globalPaths = GetGlobalPaths(projectRootDirectory);
|
||||||
projectSearchPaths = projectSearchPaths.Union(globalPaths).ToList();
|
projectSearchPaths = projectSearchPaths.Union(globalPaths).ToList();
|
||||||
|
|
||||||
var projects = new Dictionary<string, ProjectDependency>(StringComparer.Ordinal);
|
var projects = new Dictionary<string, ProjectDependency>(StringComparer.Ordinal);
|
||||||
|
@ -93,6 +155,31 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
return projects;
|
return projects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the parent directory of the project.json.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="projectJsonPath">Full path to project.json.</param>
|
||||||
|
private static string GetRootFromProjectJson(string projectJsonPath)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(projectJsonPath))
|
||||||
|
{
|
||||||
|
var file = new FileInfo(projectJsonPath);
|
||||||
|
|
||||||
|
// If for some reason we are at the root of the drive this will be null
|
||||||
|
// Use the file directory instead.
|
||||||
|
if (file.Directory.Parent == null)
|
||||||
|
{
|
||||||
|
return file.Directory.FullName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return file.Directory.Parent.FullName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectJsonPath;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create the list of potential projects from the search paths.
|
/// Create the list of potential projects from the search paths.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -5,6 +5,8 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.Build.Construction;
|
using Microsoft.Build.Construction;
|
||||||
using Microsoft.DotNet.ProjectModel;
|
using Microsoft.DotNet.ProjectModel;
|
||||||
|
using Microsoft.DotNet.ProjectModel.Graph;
|
||||||
|
using Microsoft.DotNet.Cli;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.DotNet.ProjectJsonMigration.Rules;
|
using Microsoft.DotNet.ProjectJsonMigration.Rules;
|
||||||
|
@ -22,6 +24,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
// - Migrating Deprecated project.jsons
|
// - Migrating Deprecated project.jsons
|
||||||
|
|
||||||
private readonly IMigrationRule _ruleSet;
|
private readonly IMigrationRule _ruleSet;
|
||||||
|
private readonly ProjectDependencyFinder _projectDependencyFinder = new ProjectDependencyFinder();
|
||||||
|
|
||||||
public ProjectMigrator() : this(new DefaultMigrationRuleSet()) { }
|
public ProjectMigrator() : this(new DefaultMigrationRuleSet()) { }
|
||||||
|
|
||||||
|
@ -30,9 +33,73 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
_ruleSet = ruleSet;
|
_ruleSet = ruleSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Migrate(MigrationSettings migrationSettings)
|
public void Migrate(MigrationSettings rootSettings, bool skipProjectReferences = false)
|
||||||
|
{
|
||||||
|
if (rootSettings == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
}
|
||||||
|
|
||||||
|
MigrateProject(rootSettings);
|
||||||
|
|
||||||
|
if (skipProjectReferences)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var projectDependencies = ResolveTransitiveClosureProjectDependencies(rootSettings.ProjectDirectory, rootSettings.ProjectXProjFilePath);
|
||||||
|
|
||||||
|
foreach(var project in projectDependencies)
|
||||||
|
{
|
||||||
|
var projectDir = Path.GetDirectoryName(project.ProjectFilePath);
|
||||||
|
var settings = new MigrationSettings(projectDir,
|
||||||
|
projectDir,
|
||||||
|
rootSettings.SdkPackageVersion,
|
||||||
|
rootSettings.MSBuildProjectTemplate);
|
||||||
|
MigrateProject(settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<ProjectDependency> ResolveTransitiveClosureProjectDependencies(string rootProject, string xprojFile)
|
||||||
|
{
|
||||||
|
HashSet<ProjectDependency> projectsMap = new HashSet<ProjectDependency>(new ProjectDependencyComparer());
|
||||||
|
var projectDependencies = _projectDependencyFinder.ResolveProjectDependencies(rootProject, xprojFile);
|
||||||
|
Queue<ProjectDependency> projectsQueue = new Queue<ProjectDependency>(projectDependencies);
|
||||||
|
|
||||||
|
while(projectsQueue.Count() != 0)
|
||||||
|
{
|
||||||
|
var projectDependency = projectsQueue.Dequeue();
|
||||||
|
|
||||||
|
if (projectsMap.Contains(projectDependency))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
projectsMap.Add(projectDependency);
|
||||||
|
|
||||||
|
var projectDir = Path.GetDirectoryName(projectDependency.ProjectFilePath);
|
||||||
|
projectDependencies = _projectDependencyFinder.ResolveProjectDependencies(projectDir);
|
||||||
|
|
||||||
|
foreach(var project in projectDependencies)
|
||||||
|
{
|
||||||
|
projectsQueue.Enqueue(project);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MigrateProject(MigrationSettings migrationSettings)
|
||||||
{
|
{
|
||||||
var migrationRuleInputs = ComputeMigrationRuleInputs(migrationSettings);
|
var migrationRuleInputs = ComputeMigrationRuleInputs(migrationSettings);
|
||||||
|
|
||||||
|
if (IsMigrated(migrationSettings, migrationRuleInputs))
|
||||||
|
{
|
||||||
|
// TODO : Adding user-visible logging
|
||||||
|
MigrationTrace.Instance.WriteLine($"{nameof(ProjectMigrator)}: Skip migrating {migrationSettings.ProjectDirectory}, it is already migrated.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
VerifyInputs(migrationRuleInputs, migrationSettings);
|
VerifyInputs(migrationRuleInputs, migrationSettings);
|
||||||
|
|
||||||
SetupOutputDirectory(migrationSettings.ProjectDirectory, migrationSettings.OutputDirectory);
|
SetupOutputDirectory(migrationSettings.ProjectDirectory, migrationSettings.OutputDirectory);
|
||||||
|
@ -43,7 +110,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
private MigrationRuleInputs ComputeMigrationRuleInputs(MigrationSettings migrationSettings)
|
private MigrationRuleInputs ComputeMigrationRuleInputs(MigrationSettings migrationSettings)
|
||||||
{
|
{
|
||||||
var projectContexts = ProjectContext.CreateContextForEachFramework(migrationSettings.ProjectDirectory);
|
var projectContexts = ProjectContext.CreateContextForEachFramework(migrationSettings.ProjectDirectory);
|
||||||
var xprojFile = migrationSettings.ProjectXProjFilePath ?? FindXprojFile(migrationSettings.ProjectDirectory);
|
var xprojFile = migrationSettings.ProjectXProjFilePath ?? _projectDependencyFinder.FindXprojFile(migrationSettings.ProjectDirectory);
|
||||||
|
|
||||||
ProjectRootElement xproj = null;
|
ProjectRootElement xproj = null;
|
||||||
if (xprojFile != null)
|
if (xprojFile != null)
|
||||||
|
@ -63,20 +130,6 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
return new MigrationRuleInputs(projectContexts, templateMSBuildProject, itemGroup, propertyGroup, xproj);
|
return new MigrationRuleInputs(projectContexts, templateMSBuildProject, itemGroup, propertyGroup, xproj);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string FindXprojFile(string projectDirectory)
|
|
||||||
{
|
|
||||||
var allXprojFiles = Directory.EnumerateFiles(projectDirectory, "*.xproj", SearchOption.TopDirectoryOnly);
|
|
||||||
|
|
||||||
if (allXprojFiles.Count() > 1)
|
|
||||||
{
|
|
||||||
MigrationErrorCodes
|
|
||||||
.MIGRATE1017($"Multiple xproj files found in {projectDirectory}, please specify which to use")
|
|
||||||
.Throw();
|
|
||||||
}
|
|
||||||
|
|
||||||
return allXprojFiles.FirstOrDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void VerifyInputs(MigrationRuleInputs migrationRuleInputs, MigrationSettings migrationSettings)
|
private void VerifyInputs(MigrationRuleInputs migrationRuleInputs, MigrationSettings migrationSettings)
|
||||||
{
|
{
|
||||||
VerifyProject(migrationRuleInputs.ProjectContexts, migrationSettings.ProjectDirectory);
|
VerifyProject(migrationRuleInputs.ProjectContexts, migrationSettings.ProjectDirectory);
|
||||||
|
@ -146,5 +199,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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,9 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
{
|
{
|
||||||
_projectDirectory = migrationSettings.ProjectDirectory;
|
_projectDirectory = migrationSettings.ProjectDirectory;
|
||||||
|
|
||||||
var migratedXProjDependencyPaths = MigrateXProjProjectDependencies(migrationSettings, migrationRuleInputs);
|
var migratedXProjDependencyPaths = MigrateXProjProjectDependencies(migrationRuleInputs);
|
||||||
var migratedXProjDependencyNames = new HashSet<string>(migratedXProjDependencyPaths.Select(p => Path.GetFileNameWithoutExtension(p)));
|
var migratedXProjDependencyNames = new HashSet<string>(migratedXProjDependencyPaths.Select(p => Path.GetFileNameWithoutExtension(
|
||||||
|
PathUtility.GetPathWithDirectorySeparator(p))));
|
||||||
|
|
||||||
AddPropertyTransformsToCommonPropertyGroup(migrationRuleInputs.CommonPropertyGroup);
|
AddPropertyTransformsToCommonPropertyGroup(migrationRuleInputs.CommonPropertyGroup);
|
||||||
MigrateProjectJsonProjectDependencies(
|
MigrateProjectJsonProjectDependencies(
|
||||||
|
@ -40,23 +41,17 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
migrationRuleInputs.OutputMSBuildProject);
|
migrationRuleInputs.OutputMSBuildProject);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<string> MigrateXProjProjectDependencies(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
|
private IEnumerable<string> MigrateXProjProjectDependencies(MigrationRuleInputs migrationRuleInputs)
|
||||||
{
|
{
|
||||||
var xproj = migrationRuleInputs.ProjectXproj;
|
var csprojReferenceItems = _projectDependencyFinder.ResolveXProjProjectDependencies(migrationRuleInputs.ProjectXproj);
|
||||||
if (xproj == null)
|
|
||||||
|
if (!csprojReferenceItems.Any())
|
||||||
{
|
{
|
||||||
MigrationTrace.Instance.WriteLine($"{nameof(MigrateProjectDependenciesRule)}: No xproj file given.");
|
|
||||||
return Enumerable.Empty<string>();
|
return Enumerable.Empty<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var csprojTransformedReferences = new List<ProjectItemElement>();
|
var csprojTransformedReferences = new List<ProjectItemElement>();
|
||||||
|
|
||||||
var csprojReferenceItems = xproj.Items
|
|
||||||
.Where(i => i.ItemType == "ProjectReference")
|
|
||||||
.Where(p =>
|
|
||||||
p.Includes().Any(
|
|
||||||
include => string.Equals(Path.GetExtension(include), ".csproj", StringComparison.OrdinalIgnoreCase)));
|
|
||||||
|
|
||||||
foreach (var csprojReferenceItem in csprojReferenceItems)
|
foreach (var csprojReferenceItem in csprojReferenceItems)
|
||||||
{
|
{
|
||||||
var conditionChain = csprojReferenceItem.ConditionChain();
|
var conditionChain = csprojReferenceItem.ConditionChain();
|
||||||
|
@ -71,8 +66,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
|
|
||||||
csprojTransformedReferences.Add(transformItem);
|
csprojTransformedReferences.Add(transformItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MigrationTrace.Instance.WriteLine($"{nameof(MigrateProjectDependenciesRule)}: Migrating {csprojTransformedReferences.Count()} xproj to csproj references");
|
MigrationTrace.Instance.WriteLine($"{nameof(MigrateProjectDependenciesRule)}: Migrating {csprojTransformedReferences.Count()} xproj to csproj references");
|
||||||
|
|
||||||
foreach (var csprojTransformedReference in csprojTransformedReferences)
|
foreach (var csprojTransformedReference in csprojTransformedReferences)
|
||||||
|
@ -91,7 +85,6 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
foreach (var projectContext in projectContexts)
|
foreach (var projectContext in projectContexts)
|
||||||
{
|
{
|
||||||
var projectDependencies = _projectDependencyFinder.ResolveProjectDependencies(projectContext, migratedXProjDependencyNames);
|
var projectDependencies = _projectDependencyFinder.ResolveProjectDependencies(projectContext, migratedXProjDependencyNames);
|
||||||
var projectExports = projectContext.CreateExporter("_").GetDependencies();
|
|
||||||
|
|
||||||
var projectDependencyTransformResults = projectDependencies.Select(p => ProjectDependencyTransform.Transform(p));
|
var projectDependencyTransformResults = projectDependencies.Select(p => ProjectDependencyTransform.Transform(p));
|
||||||
|
|
||||||
|
|
|
@ -13,21 +13,20 @@ namespace Microsoft.DotNet.Tools.Migrate
|
||||||
public partial class MigrateCommand
|
public partial class MigrateCommand
|
||||||
{
|
{
|
||||||
private readonly string _templateFile;
|
private readonly string _templateFile;
|
||||||
private readonly string _outputDirectory;
|
|
||||||
private readonly string _projectJson;
|
private readonly string _projectJson;
|
||||||
private readonly string _sdkVersion;
|
private readonly string _sdkVersion;
|
||||||
private readonly string _xprojFilePath;
|
private readonly string _xprojFilePath;
|
||||||
|
private readonly bool _skipProjectReferences;
|
||||||
|
|
||||||
private readonly TemporaryDotnetNewTemplateProject _temporaryDotnetNewProject;
|
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;
|
_templateFile = templateFile;
|
||||||
_outputDirectory = outputDirectory;
|
|
||||||
_projectJson = projectJson;
|
_projectJson = projectJson;
|
||||||
_sdkVersion = sdkVersion;
|
_sdkVersion = sdkVersion;
|
||||||
_xprojFilePath = xprojFilePath;
|
_xprojFilePath = xprojFilePath;
|
||||||
|
_skipProjectReferences = skipProjectReferences;
|
||||||
_temporaryDotnetNewProject = new TemporaryDotnetNewTemplateProject();
|
_temporaryDotnetNewProject = new TemporaryDotnetNewTemplateProject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,14 +39,13 @@ namespace Microsoft.DotNet.Tools.Migrate
|
||||||
var msBuildTemplate = _templateFile != null ?
|
var msBuildTemplate = _templateFile != null ?
|
||||||
ProjectRootElement.TryOpen(_templateFile) : _temporaryDotnetNewProject.MSBuildProject;
|
ProjectRootElement.TryOpen(_templateFile) : _temporaryDotnetNewProject.MSBuildProject;
|
||||||
|
|
||||||
var outputDirectory = _outputDirectory ?? projectDirectory;
|
var outputDirectory = projectDirectory;
|
||||||
EnsureNotNull(outputDirectory, "Null output directory");
|
|
||||||
|
|
||||||
var sdkVersion = _sdkVersion ?? new ProjectJsonParser(_temporaryDotnetNewProject.ProjectJson).SdkPackageVersion;
|
var sdkVersion = _sdkVersion ?? new ProjectJsonParser(_temporaryDotnetNewProject.ProjectJson).SdkPackageVersion;
|
||||||
EnsureNotNull(sdkVersion, "Null Sdk Version");
|
EnsureNotNull(sdkVersion, "Null Sdk Version");
|
||||||
|
|
||||||
var migrationSettings = new MigrationSettings(projectDirectory, outputDirectory, sdkVersion, msBuildTemplate, _xprojFilePath);
|
var migrationSettings = new MigrationSettings(projectDirectory, outputDirectory, sdkVersion, msBuildTemplate, _xprojFilePath);
|
||||||
new ProjectMigrator().Migrate(migrationSettings);
|
new ProjectMigrator().Migrate(migrationSettings, _skipProjectReferences);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,19 +21,19 @@ namespace Microsoft.DotNet.Tools.Migrate
|
||||||
app.HelpOption("-h|--help");
|
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 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 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 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 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(() =>
|
app.OnExecute(() =>
|
||||||
{
|
{
|
||||||
MigrateCommand migrateCommand = new MigrateCommand(
|
MigrateCommand migrateCommand = new MigrateCommand(
|
||||||
template.Value(),
|
template.Value(),
|
||||||
output.Value(),
|
|
||||||
project.Value(),
|
project.Value(),
|
||||||
sdkVersion.Value(),
|
sdkVersion.Value(),
|
||||||
xprojFile.Value());
|
xprojFile.Value(),
|
||||||
|
skipProjectReferences.BoolValue.HasValue ? skipProjectReferences.BoolValue.Value : false);
|
||||||
|
|
||||||
return migrateCommand.Execute();
|
return migrateCommand.Execute();
|
||||||
});
|
});
|
||||||
|
|
|
@ -113,6 +113,63 @@ namespace Microsoft.DotNet.Migration.Tests
|
||||||
outputsIdentical.Should().BeTrue();
|
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;
|
||||||
|
|
||||||
|
FixUpProjectJsons(projectDirectory);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
FixUpProjectJsons(projectDirectory);
|
||||||
|
|
||||||
|
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 void FixUpProjectJsons(string projectDirectory)
|
||||||
|
{
|
||||||
|
var pjs = Directory.EnumerateFiles(projectDirectory, "project.json.1", SearchOption.AllDirectories);
|
||||||
|
|
||||||
|
foreach(var pj in pjs)
|
||||||
|
{
|
||||||
|
var newPj = pj.Replace("project.json.1", "project.json");
|
||||||
|
File.Move(pj, newPj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private MigratedBuildComparisonData GetDotnetNewComparisonData(string projectDirectory, string dotnetNewType)
|
private MigratedBuildComparisonData GetDotnetNewComparisonData(string projectDirectory, string dotnetNewType)
|
||||||
{
|
{
|
||||||
DotnetNew(projectDirectory, dotnetNewType);
|
DotnetNew(projectDirectory, dotnetNewType);
|
||||||
|
|
Loading…
Reference in a new issue