Merge pull request #4224 from brthor/brthor/remove-nuget

Migration: Remove dependency on lock file and dotnet restore
This commit is contained in:
Bryan Thornbury 2016-09-22 18:47:58 -07:00 committed by GitHub
commit a04194c0f7
30 changed files with 586 additions and 134 deletions

View file

@ -3,13 +3,8 @@
using System;
using System.Collections.Generic;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli;
using System.Linq;
using System.IO;
namespace Microsoft.DotNet.ProjectJsonMigration
{

View file

@ -1,4 +1,5 @@
using System;
// 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.
namespace Microsoft.DotNet.ProjectJsonMigration
{
@ -19,7 +20,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration
public void Throw()
{
throw new Exception(GetFormattedErrorMessage());
throw new MigrationException(GetFormattedErrorMessage());
}
public string GetFormattedErrorMessage()

View file

@ -1,4 +1,7 @@
using System;
// 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 Microsoft.DotNet.ProjectJsonMigration
{
@ -22,6 +25,9 @@ namespace Microsoft.DotNet.ProjectJsonMigration
public static Func<string, MigrationError> MIGRATE1016
=> (message) => new MigrationError(nameof(MIGRATE1016), "Unsupported Script Variable", message);
public static Func<string, MigrationError> MIGRATE1017
=> (message) => new MigrationError(nameof(MIGRATE1017), "Multiple Xproj Files", message);
// Potentially Temporary (Point in Time) Errors
public static Func<string, MigrationError> MIGRATE20011
=> (message) => new MigrationError(nameof(MIGRATE20011), "Multi-TFM", message);

View file

@ -0,0 +1,12 @@
// 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 Microsoft.DotNet.ProjectJsonMigration
{
public class MigrationException : Exception
{
public MigrationException(string message) : base(message) { }
}
}

View file

@ -1,20 +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.Collections.Generic;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli;
using System.Linq;
using System.IO;
namespace Microsoft.DotNet.ProjectJsonMigration
{
public class MigrationRuleInputs
{
public ProjectRootElement ProjectXproj { get; }
public ProjectRootElement OutputMSBuildProject { get; }
public ProjectItemGroupElement CommonItemGroup { get; }
@ -35,8 +32,10 @@ namespace Microsoft.DotNet.ProjectJsonMigration
IEnumerable<ProjectContext> projectContexts,
ProjectRootElement outputMSBuildProject,
ProjectItemGroupElement commonItemGroup,
ProjectPropertyGroupElement commonPropertyGroup)
ProjectPropertyGroupElement commonPropertyGroup,
ProjectRootElement projectXproj=null)
{
ProjectXproj = projectXproj;
ProjectContexts = projectContexts;
OutputMSBuildProject = outputMSBuildProject;
CommonItemGroup = commonItemGroup;

View file

@ -1,35 +1,30 @@
// 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;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli;
using System.Linq;
using System.IO;
namespace Microsoft.DotNet.ProjectJsonMigration
{
public class MigrationSettings
{
public string ProjectXProjFilePath { get; }
public string ProjectDirectory { get; }
public string OutputDirectory { get; }
public string SdkPackageVersion { get; }
public ProjectRootElement MSBuildProjectTemplate { get; }
public MigrationSettings(
string projectDirectory,
string outputDirectory,
string sdkPackageVersion,
ProjectRootElement msBuildProjectTemplate)
ProjectRootElement msBuildProjectTemplate,
string projectXprojFilePath=null)
{
ProjectDirectory = projectDirectory;
OutputDirectory = outputDirectory;
SdkPackageVersion = sdkPackageVersion;
MSBuildProjectTemplate = msBuildProjectTemplate;
ProjectXProjFilePath = projectXprojFilePath;
}
}
}

View file

@ -1,5 +1,7 @@
using System;
using System.Text.RegularExpressions;
// 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 Microsoft.DotNet.ProjectJsonMigration
{

View file

@ -1,5 +1,6 @@
// 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 Microsoft.DotNet.ProjectJsonMigration.Models

View file

@ -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.
namespace Microsoft.DotNet.ProjectJsonMigration
{
public class ProjectDependency
{
public string Name { get; }
public string ProjectFilePath { get; }
public ProjectDependency(string name, string projectFilePath)
{
Name = name;
ProjectFilePath = projectFilePath;
}
}
}

View file

@ -0,0 +1,234 @@
// 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;
using Microsoft.DotNet.ProjectModel;
using System.Linq;
using System.IO;
using Newtonsoft.Json.Linq;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Graph;
namespace Microsoft.DotNet.ProjectJsonMigration
{
public class ProjectDependencyFinder
{
public IEnumerable<ProjectDependency> ResolveProjectDependencies(ProjectContext projectContext, HashSet<string> preResolvedProjects=null)
{
preResolvedProjects = preResolvedProjects ?? new HashSet<string>();
var projectExports = projectContext.CreateExporter("_").GetDependencies();
var possibleProjectDependencies =
FindPossibleProjectDependencies(projectContext.ProjectFile.ProjectFilePath);
var projectDependencies = new List<ProjectDependency>();
foreach (var projectExport in projectExports)
{
var projectExportName = projectExport.Library.Identity.Name;
ProjectDependency projectDependency;
if (!possibleProjectDependencies.TryGetValue(projectExportName, out projectDependency))
{
if (projectExport.Library.Identity.Type.Equals(LibraryType.Project)
&& !preResolvedProjects.Contains(projectExportName))
{
MigrationErrorCodes
.MIGRATE1014($"Unresolved project dependency ({projectExportName})").Throw();
}
else
{
continue;
}
}
projectDependencies.Add(projectDependency);
}
return projectDependencies;
}
private Dictionary<string, ProjectDependency> FindPossibleProjectDependencies(string projectJsonFilePath)
{
var projectDirectory = Path.GetDirectoryName(projectJsonFilePath);
var projectSearchPaths = new List<string>();
projectSearchPaths.Add(projectDirectory);
var globalPaths = GetGlobalPaths(projectDirectory);
projectSearchPaths = projectSearchPaths.Union(globalPaths).ToList();
var projects = new Dictionary<string, ProjectDependency>(StringComparer.Ordinal);
foreach (var project in GetPotentialProjects(projectSearchPaths))
{
if (projects.ContainsKey(project.Name))
{
// Remove the existing project if it doesn't have project.json
// project.json isn't checked until the project is resolved, but here
// we need to check it up front.
var otherProject = projects[project.Name];
if (project.ProjectFilePath != otherProject.ProjectFilePath)
{
var projectExists = File.Exists(project.ProjectFilePath);
var otherExists = File.Exists(otherProject.ProjectFilePath);
if (projectExists != otherExists
&& projectExists
&& !otherExists)
{
// the project currently in the cache does not exist, but this one does
// remove the old project and add the current one
projects[project.Name] = project;
}
}
}
else
{
projects.Add(project.Name, project);
}
}
return projects;
}
/// <summary>
/// Create the list of potential projects from the search paths.
/// </summary>
private static List<ProjectDependency> GetPotentialProjects(IEnumerable<string> searchPaths)
{
var projects = new List<ProjectDependency>();
// Resolve all of the potential projects
foreach (var searchPath in searchPaths)
{
var directory = new DirectoryInfo(searchPath);
if (!directory.Exists)
{
continue;
}
foreach (var projectDirectory in directory.EnumerateDirectories())
{
// Create the path to the project.json file.
var projectFilePath = Path.Combine(projectDirectory.FullName, "project.json");
// We INTENTIONALLY do not do an exists check here because it requires disk I/O
// Instead, we'll do an exists check when we try to resolve
// Check if we've already added this, just in case it was pre-loaded into the cache
var project = new ProjectDependency(projectDirectory.Name, projectFilePath);
projects.Add(project);
}
}
return projects;
}
private static List<string> GetGlobalPaths(string rootPath)
{
var paths = new List<string>();
var globalJsonRoot = ResolveRootDirectory(rootPath);
GlobalSettings globalSettings;
if (GlobalSettings.TryGetGlobalSettings(globalJsonRoot, out globalSettings))
{
foreach (var sourcePath in globalSettings.ProjectPaths)
{
var path = Path.GetFullPath(Path.Combine(globalJsonRoot, sourcePath));
paths.Add(path);
}
}
return paths;
}
private static string ResolveRootDirectory(string projectPath)
{
var di = new DirectoryInfo(projectPath);
while (di.Parent != null)
{
var globalJsonPath = Path.Combine(di.FullName, GlobalSettings.GlobalFileName);
if (File.Exists(globalJsonPath))
{
return di.FullName;
}
di = di.Parent;
}
// If we don't find any files then make the project folder the root
return projectPath;
}
private class GlobalSettings
{
public const string GlobalFileName = "global.json";
public IList<string> ProjectPaths { get; private set; }
public string PackagesPath { get; private set; }
public string FilePath { get; private set; }
public string RootPath
{
get { return Path.GetDirectoryName(FilePath); }
}
public static bool TryGetGlobalSettings(string path, out GlobalSettings globalSettings)
{
globalSettings = null;
string globalJsonPath = null;
if (Path.GetFileName(path) == GlobalFileName)
{
globalJsonPath = path;
path = Path.GetDirectoryName(path);
}
else if (!HasGlobalFile(path))
{
return false;
}
else
{
globalJsonPath = Path.Combine(path, GlobalFileName);
}
globalSettings = new GlobalSettings();
try
{
var json = File.ReadAllText(globalJsonPath);
JObject settings = JObject.Parse(json);
var projects = settings["projects"];
var dependencies = settings["dependencies"] as JObject;
globalSettings.ProjectPaths = projects == null ? new string[] { } : projects.Select(a => a.Value<string>()).ToArray();;
globalSettings.PackagesPath = settings.Value<string>("packages");
globalSettings.FilePath = globalJsonPath;
}
catch (Exception ex)
{
throw FileFormatException.Create(ex, globalJsonPath);
}
return true;
}
public static bool HasGlobalFile(string path)
{
var projectPath = Path.Combine(path, GlobalFileName);
return File.Exists(projectPath);
}
}
}
}

View file

@ -3,15 +3,12 @@
using System;
using System.Collections.Generic;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Cli;
using System.Linq;
using System.IO;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
using Microsoft.DotNet.Tools.Common;
using Newtonsoft.Json.Linq;
namespace Microsoft.DotNet.ProjectJsonMigration
{
@ -46,6 +43,13 @@ namespace Microsoft.DotNet.ProjectJsonMigration
private MigrationRuleInputs ComputeMigrationRuleInputs(MigrationSettings migrationSettings)
{
var projectContexts = ProjectContext.CreateContextForEachFramework(migrationSettings.ProjectDirectory);
var xprojFile = migrationSettings.ProjectXProjFilePath ?? FindXprojFile(migrationSettings.ProjectDirectory);
ProjectRootElement xproj = null;
if (xprojFile != null)
{
xproj = ProjectRootElement.Open(xprojFile);
}
var templateMSBuildProject = migrationSettings.MSBuildProjectTemplate;
if (templateMSBuildProject == null)
@ -56,7 +60,21 @@ namespace Microsoft.DotNet.ProjectJsonMigration
var propertyGroup = templateMSBuildProject.AddPropertyGroup();
var itemGroup = templateMSBuildProject.AddItemGroup();
return new MigrationRuleInputs(projectContexts, templateMSBuildProject, itemGroup, propertyGroup);
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)
@ -73,12 +91,6 @@ namespace Microsoft.DotNet.ProjectJsonMigration
var defaultProjectContext = projectContexts.First();
if (defaultProjectContext.LockFile == null)
{
MigrationErrorCodes.MIGRATE1012(
$"project.lock.json not found in {projectDirectory}, please run dotnet restore before doing migration").Throw();
}
var diagnostics = defaultProjectContext.ProjectFile.Diagnostics;
if (diagnostics.Any())
{

View file

@ -9,12 +9,9 @@ using Microsoft.DotNet.ProjectJsonMigration.Transforms;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Files;
using Newtonsoft.Json.Linq;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
// TODO: Should All build options be protected by a configuration condition?
// This will prevent the entire merge issue altogether and sidesteps the problem of having a duplicate include with different excludes...
public class MigrateBuildOptionsRule : IMigrationRule
{
private AddPropertyTransform<CommonCompilerOptions>[] EmitEntryPointTransforms

View file

@ -1,7 +1,6 @@
// 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;
using System.Linq;
using Microsoft.Build.Construction;

View file

@ -1,48 +1,121 @@
// 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;
using System.IO;
using System.Linq;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration.Transforms;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.Tools.Common;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
public class MigrateProjectDependenciesRule : IMigrationRule
{
private readonly ITransformApplicator _transformApplicator;
private readonly ProjectDependencyFinder _projectDependencyFinder;
private string _projectDirectory;
public MigrateProjectDependenciesRule(ITransformApplicator transformApplicator = null)
{
_transformApplicator = transformApplicator ?? new TransformApplicator();
_projectDependencyFinder = new ProjectDependencyFinder();
}
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
_projectDirectory = migrationSettings.ProjectDirectory;
var csproj = migrationRuleInputs.OutputMSBuildProject;
var projectContext = migrationRuleInputs.DefaultProjectContext;
var projectExports = projectContext.CreateExporter("_").GetDependencies(LibraryType.Project);
var migratedXProjDependencyPaths = MigrateXProjProjectDependencies(migrationSettings, migrationRuleInputs);
var migratedXProjDependencyNames = new HashSet<string>(migratedXProjDependencyPaths.Select(p => Path.GetFileNameWithoutExtension(p)));
var projectDependencyTransformResults =
projectExports.Select(projectExport => ProjectDependencyTransform.Transform(projectExport));
if (projectDependencyTransformResults.Any())
{
AddPropertyTransformsToCommonPropertyGroup(migrationRuleInputs.CommonPropertyGroup);
AddProjectDependenciesToNewItemGroup(csproj.AddItemGroup(), projectDependencyTransformResults);
}
AddPropertyTransformsToCommonPropertyGroup(migrationRuleInputs.CommonPropertyGroup);
MigrateProjectJsonProjectDependencies(
migrationRuleInputs.ProjectContexts,
migratedXProjDependencyNames,
migrationRuleInputs.OutputMSBuildProject);
}
private void AddProjectDependenciesToNewItemGroup(ProjectItemGroupElement itemGroup, IEnumerable<ProjectItemElement> projectDependencyTransformResults)
private IEnumerable<string> MigrateXProjProjectDependencies(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
var xproj = migrationRuleInputs.ProjectXproj;
if (xproj == null)
{
MigrationTrace.Instance.WriteLine($"{nameof(MigrateProjectDependenciesRule)}: No xproj file given.");
return Enumerable.Empty<string>();
}
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)
{
var conditionChain = csprojReferenceItem.ConditionChain();
var condition = string.Join(" and ", conditionChain);
var referenceInclude = string.Join(";", csprojReferenceItem.Includes()
.Where(include =>
string.Equals(Path.GetExtension(include), ".csproj", StringComparison.OrdinalIgnoreCase)));
var transformItem = ProjectDependencyStringTransform.Transform(referenceInclude);
transformItem.Condition = condition;
csprojTransformedReferences.Add(transformItem);
}
MigrationTrace.Instance.WriteLine($"{nameof(MigrateProjectDependenciesRule)}: Migrating {csprojTransformedReferences.Count()} xproj to csproj references");
foreach (var csprojTransformedReference in csprojTransformedReferences)
{
_transformApplicator.Execute(csprojTransformedReference, migrationRuleInputs.CommonItemGroup);
}
return csprojTransformedReferences.SelectMany(r => r.Includes());
}
public void MigrateProjectJsonProjectDependencies(
IEnumerable<ProjectContext> projectContexts,
HashSet<string> migratedXProjDependencyNames,
ProjectRootElement outputMSBuildProject)
{
foreach (var projectContext in projectContexts)
{
var projectDependencies = _projectDependencyFinder.ResolveProjectDependencies(projectContext, migratedXProjDependencyNames);
var projectExports = projectContext.CreateExporter("_").GetDependencies();
var projectDependencyTransformResults = projectDependencies.Select(p => ProjectDependencyTransform.Transform(p));
if (projectDependencyTransformResults.Any())
{
AddProjectDependenciesToNewItemGroup(
outputMSBuildProject.AddItemGroup(),
projectDependencyTransformResults,
projectContext.TargetFramework);
}
}
}
private void AddProjectDependenciesToNewItemGroup(
ProjectItemGroupElement itemGroup,
IEnumerable<ProjectItemElement> projectDependencyTransformResults,
NuGetFramework targetFramework)
{
if (targetFramework != null)
{
itemGroup.Condition = $" '$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion)' == '{targetFramework.DotNetFrameworkName}' ";
}
foreach (var projectDependencyTransformResult in projectDependencyTransformResults)
{
_transformApplicator.Execute(projectDependencyTransformResult, itemGroup);
@ -73,24 +146,23 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
"true",
b => true);
private AddItemTransform<LibraryExport> ProjectDependencyTransform => new AddItemTransform<LibraryExport>(
private AddItemTransform<ProjectDependency> ProjectDependencyTransform => new AddItemTransform<ProjectDependency>(
"ProjectReference",
export =>
dep =>
{
if (!export.Library.Resolved)
{
MigrationErrorCodes.MIGRATE1014(
$"Unresolved project dependency ({export.Library.Identity.Name})").Throw();
}
var projectFile = ((ProjectDescription)export.Library).Project.ProjectFilePath;
var projectDir = Path.GetDirectoryName(projectFile);
var projectDir = Path.GetDirectoryName(dep.ProjectFilePath);
var migratedProjectFileName = Path.GetFileName(projectDir) + ".csproj";
var relativeProjectDir = PathUtility.GetRelativePath(_projectDirectory + "/", projectDir);
return Path.Combine(relativeProjectDir, migratedProjectFileName);
},
export => "",
export => true);
dep => "",
dep => true);
private AddItemTransform<string> ProjectDependencyStringTransform => new AddItemTransform<string>(
"ProjectReference",
path => path,
path => "",
path => true);
}
}

View file

@ -1,12 +0,0 @@
using System;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
public class MigrateXprojProjectReferencesRule : IMigrationRule
{
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
throw new NotImplementedException("TODO: XProj ProjectToProject references");
}
}
}

View file

@ -3,15 +3,8 @@
using System;
using System.Collections.Generic;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli;
using System.Linq;
using System.IO;
using Microsoft.DotNet.ProjectJsonMigration.Models;
using Newtonsoft.Json;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{

View file

@ -2,15 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli;
using System.Linq;
using System.IO;
using Newtonsoft.Json;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{

View file

@ -2,15 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli;
using System.Linq;
using System.IO;
using Newtonsoft.Json;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{

View file

@ -1,4 +1,5 @@
using Microsoft.Build.Construction;
// 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.
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{

View file

@ -1,4 +1,7 @@
using System.Collections.Generic;
// 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.Collections.Generic;
using Microsoft.Build.Construction;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms

View file

@ -1,9 +1,11 @@
using Microsoft.DotNet.ProjectModel.Files;
// 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 Microsoft.DotNet.ProjectModel.Files;
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Build.Construction;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectJsonMigration.Models;

View file

@ -3,14 +3,8 @@
using System;
using System.Collections.Generic;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli;
using System.Linq;
using System.IO;
using Newtonsoft.Json;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{

View file

@ -16,15 +16,17 @@ namespace Microsoft.DotNet.Tools.Migrate
private readonly string _outputDirectory;
private readonly string _projectJson;
private readonly string _sdkVersion;
private readonly string _xprojFilePath;
private readonly TemporaryDotnetNewTemplateProject _temporaryDotnetNewProject;
public MigrateCommand(string templateFile, string outputDirectory, string projectJson, string sdkVersion)
public MigrateCommand(string templateFile, string outputDirectory, string projectJson, string sdkVersion, string xprojFilePath)
{
_templateFile = templateFile;
_outputDirectory = outputDirectory;
_projectJson = projectJson;
_sdkVersion = sdkVersion;
_xprojFilePath = xprojFilePath;
_temporaryDotnetNewProject = new TemporaryDotnetNewTemplateProject();
}
@ -44,7 +46,7 @@ namespace Microsoft.DotNet.Tools.Migrate
var sdkVersion = _sdkVersion ?? new ProjectJsonParser(_temporaryDotnetNewProject.ProjectJson).SdkPackageVersion;
EnsureNotNull(sdkVersion, "Null Sdk Version");
var migrationSettings = new MigrationSettings(projectDirectory, outputDirectory, sdkVersion, msBuildTemplate);
var migrationSettings = new MigrationSettings(projectDirectory, outputDirectory, sdkVersion, msBuildTemplate, _xprojFilePath);
new ProjectMigrator().Migrate(migrationSettings);
return 0;

View file

@ -24,6 +24,7 @@ namespace Microsoft.DotNet.Tools.Migrate
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);
app.OnExecute(() =>
{
@ -31,7 +32,8 @@ namespace Microsoft.DotNet.Tools.Migrate
template.Value(),
output.Value(),
project.Value(),
sdkVersion.Value());
sdkVersion.Value(),
xprojFile.Value());
return migrateCommand.Execute();
});

View file

@ -19,7 +19,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
public void It_copies_ProjectDirectory_contents_to_OutputDirectory_when_the_directories_are_different()
{
var testProjectDirectory = TestAssetsManager.CreateTestInstance("TestAppSimple", callingMethod: "z")
.WithLockFiles().Path;
.Path;
var outputDirectory = Temp.CreateDirectory().Path;
var projectDirectoryRelativeFilePaths = EnumerateFilesWithRelativePath(testProjectDirectory);
@ -41,7 +41,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
var testProjectDirectory =
TestAssetsManager.CreateTestInstance("TestLibraryWithDeprecatedProjectFile", callingMethod: "z")
.WithLockFiles().Path;
.Path;
var mockProj = ProjectRootElement.Create();
var testSettings = new MigrationSettings(testProjectDirectory, testProjectDirectory, "1.0.0", mockProj);
@ -60,7 +60,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
var testProjectDirectory =
TestAssetsManager.CreateTestInstance("FSharpTestProjects/TestApp", callingMethod: "z")
.WithLockFiles().Path;
.Path;
var mockProj = ProjectRootElement.Create();
var testSettings = new MigrationSettings(testProjectDirectory, testProjectDirectory, "1.0.0", mockProj);

View file

@ -22,7 +22,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
public void If_a_project_dependency_is_present_DesignTimeAutoUnify_and_AutoUnify_are_present()
{
var solutionDirectory =
TestAssetsManager.CreateTestInstance("TestAppWithLibrary", callingMethod: "p").WithLockFiles().Path;
TestAssetsManager.CreateTestInstance("TestAppWithLibrary", callingMethod: "p").Path;
var appDirectory = Path.Combine(solutionDirectory, "TestApp");
var libDirectory = Path.Combine(solutionDirectory, "TestLibrary");
@ -47,13 +47,13 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
public void Project_dependencies_are_migrated_to_ProjectReference()
{
var solutionDirectory =
TestAssetsManager.CreateTestInstance("TestAppWithLibrary", callingMethod: "p").WithLockFiles().Path;
TestAssetsManager.CreateTestInstance("TestAppWithLibrary", callingMethod: "p").Path;
var appDirectory = Path.Combine(solutionDirectory, "TestApp");
var projectContext = ProjectContext.Create(appDirectory, FrameworkConstants.CommonFrameworks.NetCoreApp10);
var mockProj = ProjectRootElement.Create();
var testSettings = new MigrationSettings(appDirectory, appDirectory, "1.0.0", mockProj);
var testSettings = new MigrationSettings(appDirectory, appDirectory, "1.0.0", mockProj, null);
var testInputs = new MigrationRuleInputs(new[] {projectContext}, mockProj, mockProj.AddItemGroup(),
mockProj.AddPropertyGroup());
new MigrateProjectDependenciesRule().Apply(testSettings, testInputs);
@ -72,6 +72,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
TestAssetsManager.CreateTestInstance("TestAppWithLibrary").Path;
var appDirectory = Path.Combine(solutionDirectory, "TestApp");
var libraryDirectory = Path.Combine(solutionDirectory, "TestLibrary");
Directory.Delete(libraryDirectory, true);
var projectContext = ProjectContext.Create(appDirectory, FrameworkConstants.CommonFrameworks.NetCoreApp10);
var mockProj = ProjectRootElement.Create();
@ -82,5 +84,139 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
action.ShouldThrow<Exception>()
.Where(e => e.Message.Contains("MIGRATE1014::Unresolved Dependency: Unresolved project dependency (TestLibrary)"));
}
[Theory]
[InlineData(@"some/path/to.cSproj", new [] { @"some/path/to.cSproj" })]
[InlineData(@"to.CSPROJ",new [] { @"to.CSPROJ" })]
public void It_migrates_csproj_ProjectReference_in_xproj(string projectReference, string[] expectedMigratedReferences)
{
var xproj = ProjectRootElement.Create();
xproj.AddItem("ProjectReference", projectReference);
var projectReferenceName = Path.GetFileNameWithoutExtension(projectReference);
var projectJson = @"
{
""dependencies"": {" +
$"\"{projectReferenceName}\"" + @": {
""target"" : ""project""
}
}
}
";
var testDirectory = Temp.CreateDirectory().Path;
var migratedProj = TemporaryProjectFileRuleRunner.RunRules(new IMigrationRule[]
{
new MigrateProjectDependenciesRule()
}, projectJson, testDirectory, xproj);
var migratedProjectReferenceItems = migratedProj.Items.Where(i => i.ItemType == "ProjectReference");
migratedProjectReferenceItems.Should().HaveCount(expectedMigratedReferences.Length);
migratedProjectReferenceItems.Select(m => m.Include).Should().BeEquivalentTo(expectedMigratedReferences);
}
[Fact]
public void It_migrates_csproj_ProjectReference_in_xproj_including_condition_on_ProjectReference()
{
var projectReference = "some/to.csproj";
var xproj = ProjectRootElement.Create();
var csprojReferenceItem = xproj.AddItem("ProjectReference", projectReference);
csprojReferenceItem.Condition = " '$(Foo)' == 'bar' ";
var projectReferenceName = Path.GetFileNameWithoutExtension(projectReference);
var projectJson = @"
{
""dependencies"": {" +
$"\"{projectReferenceName}\"" + @": {
""target"" : ""project""
}
}
}
";
var testDirectory = Temp.CreateDirectory().Path;
var migratedProj = TemporaryProjectFileRuleRunner.RunRules(new IMigrationRule[]
{
new MigrateProjectDependenciesRule()
}, projectJson, testDirectory, xproj);
var migratedProjectReferenceItems = migratedProj.Items.Where(i => i.ItemType == "ProjectReference");
migratedProjectReferenceItems.Should().HaveCount(1);
var migratedProjectReferenceItem = migratedProjectReferenceItems.First();
migratedProjectReferenceItem.Include.Should().Be(projectReference);
migratedProjectReferenceItem.Condition.Should().Be(" '$(Foo)' == 'bar' ");
}
[Fact]
public void It_migrates_csproj_ProjectReference_in_xproj_including_condition_on_ProjectReference_parent()
{
var projectReference = "some/to.csproj";
var xproj = ProjectRootElement.Create();
var csprojReferenceItem = xproj.AddItem("ProjectReference", projectReference);
csprojReferenceItem.Parent.Condition = " '$(Foo)' == 'bar' ";
var projectReferenceName = Path.GetFileNameWithoutExtension(projectReference);
var projectJson = @"
{
""dependencies"": {" +
$"\"{projectReferenceName}\"" + @": {
""target"" : ""project""
}
}
}
";
var testDirectory = Temp.CreateDirectory().Path;
var migratedProj = TemporaryProjectFileRuleRunner.RunRules(new IMigrationRule[]
{
new MigrateProjectDependenciesRule()
}, projectJson, testDirectory, xproj);
var migratedProjectReferenceItems = migratedProj.Items.Where(i => i.ItemType == "ProjectReference");
migratedProjectReferenceItems.Should().HaveCount(1);
var migratedProjectReferenceItem = migratedProjectReferenceItems.First();
migratedProjectReferenceItem.Include.Should().Be(projectReference);
migratedProjectReferenceItem.Condition.Should().Be(" '$(Foo)' == 'bar' ");
}
[Fact]
public void It_migrates_csproj_ProjectReference_in_xproj_including_condition_on_ProjectReference_parent_and_item()
{
var projectReference = "some/to.csproj";
var xproj = ProjectRootElement.Create();
var csprojReferenceItem = xproj.AddItem("ProjectReference", projectReference);
csprojReferenceItem.Parent.Condition = " '$(Foo)' == 'bar' ";
csprojReferenceItem.Condition = " '$(Bar)' == 'foo' ";
var projectReferenceName = Path.GetFileNameWithoutExtension(projectReference);
var projectJson = @"
{
""dependencies"": {" +
$"\"{projectReferenceName}\"" + @": {
""target"" : ""project""
}
}
}
";
var testDirectory = Temp.CreateDirectory().Path;
var migratedProj = TemporaryProjectFileRuleRunner.RunRules(new IMigrationRule[]
{
new MigrateProjectDependenciesRule()
}, projectJson, testDirectory, xproj);
var migratedProjectReferenceItems = migratedProj.Items.Where(i => i.ItemType == "ProjectReference");
migratedProjectReferenceItems.Should().HaveCount(1);
var migratedProjectReferenceItem = migratedProjectReferenceItems.First();
migratedProjectReferenceItem.Include.Should().Be(projectReference);
migratedProjectReferenceItem.Condition.Should().Be(" '$(Bar)' == 'foo' and '$(Foo)' == 'bar' ");
}
}
}

View file

@ -25,7 +25,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
[Fact]
public void RuntimeOptions_are_copied_from_projectJson_to_runtimeconfig_template_json_file()
{
var testInstance = TestAssetsManager.CreateTestInstance("TestAppWithRuntimeOptions").WithLockFiles();
var testInstance = TestAssetsManager.CreateTestInstance("TestAppWithRuntimeOptions");
var projectDir = testInstance.Path;
var projectPath = Path.Combine(testInstance.Path, "project.json");
@ -49,7 +49,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
[Fact]
public void Migrating_ProjectJson_with_no_RuntimeOptions_produces_no_runtimeconfig_template_json_file()
{
var testInstance = TestAssetsManager.CreateTestInstance("TestAppSimple").WithLockFiles();
var testInstance = TestAssetsManager.CreateTestInstance("TestAppSimple");
var projectDir = testInstance.Path;
var projectContext = ProjectContext.Create(projectDir, FrameworkConstants.CommonFrameworks.NetCoreApp10);

View file

@ -13,7 +13,7 @@ using Microsoft.DotNet.ProjectJsonMigration.Rules;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
public class GivenThatIWantToMigrateProjectFramework : TestBase
public class GivenThatIWantToMigrateTFMs : TestBase
{
[Fact(Skip="Emitting this until x-targetting full support is in")]
public void Migrating_netcoreapp_project_Does_not_populate_TargetFrameworkIdentifier_and_TargetFrameworkVersion()
@ -43,6 +43,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
mockProj.Properties.Count(p => p.Name == "TargetFrameworkVersion").Should().Be(0);
}
[Fact]
public void Migrating_MultiTFM_project_Populates_TargetFrameworks_with_short_tfms()
{
var testDirectory = Temp.CreateDirectory().Path;
@ -50,12 +51,12 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
.FromTestAssetBase("TestLibraryWithMultipleFrameworks")
.SaveToDisk(testDirectory);
var projectContext = ProjectContext.Create(testDirectory, FrameworkConstants.CommonFrameworks.NetCoreApp10);
var projectContexts = ProjectContext.CreateContextForEachFramework(testDirectory);
var mockProj = ProjectRootElement.Create();
var migrationSettings = new MigrationSettings(testDirectory, testDirectory, "1.0.0", mockProj);
var migrationInputs = new MigrationRuleInputs(
new[] { projectContext },
projectContexts,
mockProj,
mockProj.AddItemGroup(),
mockProj.AddPropertyGroup());
@ -67,7 +68,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
.Value.Should().Be("net20;net35;net40;net461;netstandard1.5");
}
public void Migrating_Single_TFM_project_Populates_TargetFrameworks_with_short_tfm()
[Fact]
public void Migrating_Single_TFM_project_does_not_Populate_TargetFrameworks()
{
var testDirectory = Temp.CreateDirectory().Path;
var testPJ = new ProjectJsonBuilder(TestAssetsManager)
@ -78,21 +80,21 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
})
.SaveToDisk(testDirectory);
var projectContext = ProjectContext.Create(testDirectory, FrameworkConstants.CommonFrameworks.NetCoreApp10);
var projectContexts = ProjectContext.CreateContextForEachFramework(testDirectory);
var mockProj = ProjectRootElement.Create();
// Run BuildOptionsRule
var migrationSettings = new MigrationSettings(testDirectory, testDirectory, "1.0.0", mockProj);
var migrationInputs = new MigrationRuleInputs(
new[] { projectContext },
projectContexts,
mockProj,
mockProj.AddItemGroup(),
mockProj.AddPropertyGroup());
new MigrateTFMRule().Apply(migrationSettings, migrationInputs);
Console.WriteLine(mockProj.RawXml);
mockProj.Properties.Count(p => p.Name == "TargetFrameworks").Should().Be(1);
mockProj.Properties.First(p => p.Name == "TargetFrameworks").Value.Should().Be("netcoreapp1.0");
mockProj.Properties.Count(p => p.Name == "TargetFrameworks").Should().Be(0);
}
}
}

