diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectA/.noautobuild b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectA/.noautobuild
new file mode 100644
index 000000000..e69de29bb
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectA/Program.cs b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectA/Program.cs
new file mode 100644
index 000000000..7b3022c33
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectA/Program.cs
@@ -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;
+ }
+ }
+}
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectA/project.json.1 b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectA/project.json.1
new file mode 100644
index 000000000..de7f186e5
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectA/project.json.1
@@ -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": {}
+ }
+}
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectB/.noautobuild b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectB/.noautobuild
new file mode 100644
index 000000000..e69de29bb
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectB/Helper.cs b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectB/Helper.cs
new file mode 100644
index 000000000..5a986d891
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectB/Helper.cs
@@ -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";
+ }
+ }
+}
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectB/project.json.1 b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectB/project.json.1
new file mode 100644
index 000000000..1eb055cca
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectB/project.json.1
@@ -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": {}
+ }
+}
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectC/.noautobuild b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectC/.noautobuild
new file mode 100644
index 000000000..e69de29bb
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectC/Helper.cs b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectC/Helper.cs
new file mode 100644
index 000000000..317f57fc2
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectC/Helper.cs
@@ -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";
+ }
+ }
+}
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectC/ProjectC.xproj b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectC/ProjectC.xproj
new file mode 100644
index 000000000..75a8bd8d7
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectC/ProjectC.xproj
@@ -0,0 +1,23 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ 084222f1-7909-48f4-81e8-a97398b26b1c
+ ProjectC
+ obj
+ bin
+ v4.6
+
+
+ 2.0
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectC/project.json.1 b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectC/project.json.1
new file mode 100644
index 000000000..ef6bf69c3
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectC/project.json.1
@@ -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": {}
+ }
+}
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectD/.noautobuild b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectD/.noautobuild
new file mode 100644
index 000000000..e69de29bb
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectD/Helper.cs b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectD/Helper.cs
new file mode 100644
index 000000000..0c0422913
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectD/Helper.cs
@@ -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";
+ }
+ }
+}
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectD/project.json.1 b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectD/project.json.1
new file mode 100644
index 000000000..48bc772d8
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectD/project.json.1
@@ -0,0 +1,18 @@
+{
+ "version": "1.0.0-*",
+ "buildOptions": {
+ "nowarn": [
+ "CS1591"
+ ],
+ "xmlDoc": true,
+ "additionalArguments": [
+ "-highentropyva+"
+ ]
+ },
+ "dependencies": {
+ "NETStandard.Library": "1.6.0"
+ },
+ "frameworks": {
+ "netstandard1.5": {}
+ }
+}
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectE/.noautobuild b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectE/.noautobuild
new file mode 100644
index 000000000..e69de29bb
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectE/Helper.cs b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectE/Helper.cs
new file mode 100644
index 000000000..b91a23164
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectE/Helper.cs
@@ -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";
+ }
+ }
+}
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectE/ProjectE.xproj b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectE/ProjectE.xproj
new file mode 100644
index 000000000..3e583c67f
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectE/ProjectE.xproj
@@ -0,0 +1,23 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ 7fb8f138-ffb0-4eec-af9e-2e6ff9979593
+ ProjectE
+ obj
+ bin
+ v4.6
+
+
+ 2.0
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TestAssets/TestProjects/TestAppDependencyGraph/ProjectE/project.json.1 b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectE/project.json.1
new file mode 100644
index 000000000..fae1dbe77
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppDependencyGraph/ProjectE/project.json.1
@@ -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": {}
+ }
+}
diff --git a/src/Microsoft.DotNet.ProjectJsonMigration/ProjectDependencyComparer.cs b/src/Microsoft.DotNet.ProjectJsonMigration/ProjectDependencyComparer.cs
new file mode 100644
index 000000000..afb2a8578
--- /dev/null
+++ b/src/Microsoft.DotNet.ProjectJsonMigration/ProjectDependencyComparer.cs
@@ -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
+ {
+ 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);
+ }
+ }
+}
diff --git a/src/Microsoft.DotNet.ProjectJsonMigration/ProjectDependencyFinder.cs b/src/Microsoft.DotNet.ProjectJsonMigration/ProjectDependencyFinder.cs
index 1c4280172..0fcd24896 100644
--- a/src/Microsoft.DotNet.ProjectJsonMigration/ProjectDependencyFinder.cs
+++ b/src/Microsoft.DotNet.ProjectJsonMigration/ProjectDependencyFinder.cs
@@ -3,18 +3,45 @@
using System;
using System.Collections.Generic;
+using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectModel;
using System.Linq;
using System.IO;
using Newtonsoft.Json.Linq;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Graph;
+using Microsoft.DotNet.Tools.Common;
namespace Microsoft.DotNet.ProjectJsonMigration
{
public class ProjectDependencyFinder
{
- public IEnumerable ResolveProjectDependencies(ProjectContext projectContext, HashSet preResolvedProjects=null)
+ public IEnumerable 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 ResolveProjectDependencies(IEnumerable projectContexts, IEnumerable preResolvedProjects=null)
+ {
+ foreach(var projectContext in projectContexts)
+ {
+ foreach(var projectDependency in ResolveProjectDependencies(projectContext, preResolvedProjects))
+ {
+ yield return projectDependency;
+ }
+ }
+ }
+
+ public IEnumerable ResolveProjectDependencies(ProjectContext projectContext, IEnumerable preResolvedProjects=null)
{
preResolvedProjects = preResolvedProjects ?? new HashSet();
@@ -48,14 +75,49 @@ namespace Microsoft.DotNet.ProjectJsonMigration
return projectDependencies;
}
+ private IEnumerable ResolveXProjProjectDependencyNames(ProjectRootElement xproj)
+ {
+ var xprojDependencies = ResolveXProjProjectDependencies(xproj).SelectMany(r => r.Includes());
+ return new HashSet(xprojDependencies.Select(p => Path.GetFileNameWithoutExtension(
+ PathUtility.GetPathWithDirectorySeparator(p))));
+ }
+
+ internal IEnumerable ResolveXProjProjectDependencies(ProjectRootElement xproj)
+ {
+ if (xproj == null)
+ {
+ MigrationTrace.Instance.WriteLine($"{nameof(ProjectDependencyFinder)}: No xproj file given.");
+ return Enumerable.Empty();
+ }
+
+ 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 FindPossibleProjectDependencies(string projectJsonFilePath)
{
- var projectDirectory = Path.GetDirectoryName(projectJsonFilePath);
+ var projectRootDirectory = GetRootFromProjectJson(projectJsonFilePath);
var projectSearchPaths = new List();
- projectSearchPaths.Add(projectDirectory);
+ projectSearchPaths.Add(projectRootDirectory);
- var globalPaths = GetGlobalPaths(projectDirectory);
+ var globalPaths = GetGlobalPaths(projectRootDirectory);
projectSearchPaths = projectSearchPaths.Union(globalPaths).ToList();
var projects = new Dictionary(StringComparer.Ordinal);
@@ -93,6 +155,31 @@ namespace Microsoft.DotNet.ProjectJsonMigration
return projects;
}
+ ///
+ /// Finds the parent directory of the project.json.
+ ///
+ /// Full path to project.json.
+ 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;
+ }
+
///
/// Create the list of potential projects from the search paths.
///
diff --git a/src/Microsoft.DotNet.ProjectJsonMigration/ProjectMigrator.cs b/src/Microsoft.DotNet.ProjectJsonMigration/ProjectMigrator.cs
index 6633e0459..8f1385f67 100644
--- a/src/Microsoft.DotNet.ProjectJsonMigration/ProjectMigrator.cs
+++ b/src/Microsoft.DotNet.ProjectJsonMigration/ProjectMigrator.cs
@@ -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();
+ }
+
+ 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 ResolveTransitiveClosureProjectDependencies(string rootProject, string xprojFile)
+ {
+ HashSet projectsMap = new HashSet(new ProjectDependencyComparer());
+ var projectDependencies = _projectDependencyFinder.ResolveProjectDependencies(rootProject, xprojFile);
+ Queue projectsQueue = new Queue(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);
+
+ 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);
SetupOutputDirectory(migrationSettings.ProjectDirectory, migrationSettings.OutputDirectory);
@@ -43,7 +110,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration
private MigrationRuleInputs ComputeMigrationRuleInputs(MigrationSettings migrationSettings)
{
var projectContexts = ProjectContext.CreateContextForEachFramework(migrationSettings.ProjectDirectory);
- var xprojFile = migrationSettings.ProjectXProjFilePath ?? FindXprojFile(migrationSettings.ProjectDirectory);
+ var xprojFile = migrationSettings.ProjectXProjFilePath ?? _projectDependencyFinder.FindXprojFile(migrationSettings.ProjectDirectory);
ProjectRootElement xproj = null;
if (xprojFile != null)
@@ -63,20 +130,6 @@ namespace Microsoft.DotNet.ProjectJsonMigration
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)
{
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);
+ }
+
}
}
diff --git a/src/Microsoft.DotNet.ProjectJsonMigration/Rules/MigrateProjectDependenciesRule.cs b/src/Microsoft.DotNet.ProjectJsonMigration/Rules/MigrateProjectDependenciesRule.cs
index c1ee02a41..9386c8fa4 100644
--- a/src/Microsoft.DotNet.ProjectJsonMigration/Rules/MigrateProjectDependenciesRule.cs
+++ b/src/Microsoft.DotNet.ProjectJsonMigration/Rules/MigrateProjectDependenciesRule.cs
@@ -30,8 +30,9 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
_projectDirectory = migrationSettings.ProjectDirectory;
- var migratedXProjDependencyPaths = MigrateXProjProjectDependencies(migrationSettings, migrationRuleInputs);
- var migratedXProjDependencyNames = new HashSet(migratedXProjDependencyPaths.Select(p => Path.GetFileNameWithoutExtension(p)));
+ var migratedXProjDependencyPaths = MigrateXProjProjectDependencies(migrationRuleInputs);
+ var migratedXProjDependencyNames = new HashSet(migratedXProjDependencyPaths.Select(p => Path.GetFileNameWithoutExtension(
+ PathUtility.GetPathWithDirectorySeparator(p))));
AddPropertyTransformsToCommonPropertyGroup(migrationRuleInputs.CommonPropertyGroup);
MigrateProjectJsonProjectDependencies(
@@ -40,23 +41,17 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
migrationRuleInputs.OutputMSBuildProject);
}
- private IEnumerable MigrateXProjProjectDependencies(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
+ private IEnumerable MigrateXProjProjectDependencies(MigrationRuleInputs migrationRuleInputs)
{
- var xproj = migrationRuleInputs.ProjectXproj;
- if (xproj == null)
+ var csprojReferenceItems = _projectDependencyFinder.ResolveXProjProjectDependencies(migrationRuleInputs.ProjectXproj);
+
+ if (!csprojReferenceItems.Any())
{
- MigrationTrace.Instance.WriteLine($"{nameof(MigrateProjectDependenciesRule)}: No xproj file given.");
return Enumerable.Empty();
}
var csprojTransformedReferences = new List();
- 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)
{
var conditionChain = csprojReferenceItem.ConditionChain();
@@ -71,8 +66,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
csprojTransformedReferences.Add(transformItem);
}
-
-
+
MigrationTrace.Instance.WriteLine($"{nameof(MigrateProjectDependenciesRule)}: Migrating {csprojTransformedReferences.Count()} xproj to csproj references");
foreach (var csprojTransformedReference in csprojTransformedReferences)
@@ -91,7 +85,6 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
foreach (var projectContext in projectContexts)
{
var projectDependencies = _projectDependencyFinder.ResolveProjectDependencies(projectContext, migratedXProjDependencyNames);
- var projectExports = projectContext.CreateExporter("_").GetDependencies();
var projectDependencyTransformResults = projectDependencies.Select(p => ProjectDependencyTransform.Transform(p));
diff --git a/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs b/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs
index ccd599cd5..4de5c1866 100644
--- a/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs
+++ b/src/dotnet/commands/dotnet-migrate/MigrateCommand.cs
@@ -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;
}
diff --git a/src/dotnet/commands/dotnet-migrate/Program.cs b/src/dotnet/commands/dotnet-migrate/Program.cs
index da11f8477..706edd85c 100644
--- a/src/dotnet/commands/dotnet-migrate/Program.cs
+++ b/src/dotnet/commands/dotnet-migrate/Program.cs
@@ -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();
});
diff --git a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateTestApps.cs b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateTestApps.cs
index 7ea3a3cfb..08f9a657f 100644
--- a/test/dotnet-migrate.Tests/GivenThatIWantToMigrateTestApps.cs
+++ b/test/dotnet-migrate.Tests/GivenThatIWantToMigrateTestApps.cs
@@ -113,6 +113,63 @@ 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;
+
+ 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)
{
DotnetNew(projectDirectory, dotnetNewType);