View file

@ -9,10 +9,10 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
public class TemporaryProjectFileRuleRunner
{
public static ProjectRootElement RunRules(IEnumerable<IMigrationRule> rules, string projectJson,
string testDirectory)
string testDirectory, ProjectRootElement xproj=null)
{
var projectContext = GenerateProjectContextFromString(testDirectory, projectJson);
return RunMigrationRulesOnGeneratedProject(rules, projectContext, testDirectory);
return RunMigrationRulesOnGeneratedProject(rules, projectContext, testDirectory, xproj);
}
private static ProjectContext GenerateProjectContextFromString(string projectDirectory, string json)
@ -25,13 +25,14 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
}
private static ProjectRootElement RunMigrationRulesOnGeneratedProject(IEnumerable<IMigrationRule> rules,
ProjectContext projectContext, string testDirectory)
ProjectContext projectContext, string testDirectory, ProjectRootElement xproj)
{
var project = ProjectRootElement.Create();
var testSettings = new MigrationSettings(testDirectory, testDirectory, "1.0.0", project);
var testInputs = new MigrationRuleInputs(new[] {projectContext}, project,
project.AddItemGroup(),
project.AddPropertyGroup());
project.AddPropertyGroup(),
xproj);
foreach (var rule in rules)
{

View file

@ -142,6 +142,9 @@ namespace Microsoft.DotNet.Migration.Tests
var projectJsonBuildOutputs = new HashSet<string>(CollectBuildOutputs(projectDirectory));
CleanBinObj(projectDirectory);
// Remove lock file for migration
File.Delete(Path.Combine(projectDirectory, "project.lock.json"));
MigrateProject(projectDirectory);
Restore(projectDirectory);
BuildMSBuild(projectDirectory);