Merge pull request #4074 from brthor/brthor/migrate-reviewable

ProjectJson to  CSProj Migration
This commit is contained in:
Bryan Thornbury 2016-08-30 13:36:07 -07:00 committed by GitHub
commit dc59da7ce3
76 changed files with 4996 additions and 32 deletions

View file

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
VisualStudioVersion = 14.0.25123.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED2FE3E2-F7E7-4389-8231-B65123F2076F}"
EndProject
@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{17735A9D-B
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{0722D325-24C8-4E83-B5AF-0A083E7F0749}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MultiProjectValidator", "tools\MultiProjectValidator\MultiProjectValidator.xproj", "{08A68C6A-86F6-4ED2-89A7-B166D33E9F85}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "EndToEnd", "test\EndToEnd\EndToEnd.xproj", "{65741CB1-8AEE-4C66-8198-10A7EA0E4258}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestProjects", "TestProjects", "{713CBFBB-5392-438D-B766-A9A585EF1BB8}"
@ -152,7 +154,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "publish", "publish", "{27B1
build\publish\PublishContent.targets = build\publish\PublishContent.targets
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Tools.Test", "src\Microsoft.DotNet.Tools.Test\Microsoft.DotNet.Tools.Test.xproj", "{6D028154-5518-4A56-BAD6-938A90E5BCF6}"
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.ProjectJsonMigration", "src\Microsoft.DotNet.ProjectJsonMigration\Microsoft.DotNet.ProjectJsonMigration.xproj", "{0E083818-2320-4388-8007-4F720FD5C634}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.ProjectJsonMigration.Tests", "test\Microsoft.DotNet.ProjectJsonMigration.Tests\Microsoft.DotNet.ProjectJsonMigration.Tests.xproj", "{1F2EF070-AC5F-4078-AFB0-65745AC691B9}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "dotnet-migrate.Tests", "test\dotnet-migrate.Tests\dotnet-migrate.Tests.xproj", "{1F2EF070-AC5F-4078-AFB0-65745AC691B9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -246,6 +252,22 @@ Global
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Debug|x64.ActiveCfg = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Debug|x64.Build.0 = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Release|Any CPU.ActiveCfg = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Release|Any CPU.Build.0 = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Release|x64.ActiveCfg = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.Release|x64.Build.0 = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{688870C8-9843-4F9E-8576-D39290AD0F25}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{65741CB1-8AEE-4C66-8198-10A7EA0E4258}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65741CB1-8AEE-4C66-8198-10A7EA0E4258}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65741CB1-8AEE-4C66-8198-10A7EA0E4258}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -838,6 +860,55 @@ Global
{E4F46EAB-B5A5-4E60-9B9D-40A1FADBF45C}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{E4F46EAB-B5A5-4E60-9B9D-40A1FADBF45C}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{E4F46EAB-B5A5-4E60-9B9D-40A1FADBF45C}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.Debug|x64.ActiveCfg = Debug|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.Debug|x64.Build.0 = Debug|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.Release|Any CPU.Build.0 = Release|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.Release|x64.ActiveCfg = Release|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.Release|x64.Build.0 = Release|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{0E083818-2320-4388-8007-4F720FD5C634}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E083818-2320-4388-8007-4F720FD5C634}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E083818-2320-4388-8007-4F720FD5C634}.Debug|x64.ActiveCfg = Debug|x64
{0E083818-2320-4388-8007-4F720FD5C634}.Debug|x64.Build.0 = Debug|x64
{0E083818-2320-4388-8007-4F720FD5C634}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|Any CPU
{0E083818-2320-4388-8007-4F720FD5C634}.MinSizeRel|Any CPU.Build.0 = MinSizeRel|Any CPU
{0E083818-2320-4388-8007-4F720FD5C634}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{0E083818-2320-4388-8007-4F720FD5C634}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{0E083818-2320-4388-8007-4F720FD5C634}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E083818-2320-4388-8007-4F720FD5C634}.Release|Any CPU.Build.0 = Release|Any CPU
{0E083818-2320-4388-8007-4F720FD5C634}.Release|x64.ActiveCfg = Release|x64
{0E083818-2320-4388-8007-4F720FD5C634}.Release|x64.Build.0 = Release|x64
{0E083818-2320-4388-8007-4F720FD5C634}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|Any CPU
{0E083818-2320-4388-8007-4F720FD5C634}.RelWithDebInfo|Any CPU.Build.0 = RelWithDebInfo|Any CPU
{0E083818-2320-4388-8007-4F720FD5C634}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{0E083818-2320-4388-8007-4F720FD5C634}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.Debug|x64.ActiveCfg = Debug|x64
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.Debug|x64.Build.0 = Debug|x64
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.MinSizeRel|Any CPU.ActiveCfg = MinSizeRel|Any CPU
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.MinSizeRel|Any CPU.Build.0 = MinSizeRel|Any CPU
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.Release|Any CPU.Build.0 = Release|Any CPU
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.Release|x64.ActiveCfg = Release|x64
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.Release|x64.Build.0 = Release|x64
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.RelWithDebInfo|Any CPU.ActiveCfg = RelWithDebInfo|Any CPU
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.RelWithDebInfo|Any CPU.Build.0 = RelWithDebInfo|Any CPU
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{1F2EF070-AC5F-4078-AFB0-65745AC691B9}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{6D028154-5518-4A56-BAD6-938A90E5BCF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6D028154-5518-4A56-BAD6-938A90E5BCF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D028154-5518-4A56-BAD6-938A90E5BCF6}.Debug|x64.ActiveCfg = Debug|x64
@ -864,6 +935,7 @@ Global
{A16958E1-24C7-4F1E-B317-204AD91625DD} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{BD7833F8-3209-4682-BF75-B4BCA883E279} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{DCDFE282-03DE-4DBC-B90C-CC3CE3EC8162} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{688870C8-9843-4F9E-8576-D39290AD0F25} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{65741CB1-8AEE-4C66-8198-10A7EA0E4258} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{713CBFBB-5392-438D-B766-A9A585EF1BB8} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{60CF7E6C-D6C8-439D-B7B7-D8A27E29BE2C} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
@ -911,5 +983,8 @@ Global
{E4F46EAB-B5A5-4E60-9B9D-40A1FADBF45C} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{27B12960-ABB0-4903-9C60-5E9157E659C8} = {89905EC4-BC0F-443B-8ADF-691321F10108}
{6D028154-5518-4A56-BAD6-938A90E5BCF6} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{EDD6C92D-0A58-4FCB-A0E9-9D0FFC045177} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
{0E083818-2320-4388-8007-4F720FD5C634} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F}
{1F2EF070-AC5F-4078-AFB0-65745AC691B9} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7}
EndGlobalSection
EndGlobal

View file

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>58808bbc-371e-47d6-a3d0-4902145eda4e</ProjectGuid>
<RootNamespace>TestApp</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

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.
using System;
using System.Diagnostics;
namespace TestApp
{
public class Program
{
public static int Main(string[] args)
{
Console.WriteLine("Hello World");
return 0;
}
}
}

View file

@ -0,0 +1,2 @@
@echo off
echo %*

View file

@ -0,0 +1,2 @@
#!/usr/bin/env sh
echo $@

View file

@ -0,0 +1,29 @@
{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true
},
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0",
}
},
"frameworks": {
"netcoreapp1.0": {}
},
"scripts": {
"prepublish": [
"echoscript prepublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:FullTargetFramework%?"
],
"postpublish": [
"echoscript postpublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:FullTargetFramework%?"
],
"precompile": [
"echoscript precompile_output ?%compile:Configuration%? ?%compile:OutputDir%? ?%compile:FullTargetFramework%?"
],
"postcompile": [
"echoscript postcompile_output ?%compile:Configuration%? ?%compile:OutputDir%? ?%compile:FullTargetFramework%?"
]
}
}

View file

@ -0,0 +1,12 @@
using System;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}

View file

@ -0,0 +1,26 @@
{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true
},
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0",
"type": "platform"
}
},
"frameworks": {
"netcoreapp1.0": {}
},
"runtimeOptions": {
"somethingString": "anything",
"somethingBoolean": true,
"someArray": [
"one",
"two"
],
"someObject": {
"someProperty": "someValue"
}
}
}

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectModel;
using System.IO;
using Newtonsoft.Json.Linq;
using System.Text;
namespace Microsoft.DotNet.ProjectJsonMigration
{
public class ConstantPackageNames
{
public const string CSdkPackageName = "Microsoft.DotNet.Core.Sdk";
}
}

View file

@ -0,0 +1,42 @@
// 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;
using System.Linq;
using System.IO;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
using Newtonsoft.Json;
namespace Microsoft.DotNet.ProjectJsonMigration
{
public class DefaultMigrationRuleSet : IMigrationRule
{
private IMigrationRule[] Rules => new IMigrationRule[]
{
new MigrateRootOptionsRule(),
new MigrateTFMRule(),
new MigrateBuildOptionsRule(),
new MigrateRuntimeOptionsRule(),
new MigratePublishOptionsRule(),
new MigrateProjectDependenciesRule(),
new MigrateConfigurationsRule(),
new MigrateScriptsRule(),
new TemporaryMutateProjectJsonRule(),
new SaveOutputProjectRule()
};
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
foreach (var rule in Rules)
{
MigrationTrace.Instance.WriteLine($"{nameof(DefaultMigrationRuleSet)}: Executing migration rule {rule.GetType().Name}");
rule.Apply(migrationSettings, migrationRuleInputs);
}
}
}
}

View file

@ -0,0 +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 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 static class MSBuildExtensions
{
public static IEnumerable<ProjectPropertyElement> PropertiesWithoutConditions(
this ProjectRootElement projectRoot)
{
return ElementsWithoutConditions(projectRoot.Properties);
}
public static IEnumerable<ProjectItemElement> ItemsWithoutConditions(
this ProjectRootElement projectRoot)
{
return ElementsWithoutConditions(projectRoot.Items);
}
public static IEnumerable<string> Includes(
this ProjectItemElement item)
{
return SplitSemicolonDelimitedValues(item.Include);
}
public static IEnumerable<string> Excludes(
this ProjectItemElement item)
{
return SplitSemicolonDelimitedValues(item.Exclude);
}
public static IEnumerable<string> AllConditions(this ProjectElement projectElement)
{
return new string[] { projectElement.Condition }.Concat(projectElement.AllParents.Select(p=> p.Condition));
}
public static IEnumerable<string> IntersectIncludes(this ProjectItemElement item, ProjectItemElement otherItem)
{
return item.Includes().Intersect(otherItem.Includes());
}
public static void RemoveIncludes(this ProjectItemElement item, IEnumerable<string> includesToRemove)
{
item.Include = string.Join(";", item.Includes().Except(includesToRemove));
}
public static void UnionIncludes(this ProjectItemElement item, IEnumerable<string> includesToAdd)
{
item.Include = string.Join(";", item.Includes().Union(includesToAdd));
}
public static void UnionExcludes(this ProjectItemElement item, IEnumerable<string> excludesToAdd)
{
item.Exclude = string.Join(";", item.Excludes().Union(excludesToAdd));
}
public static ProjectMetadataElement GetMetadataWithName(this ProjectItemElement item, string name)
{
return item.Metadata.FirstOrDefault(m => m.Name.Equals(name, StringComparison.Ordinal));
}
public static bool ValueEquals(this ProjectMetadataElement metadata, ProjectMetadataElement otherMetadata)
{
return metadata.Value.Equals(otherMetadata.Value, StringComparison.Ordinal);
}
public static void AddMetadata(this ProjectItemElement item, ICollection<ProjectMetadataElement> metadataElements)
{
foreach (var metadata in metadataElements)
{
item.AddMetadata(metadata);
}
}
public static void RemoveIfEmpty(this ProjectElementContainer container)
{
if (!container.Children.Any())
{
container.Parent.RemoveChild(container);
}
}
public static void AddMetadata(this ProjectItemElement item, ProjectMetadataElement metadata)
{
var existingMetadata = item.GetMetadataWithName(metadata.Name);
if (existingMetadata != default(ProjectMetadataElement) && !existingMetadata.ValueEquals(metadata))
{
throw new Exception("Cannot merge metadata with the same name and different values");
}
if (existingMetadata == default(ProjectMetadataElement))
{
MigrationTrace.Instance.WriteLine($"{nameof(AddMetadata)}: Adding metadata to {item.ItemType} item: {{ {metadata.Name}, {metadata.Value} }}");
item.AddMetadata(metadata.Name, metadata.Value);
}
}
private static IEnumerable<string> SplitSemicolonDelimitedValues(string combinedValue)
{
return string.IsNullOrEmpty(combinedValue) ? Enumerable.Empty<string>() : combinedValue.Split(';');
}
private static IEnumerable<T> ElementsWithoutConditions<T>(IEnumerable<T> elements) where T : ProjectElement
{
return elements
.Where(e => string.IsNullOrEmpty(e.Condition)
&& e.AllParents.All(parent => string.IsNullOrEmpty(parent.Condition)));
}
}
}

View file

@ -4,16 +4,15 @@
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>947dd232-8d9b-4b78-9c6a-94f807d2dd58</ProjectGuid>
<RootNamespace>TestLibrary</RootNamespace>
<ProjectGuid>0E083818-2320-4388-8007-4F720FD5C634</ProjectGuid>
<RootNamespace>Microsoft.DotNet.ProjectJsonMigration</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View file

@ -0,0 +1,30 @@
using System;
namespace Microsoft.DotNet.ProjectJsonMigration
{
public class MigrationError
{
public string ErrorCode { get; }
public string GeneralErrorReason { get; }
public string Message { get; }
public MigrationError(string errorCode, string generalErrorReason, string message)
{
ErrorCode = errorCode;
GeneralErrorReason = generalErrorReason;
Message = message;
}
public void Throw()
{
throw new Exception(GetFormattedErrorMessage());
}
public string GetFormattedErrorMessage()
{
return $"{ErrorCode}::{GeneralErrorReason}: {Message}";
}
}
}

View file

@ -0,0 +1,35 @@
using System;
namespace Microsoft.DotNet.ProjectJsonMigration
{
public static partial class MigrationErrorCodes
{
public static Func<string, MigrationError> MIGRATE1011
=> (message) => new MigrationError(nameof(MIGRATE1011), "Deprecated Project", message);
public static Func<string, MigrationError> MIGRATE1012
=> (message) => new MigrationError(nameof(MIGRATE1012), "Project not Restored", message);
public static Func<string, MigrationError> MIGRATE1013
=> (message) => new MigrationError(nameof(MIGRATE1013), "No Project", message);
public static Func<string, MigrationError> MIGRATE1014
=> (message) => new MigrationError(nameof(MIGRATE1014), "Unresolved Dependency", message);
public static Func<string, MigrationError> MIGRATE1015
=> (message) => new MigrationError(nameof(MIGRATE1015), "File Overwrite", message);
public static Func<string, MigrationError> MIGRATE1016
=> (message) => new MigrationError(nameof(MIGRATE1016), "Unsupported Script Variable", message);
// Potentially Temporary (Point in Time) Errors
public static Func<string, MigrationError> MIGRATE20011
=> (message) => new MigrationError(nameof(MIGRATE20011), "Multi-TFM", message);
public static Func<string, MigrationError> MIGRATE20012
=> (message) => new MigrationError(nameof(MIGRATE20012), "Configuration Exclude", message);
public static Func<string, MigrationError> MIGRATE20013
=> (message) => new MigrationError(nameof(MIGRATE20013), "Non-Csharp App", message);
}
}

View file

@ -0,0 +1,46 @@
// 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 OutputMSBuildProject { get; }
public ProjectItemGroupElement CommonItemGroup { get; }
public ProjectPropertyGroupElement CommonPropertyGroup { get; }
public IEnumerable<ProjectContext> ProjectContexts { get; }
public ProjectContext DefaultProjectContext
{
get
{
return ProjectContexts.First();
}
}
public MigrationRuleInputs(
IEnumerable<ProjectContext> projectContexts,
ProjectRootElement outputMSBuildProject,
ProjectItemGroupElement commonItemGroup,
ProjectPropertyGroupElement commonPropertyGroup)
{
ProjectContexts = projectContexts;
OutputMSBuildProject = outputMSBuildProject;
CommonItemGroup = commonItemGroup;
CommonPropertyGroup = commonPropertyGroup;
}
}
}

View file

@ -0,0 +1,35 @@
// 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 ProjectDirectory { get; }
public string OutputDirectory { get; }
public string SdkPackageVersion { get; }
public ProjectRootElement MSBuildProjectTemplate { get; }
public MigrationSettings(
string projectDirectory,
string outputDirectory,
string sdkPackageVersion,
ProjectRootElement msBuildProjectTemplate)
{
ProjectDirectory = projectDirectory;
OutputDirectory = outputDirectory;
SdkPackageVersion = sdkPackageVersion;
MSBuildProjectTemplate = msBuildProjectTemplate;
}
}
}

View file

@ -0,0 +1,37 @@
using System;
using System.Text.RegularExpressions;
namespace Microsoft.DotNet.ProjectJsonMigration
{
public class MigrationTrace
{
public static MigrationTrace Instance { get; set; }
static MigrationTrace ()
{
Instance = new MigrationTrace();
}
public string EnableEnvironmentVariable => "DOTNET_MIGRATION_TRACE";
public bool IsEnabled
{
get
{
#if DEBUG
return true;
#else
return Environment.GetEnvironmentVariable(EnableEnvironmentVariable) != null;
#endif
}
}
public void WriteLine(string message)
{
if (IsEnabled)
{
Console.WriteLine(message);
}
}
}
}

View file

@ -0,0 +1,31 @@
// 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
{
public class ItemMetadataValue<T>
{
public string MetadataName { get; }
private readonly string _metadataValue;
private readonly Func<T, string> _metadataValueFunc;
public ItemMetadataValue(string metadataName, string metadataValue)
{
MetadataName = metadataName;
_metadataValue = metadataValue;
}
public ItemMetadataValue(string metadataName, Func<T, string> metadataValueFunc)
{
MetadataName = metadataName;
_metadataValueFunc = metadataValueFunc;
}
public string GetMetadataValue(T source)
{
return _metadataValue ?? _metadataValueFunc(source);
}
}
}

View file

@ -0,0 +1,140 @@
// 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;
using System.Linq;
using System.IO;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
using Microsoft.DotNet.Tools.Common;
using Newtonsoft.Json.Linq;
namespace Microsoft.DotNet.ProjectJsonMigration
{
public class ProjectMigrator
{
// TODO: Migrate PackOptions
// TODO: Support Mappings in IncludeContext Transformations
// TODO: Migrate Multi-TFM projects
// TODO: Tests
// TODO: Out of Scope
// - Globs that resolve to directories: /some/path/**/somedir
// - Migrating Deprecated project.jsons
// - Configuration dependent source exclusion
private readonly IMigrationRule _ruleSet;
public ProjectMigrator() : this(new DefaultMigrationRuleSet()) { }
public ProjectMigrator(IMigrationRule ruleSet)
{
_ruleSet = ruleSet;
}
public void Migrate(MigrationSettings migrationSettings)
{
var migrationRuleInputs = ComputeMigrationRuleInputs(migrationSettings);
VerifyInputs(migrationRuleInputs, migrationSettings);
SetupOutputDirectory(migrationSettings.ProjectDirectory, migrationSettings.OutputDirectory);
_ruleSet.Apply(migrationSettings, migrationRuleInputs);
}
private MigrationRuleInputs ComputeMigrationRuleInputs(MigrationSettings migrationSettings)
{
var projectContexts = ProjectContext.CreateContextForEachFramework(migrationSettings.ProjectDirectory);
var templateMSBuildProject = migrationSettings.MSBuildProjectTemplate;
if (templateMSBuildProject == null)
{
throw new Exception("Expected non-null MSBuildProjectTemplate in MigrationSettings");
}
var propertyGroup = templateMSBuildProject.AddPropertyGroup();
var itemGroup = templateMSBuildProject.AddItemGroup();
return new MigrationRuleInputs(projectContexts, templateMSBuildProject, itemGroup, propertyGroup);
}
private void VerifyInputs(MigrationRuleInputs migrationRuleInputs, MigrationSettings migrationSettings)
{
VerifyProject(migrationRuleInputs.ProjectContexts, migrationSettings.ProjectDirectory);
}
private void VerifyProject(IEnumerable<ProjectContext> projectContexts, string projectDirectory)
{
if (projectContexts.Count() > 1)
{
MigrationErrorCodes.MIGRATE20011($"Multi-TFM projects currently not supported.").Throw();
}
if (!projectContexts.Any())
{
MigrationErrorCodes.MIGRATE1013($"No projects found in {projectDirectory}").Throw();
}
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())
{
MigrationErrorCodes.MIGRATE1011(
$"{projectDirectory}{Environment.NewLine}{string.Join(Environment.NewLine, diagnostics.Select(d => d.Message))}")
.Throw();
}
var compilerName =
defaultProjectContext.ProjectFile.GetCompilerOptions(defaultProjectContext.TargetFramework, "_")
.CompilerName;
if (!compilerName.Equals("csc", StringComparison.OrdinalIgnoreCase))
{
MigrationErrorCodes.MIGRATE20013(
$"Cannot migrate project {defaultProjectContext.ProjectFile.ProjectFilePath} using compiler {compilerName}").Throw();
}
}
private void SetupOutputDirectory(string projectDirectory, string outputDirectory)
{
if (!Directory.Exists(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
}
if (projectDirectory != outputDirectory)
{
CopyProjectToOutputDirectory(projectDirectory, outputDirectory);
}
}
private void CopyProjectToOutputDirectory(string projectDirectory, string outputDirectory)
{
var sourceFilePaths = Directory.EnumerateFiles(projectDirectory, "*", SearchOption.AllDirectories);
foreach (var sourceFilePath in sourceFilePaths)
{
var relativeFilePath = PathUtility.GetRelativePath(projectDirectory, sourceFilePath);
var destinationFilePath = Path.Combine(outputDirectory, relativeFilePath);
var destinationDirectory = Path.GetDirectoryName(destinationFilePath);
if (!Directory.Exists(destinationDirectory))
{
Directory.CreateDirectory(destinationDirectory);
}
File.Copy(sourceFilePath, destinationFilePath);
}
}
}
}

View file

@ -0,0 +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.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.DotNet.ProjectJsonMigration.Tests")]

View file

@ -0,0 +1,10 @@
// 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.Rules
{
public interface IMigrationRule
{
void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs);
}
}

View file

@ -0,0 +1,402 @@
// 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;
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
=> new []
{
new AddPropertyTransform<CommonCompilerOptions>("OutputType", "Exe",
compilerOptions => compilerOptions.EmitEntryPoint != null && compilerOptions.EmitEntryPoint.Value),
new AddPropertyTransform<CommonCompilerOptions>("OutputType", "Library",
compilerOptions => compilerOptions.EmitEntryPoint == null || !compilerOptions.EmitEntryPoint.Value)
};
private AddPropertyTransform<CommonCompilerOptions>[] KeyFileTransforms
=> new []
{
new AddPropertyTransform<CommonCompilerOptions>("AssemblyOriginatorKeyFile",
compilerOptions => compilerOptions.KeyFile,
compilerOptions => !string.IsNullOrEmpty(compilerOptions.KeyFile)),
new AddPropertyTransform<CommonCompilerOptions>("SignAssembly",
"true",
compilerOptions => !string.IsNullOrEmpty(compilerOptions.KeyFile))
};
private AddPropertyTransform<CommonCompilerOptions> DefineTransform => new AddPropertyTransform<CommonCompilerOptions>(
"DefineConstants",
compilerOptions => string.Join(";", compilerOptions.Defines),
compilerOptions => compilerOptions.Defines != null && compilerOptions.Defines.Any());
private AddPropertyTransform<CommonCompilerOptions> NoWarnTransform => new AddPropertyTransform<CommonCompilerOptions>(
"NoWarn",
compilerOptions => string.Join(";", compilerOptions.SuppressWarnings),
compilerOptions => compilerOptions.SuppressWarnings != null && compilerOptions.SuppressWarnings.Any());
private AddPropertyTransform<CommonCompilerOptions> PreserveCompilationContextTransform =>
new AddPropertyTransform<CommonCompilerOptions>("PreserveCompilationContext",
compilerOptions => compilerOptions.PreserveCompilationContext.ToString().ToLower(),
compilerOptions => compilerOptions.PreserveCompilationContext != null && compilerOptions.PreserveCompilationContext.Value);
private AddPropertyTransform<CommonCompilerOptions> WarningsAsErrorsTransform =>
new AddPropertyTransform<CommonCompilerOptions>("WarningsAsErrors",
compilerOptions => compilerOptions.WarningsAsErrors.ToString().ToLower(),
compilerOptions => compilerOptions.WarningsAsErrors != null && compilerOptions.WarningsAsErrors.Value);
private AddPropertyTransform<CommonCompilerOptions> AllowUnsafeTransform =>
new AddPropertyTransform<CommonCompilerOptions>("AllowUnsafeBlocks",
compilerOptions => compilerOptions.AllowUnsafe.ToString().ToLower(),
compilerOptions => compilerOptions.AllowUnsafe != null && compilerOptions.AllowUnsafe.Value);
private AddPropertyTransform<CommonCompilerOptions> OptimizeTransform =>
new AddPropertyTransform<CommonCompilerOptions>("Optimize",
compilerOptions => compilerOptions.Optimize.ToString().ToLower(),
compilerOptions => compilerOptions.Optimize != null && compilerOptions.Optimize.Value);
private AddPropertyTransform<CommonCompilerOptions> PlatformTransform =>
new AddPropertyTransform<CommonCompilerOptions>("PlatformTarget",
compilerOptions => compilerOptions.Platform,
compilerOptions => !string.IsNullOrEmpty(compilerOptions.Platform));
private AddPropertyTransform<CommonCompilerOptions> LanguageVersionTransform =>
new AddPropertyTransform<CommonCompilerOptions>("LangVersion",
compilerOptions => compilerOptions.LanguageVersion,
compilerOptions => !string.IsNullOrEmpty(compilerOptions.LanguageVersion));
private AddPropertyTransform<CommonCompilerOptions> DelaySignTransform =>
new AddPropertyTransform<CommonCompilerOptions>("DelaySign",
compilerOptions => compilerOptions.DelaySign.ToString().ToLower(),
compilerOptions => compilerOptions.DelaySign != null && compilerOptions.DelaySign.Value);
private AddPropertyTransform<CommonCompilerOptions> PublicSignTransform =>
new AddPropertyTransform<CommonCompilerOptions>("PublicSign",
compilerOptions => compilerOptions.PublicSign.ToString().ToLower(),
compilerOptions => compilerOptions.PublicSign != null && compilerOptions.PublicSign.Value);
private AddPropertyTransform<CommonCompilerOptions> DebugTypeTransform =>
new AddPropertyTransform<CommonCompilerOptions>("DebugType",
compilerOptions => compilerOptions.DebugType,
compilerOptions => !string.IsNullOrEmpty(compilerOptions.DebugType));
private AddPropertyTransform<CommonCompilerOptions> XmlDocTransform =>
new AddPropertyTransform<CommonCompilerOptions>("GenerateDocumentationFile",
compilerOptions => compilerOptions.GenerateXmlDocumentation.ToString().ToLower(),
compilerOptions => compilerOptions.GenerateXmlDocumentation != null && compilerOptions.GenerateXmlDocumentation.Value);
// TODO: https://github.com/dotnet/sdk/issues/67
private AddPropertyTransform<CommonCompilerOptions> XmlDocTransformFilePath =>
new AddPropertyTransform<CommonCompilerOptions>("DocumentationFile",
@"$(OutputPath)\$(AssemblyName).xml",
compilerOptions => compilerOptions.GenerateXmlDocumentation != null && compilerOptions.GenerateXmlDocumentation.Value);
private AddPropertyTransform<CommonCompilerOptions> OutputNameTransform =>
new AddPropertyTransform<CommonCompilerOptions>("AssemblyName",
compilerOptions => compilerOptions.OutputName,
compilerOptions => !string.IsNullOrEmpty(compilerOptions.OutputName));
private IncludeContextTransform CompileFilesTransform =>
new IncludeContextTransform("Compile", transformMappings: false);
private IncludeContextTransform EmbedFilesTransform =>
new IncludeContextTransform("EmbeddedResource", transformMappings: false);
private IncludeContextTransform CopyToOutputFilesTransform =>
new IncludeContextTransform("Content", transformMappings: true)
.WithMetadata("CopyToOutputDirectory", "PreserveNewest");
private Func<CommonCompilerOptions, string, IEnumerable<ProjectItemElement>> CompileFilesTransformExecute =>
(compilerOptions, projectDirectory) =>
CompileFilesTransform.Transform(GetCompileIncludeContext(compilerOptions, projectDirectory));
private Func<CommonCompilerOptions, string, IEnumerable<ProjectItemElement>> EmbedFilesTransformExecute =>
(compilerOptions, projectDirectory) =>
EmbedFilesTransform.Transform(GetEmbedIncludeContext(compilerOptions, projectDirectory));
private Func<CommonCompilerOptions, string, IEnumerable<ProjectItemElement>> CopyToOutputFilesTransformExecute =>
(compilerOptions, projectDirectory) =>
CopyToOutputFilesTransform.Transform(GetCopyToOutputIncludeContext(compilerOptions, projectDirectory));
private readonly string _configuration;
private readonly NuGetFramework _framework;
private readonly ProjectPropertyGroupElement _configurationPropertyGroup;
private readonly ProjectItemGroupElement _configurationItemGroup;
private List<AddPropertyTransform<CommonCompilerOptions>> _propertyTransforms;
private List<Func<CommonCompilerOptions, string, IEnumerable<ProjectItemElement>>> _includeContextTransformExecutes;
private readonly ITransformApplicator _transformApplicator;
public MigrateBuildOptionsRule(ITransformApplicator transformApplicator = null)
{
_transformApplicator = transformApplicator ?? new TransformApplicator();
ConstructTransformLists();
}
public MigrateBuildOptionsRule(
string configuration,
NuGetFramework framework,
ProjectPropertyGroupElement configurationPropertyGroup,
ProjectItemGroupElement configurationItemGroup,
ITransformApplicator transformApplicator = null)
{
_configuration = configuration;
_framework = framework;
_configurationPropertyGroup = configurationPropertyGroup;
_configurationItemGroup = configurationItemGroup;
_transformApplicator = transformApplicator ?? new TransformApplicator();
ConstructTransformLists();
}
private void ConstructTransformLists()
{
_propertyTransforms = new List<AddPropertyTransform<CommonCompilerOptions>>()
{
DefineTransform,
NoWarnTransform,
WarningsAsErrorsTransform,
AllowUnsafeTransform,
OptimizeTransform,
PlatformTransform,
LanguageVersionTransform,
DelaySignTransform,
PublicSignTransform,
DebugTypeTransform,
OutputNameTransform,
XmlDocTransform,
XmlDocTransformFilePath,
PreserveCompilationContextTransform
};
_propertyTransforms.AddRange(EmitEntryPointTransforms);
_propertyTransforms.AddRange(KeyFileTransforms);
_includeContextTransformExecutes = new List<Func<CommonCompilerOptions, string, IEnumerable<ProjectItemElement>>>()
{
CompileFilesTransformExecute,
EmbedFilesTransformExecute,
CopyToOutputFilesTransformExecute
};
}
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
var csproj = migrationRuleInputs.OutputMSBuildProject;
var projectContext = migrationRuleInputs.DefaultProjectContext;
var propertyGroup = _configurationPropertyGroup ?? migrationRuleInputs.CommonPropertyGroup;
var itemGroup = _configurationItemGroup ?? migrationRuleInputs.CommonItemGroup;
var compilerOptions = projectContext.ProjectFile.GetCompilerOptions(projectContext.TargetFramework, null);
var configurationCompilerOptions =
projectContext.ProjectFile.GetCompilerOptions(_framework, _configuration);
// If we're in a configuration, we need to be careful not to overwrite values from BuildOptions
// without a configuration
if (_configuration == null)
{
CleanExistingProperties(csproj);
PerformPropertyAndItemMappings(
compilerOptions,
propertyGroup,
itemGroup,
_transformApplicator,
migrationSettings.ProjectDirectory);
}
else
{
PerformConfigurationPropertyAndItemMappings(
compilerOptions,
configurationCompilerOptions,
propertyGroup,
itemGroup,
_transformApplicator,
migrationSettings.ProjectDirectory);
}
}
private void PerformConfigurationPropertyAndItemMappings(
CommonCompilerOptions compilerOptions,
CommonCompilerOptions configurationCompilerOptions,
ProjectPropertyGroupElement propertyGroup,
ProjectItemGroupElement itemGroup,
ITransformApplicator transformApplicator,
string projectDirectory)
{
foreach (var transform in _propertyTransforms)
{
var nonConfigurationOutput = transform.Transform(compilerOptions);
var configurationOutput = transform.Transform(configurationCompilerOptions);
if (!PropertiesAreEqual(nonConfigurationOutput, configurationOutput))
{
transformApplicator.Execute(configurationOutput, propertyGroup);
}
}
foreach (var includeContextTransformExecute in _includeContextTransformExecutes)
{
var nonConfigurationOutput = includeContextTransformExecute(compilerOptions, projectDirectory);
var configurationOutput = includeContextTransformExecute(configurationCompilerOptions, projectDirectory).ToArray();
if (configurationOutput != null && nonConfigurationOutput != null)
{
// TODO: HACK: this is leaky, see top comments, the throw at least covers the scenario
ThrowIfConfigurationHasAdditionalExcludes(configurationOutput, nonConfigurationOutput);
RemoveCommonIncludes(configurationOutput, nonConfigurationOutput);
configurationOutput = configurationOutput.Where(i => i != null && !string.IsNullOrEmpty(i.Include)).ToArray();
}
// Don't merge with existing items when doing a configuration
transformApplicator.Execute(configurationOutput, itemGroup, mergeExisting: false);
}
}
private void ThrowIfConfigurationHasAdditionalExcludes(IEnumerable<ProjectItemElement> configurationOutput, IEnumerable<ProjectItemElement> nonConfigurationOutput)
{
foreach (var item1 in configurationOutput)
{
if (item1 == null)
{
continue;
}
var item2Excludes = new HashSet<string>();
foreach (var item2 in nonConfigurationOutput)
{
if (item2 != null)
{
item2Excludes.UnionWith(item2.Excludes());
}
}
var configurationHasAdditionalExclude =
item1.Excludes().Any(exclude => item2Excludes.All(item2Exclude => item2Exclude != exclude));
if (configurationHasAdditionalExclude)
{
MigrationTrace.Instance.WriteLine(item1.Exclude);
MigrationTrace.Instance.WriteLine(item2Excludes.ToString());
MigrationErrorCodes.MIGRATE20012("Unable to migrate projects with excluded files in configurations.")
.Throw();
}
}
}
private void RemoveCommonIncludes(IEnumerable<ProjectItemElement> itemsToRemoveFrom,
IEnumerable<ProjectItemElement> otherItems)
{
foreach (var item1 in itemsToRemoveFrom)
{
if (item1 == null)
{
continue;
}
foreach (
var item2 in
otherItems.Where(
i => i != null && string.Equals(i.ItemType, item1.ItemType, StringComparison.Ordinal)))
{
item1.Include = string.Join(";", item1.Includes().Except(item2.Includes()));
}
}
}
private bool PropertiesAreEqual(ProjectPropertyElement nonConfigurationOutput, ProjectPropertyElement configurationOutput)
{
if (configurationOutput != null && nonConfigurationOutput != null)
{
return string.Equals(nonConfigurationOutput.Value, configurationOutput.Value, StringComparison.Ordinal);
}
return configurationOutput == nonConfigurationOutput;
}
private void PerformPropertyAndItemMappings(
CommonCompilerOptions compilerOptions,
ProjectPropertyGroupElement propertyGroup,
ProjectItemGroupElement itemGroup,
ITransformApplicator transformApplicator,
string projectDirectory)
{
foreach (var transform in _propertyTransforms)
{
transformApplicator.Execute(transform.Transform(compilerOptions), propertyGroup);
}
foreach (var includeContextTransformExecute in _includeContextTransformExecutes)
{
transformApplicator.Execute(
includeContextTransformExecute(compilerOptions, projectDirectory),
itemGroup,
mergeExisting: true);
}
}
private void CleanExistingProperties(ProjectRootElement csproj)
{
var existingPropertiesToRemove = new [] {"OutputType", "TargetExt"};
foreach (var propertyName in existingPropertiesToRemove)
{
var properties = csproj.Properties.Where(p => p.Name == propertyName);
foreach (var property in properties)
{
property.Parent.RemoveChild(property);
}
}
}
private IncludeContext GetCompileIncludeContext(CommonCompilerOptions compilerOptions, string projectDirectory)
{
// Defaults from src/Microsoft.DotNet.ProjectModel/ProjectReader.cs #L596
return compilerOptions.CompileInclude ??
new IncludeContext(
projectDirectory,
"compile",
new JObject(),
ProjectFilesCollection.DefaultCompileBuiltInPatterns,
ProjectFilesCollection.DefaultBuiltInExcludePatterns);
}
private IncludeContext GetEmbedIncludeContext(CommonCompilerOptions compilerOptions, string projectDirectory)
{
// Defaults from src/Microsoft.DotNet.ProjectModel/ProjectReader.cs #L602
return compilerOptions.EmbedInclude ??
new IncludeContext(
projectDirectory,
"embed",
new JObject(),
ProjectFilesCollection.DefaultResourcesBuiltInPatterns,
ProjectFilesCollection.DefaultBuiltInExcludePatterns);
}
private IncludeContext GetCopyToOutputIncludeContext(CommonCompilerOptions compilerOptions, string projectDirectory)
{
// Defaults from src/Microsoft.DotNet.ProjectModel/ProjectReader.cs #608
return compilerOptions.CopyToOutputInclude ??
new IncludeContext(
projectDirectory,
"copyToOutput",
new JObject(),
null,
ProjectFilesCollection.DefaultPublishExcludePatterns);
}
}
}

View file

@ -0,0 +1,80 @@
// 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;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
public class MigrateConfigurationsRule : IMigrationRule
{
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
var projectContext = migrationRuleInputs.DefaultProjectContext;
var configurations = projectContext.ProjectFile.GetConfigurations().ToList();
var frameworks = new List<NuGetFramework>();
frameworks.Add(null);
frameworks.AddRange(projectContext.ProjectFile.GetTargetFrameworks().Select(t => t.FrameworkName));
if (!configurations.Any())
{
return;
}
var frameworkConfigurationCombinations = frameworks.SelectMany(f => configurations, Tuple.Create);
foreach (var entry in frameworkConfigurationCombinations)
{
var framework = entry.Item1;
var configuration = entry.Item2;
MigrateConfiguration(configuration, framework, migrationSettings, migrationRuleInputs);
}
}
private void MigrateConfiguration(
string configuration,
NuGetFramework framework,
MigrationSettings migrationSettings,
MigrationRuleInputs migrationRuleInputs)
{
var csproj = migrationRuleInputs.OutputMSBuildProject;
var propertyGroup = CreatePropertyGroupAtEndOfProject(csproj);
var itemGroup = CreateItemGroupAtEndOfProject(csproj);
var configurationCondition = $" '$(Configuration)' == '{configuration}' ";
if (framework != null)
{
configurationCondition +=
$" and '$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion)' == '{framework.DotNetFrameworkName}' ";
}
propertyGroup.Condition = configurationCondition;
itemGroup.Condition = configurationCondition;
new MigrateBuildOptionsRule(configuration, framework, propertyGroup, itemGroup)
.Apply(migrationSettings, migrationRuleInputs);
propertyGroup.RemoveIfEmpty();
itemGroup.RemoveIfEmpty();
}
private ProjectPropertyGroupElement CreatePropertyGroupAtEndOfProject(ProjectRootElement csproj)
{
var propertyGroup = csproj.CreatePropertyGroupElement();
csproj.InsertBeforeChild(propertyGroup, csproj.LastChild);
return propertyGroup;
}
private ProjectItemGroupElement CreateItemGroupAtEndOfProject(ProjectRootElement csproj)
{
var itemGroup = csproj.CreateItemGroupElement();
csproj.InsertBeforeChild(itemGroup, csproj.LastChild);
return itemGroup;
}
}
}

View file

@ -0,0 +1,96 @@
// 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 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;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
public class MigrateProjectDependenciesRule : IMigrationRule
{
private readonly ITransformApplicator _transformApplicator;
private string _projectDirectory;
public MigrateProjectDependenciesRule(ITransformApplicator transformApplicator = null)
{
_transformApplicator = transformApplicator ?? new TransformApplicator();
}
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 projectDependencyTransformResults =
projectExports.Select(projectExport => ProjectDependencyTransform.Transform(projectExport));
if (projectDependencyTransformResults.Any())
{
AddPropertyTransformsToCommonPropertyGroup(migrationRuleInputs.CommonPropertyGroup);
AddProjectDependenciesToNewItemGroup(csproj.AddItemGroup(), projectDependencyTransformResults);
}
}
private void AddProjectDependenciesToNewItemGroup(ProjectItemGroupElement itemGroup, IEnumerable<ProjectItemElement> projectDependencyTransformResults)
{
foreach (var projectDependencyTransformResult in projectDependencyTransformResults)
{
_transformApplicator.Execute(projectDependencyTransformResult, itemGroup);
}
}
private void AddPropertyTransformsToCommonPropertyGroup(ProjectPropertyGroupElement commonPropertyGroup)
{
var propertyTransformResults = new[]
{
AutoUnifyTransform.Transform(true),
DesignTimeAutoUnifyTransform.Transform(true)
};
foreach (var propertyTransformResult in propertyTransformResults)
{
_transformApplicator.Execute(propertyTransformResult, commonPropertyGroup);
}
}
private AddPropertyTransform<bool> AutoUnifyTransform => new AddPropertyTransform<bool>(
"AutoUnify",
"true",
b => true);
private AddPropertyTransform<bool> DesignTimeAutoUnifyTransform => new AddPropertyTransform<bool>(
"DesignTimeAutoUnify",
"true",
b => true);
private AddItemTransform<LibraryExport> ProjectDependencyTransform => new AddItemTransform<LibraryExport>(
"ProjectReference",
export =>
{
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 migratedProjectFileName = Path.GetFileName(projectDir) + ".csproj";
var relativeProjectDir = PathUtility.GetRelativePath(_projectDirectory + "/", projectDir);
return Path.Combine(relativeProjectDir, migratedProjectFileName);
},
export => "",
export => true);
}
}

View file

@ -0,0 +1,39 @@
// 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.Linq;
using Microsoft.DotNet.ProjectJsonMigration.Transforms;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
public class MigratePublishOptionsRule : IMigrationRule
{
private readonly ITransformApplicator _transformApplicator;
public MigratePublishOptionsRule(ITransformApplicator transformApplicator = null)
{
_transformApplicator = transformApplicator ?? new TransformApplicator();
}
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
var csproj = migrationRuleInputs.OutputMSBuildProject;
var projectContext = migrationRuleInputs.DefaultProjectContext;
var transformResult = CopyToOutputFilesTransform.Transform(projectContext.ProjectFile.PublishOptions);
if (transformResult != null && transformResult.Any())
{
var itemGroup = migrationRuleInputs.CommonItemGroup;
_transformApplicator.Execute(
transformResult,
itemGroup,
mergeExisting: true);
}
}
private IncludeContextTransform CopyToOutputFilesTransform =>
new IncludeContextTransform("Content", transformMappings: true)
.WithMetadata("CopyToPublishDirectory", "PreserveNewest");
}
}

View file

@ -0,0 +1,64 @@
// 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.Linq;
using Microsoft.DotNet.ProjectJsonMigration.Transforms;
using Project = Microsoft.DotNet.ProjectModel.Project;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
public class MigrateRootOptionsRule : IMigrationRule
{
private readonly ITransformApplicator _transformApplicator;
private readonly AddPropertyTransform<Project>[] _transforms;
public MigrateRootOptionsRule(ITransformApplicator transformApplicator = null)
{
_transformApplicator = transformApplicator ?? new TransformApplicator();
_transforms = new[]
{
DescriptionTransform,
CopyrightTransform,
TitleTransform,
LanguageTransform,
VersionTransform
};
}
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
var projectContext = migrationRuleInputs.DefaultProjectContext;
var transformResults = _transforms.Select(t => t.Transform(projectContext.ProjectFile)).ToArray();
if (transformResults.Any())
{
var propertyGroup = migrationRuleInputs.CommonPropertyGroup;
foreach (var transformResult in transformResults)
{
_transformApplicator.Execute(transformResult, propertyGroup);
}
}
}
private AddPropertyTransform<Project> DescriptionTransform => new AddPropertyTransform<Project>("Description",
project => project.Description,
project => !string.IsNullOrEmpty(project.Description));
private AddPropertyTransform<Project> CopyrightTransform => new AddPropertyTransform<Project>("Copyright",
project => project.Copyright,
project => !string.IsNullOrEmpty(project.Copyright));
private AddPropertyTransform<Project> TitleTransform => new AddPropertyTransform<Project>("AssemblyTitle",
project => project.Title,
project => !string.IsNullOrEmpty(project.Title));
private AddPropertyTransform<Project> LanguageTransform => new AddPropertyTransform<Project>("NeutralLanguage",
project => project.Language,
project => !string.IsNullOrEmpty(project.Language));
private AddPropertyTransform<Project> VersionTransform => new AddPropertyTransform<Project>("VersionPrefix",
project => project.Version.ToString(), p => true);
}
}

View file

@ -0,0 +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.IO;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
public class MigrateRuntimeOptionsRule : IMigrationRule
{
private static readonly string s_runtimeOptionsFileName = "runtimeconfig.template.json";
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
var projectContext = migrationRuleInputs.DefaultProjectContext;
var raw = projectContext.ProjectFile.RawRuntimeOptions;
var outputRuntimeOptionsFile = Path.Combine(migrationSettings.OutputDirectory, s_runtimeOptionsFileName);
if (!string.IsNullOrEmpty(raw))
{
if (File.Exists(outputRuntimeOptionsFile))
{
MigrationErrorCodes.MIGRATE1015(
$"{outputRuntimeOptionsFile} already exists. Has migration already been run?").Throw();
}
File.WriteAllText(outputRuntimeOptionsFile, raw);
}
}
}
}

View file

@ -0,0 +1,216 @@
// 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 System.Linq;
using Microsoft.Build.Construction;
using Microsoft.DotNet.Cli.Utils.CommandParsing;
using Microsoft.DotNet.ProjectJsonMigration.Transforms;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
public class MigrateScriptsRule : IMigrationRule
{
private static readonly string s_unixScriptExtension = ".sh";
private static readonly string s_windowsScriptExtension = ".cmd";
private readonly ITransformApplicator _transformApplicator;
public MigrateScriptsRule(ITransformApplicator transformApplicator = null)
{
_transformApplicator = transformApplicator ?? new TransformApplicator();
}
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
var csproj = migrationRuleInputs.OutputMSBuildProject;
var projectContext = migrationRuleInputs.DefaultProjectContext;
var scripts = projectContext.ProjectFile.Scripts;
foreach (var scriptSet in scripts)
{
MigrateScriptSet(csproj, migrationRuleInputs.CommonPropertyGroup, scriptSet.Value, scriptSet.Key);
}
}
public ProjectTargetElement MigrateScriptSet(ProjectRootElement csproj,
ProjectPropertyGroupElement propertyGroup,
IEnumerable<string> scriptCommands,
string scriptSetName)
{
var target = CreateTarget(csproj, scriptSetName);
var count = 0;
foreach (var scriptCommand in scriptCommands)
{
var scriptExtensionPropertyName = AddScriptExtension(propertyGroup, scriptCommand, $"{scriptSetName}_{++count}");
AddExec(target, FormatScriptCommand(scriptCommand, scriptExtensionPropertyName));
}
return target;
}
private string AddScriptExtension(ProjectPropertyGroupElement propertyGroup, string scriptCommandline, string scriptId)
{
var scriptArguments = CommandGrammar.Process(
scriptCommandline,
(s) => null,
preserveSurroundingQuotes: false);
scriptArguments = scriptArguments.Where(argument => !string.IsNullOrEmpty(argument)).ToArray();
var scriptCommand = scriptArguments.First();
var propertyName = $"MigratedScriptExtension_{scriptId}";
var windowsScriptExtensionProperty = propertyGroup.AddProperty(propertyName,
s_windowsScriptExtension);
var unixScriptExtensionProperty = propertyGroup.AddProperty(propertyName,
s_unixScriptExtension);
windowsScriptExtensionProperty.Condition =
$" '$(OS)' == 'Windows_NT' and Exists('{scriptCommand}{s_windowsScriptExtension}') ";
unixScriptExtensionProperty.Condition =
$" '$(OS)' != 'Windows_NT' and Exists('{scriptCommand}{s_unixScriptExtension}') ";
return propertyName;
}
internal string FormatScriptCommand(string scriptCommandline, string scriptExtensionPropertyName)
{
var command = AddScriptExtensionPropertyToCommandLine(scriptCommandline, scriptExtensionPropertyName);
return ReplaceScriptVariables(command);
}
internal string AddScriptExtensionPropertyToCommandLine(string scriptCommandline,
string scriptExtensionPropertyName)
{
var scriptArguments = CommandGrammar.Process(
scriptCommandline,
(s) => null,
preserveSurroundingQuotes: true);
scriptArguments = scriptArguments.Where(argument => !string.IsNullOrEmpty(argument)).ToArray();
var scriptCommand = scriptArguments.First();
var trimmedCommand = scriptCommand.Trim('\"').Trim('\'');
// Path.IsPathRooted only looks at paths conforming to the current os,
// we need to account for all things
if (!IsPathRootedForAnyOS(trimmedCommand))
{
scriptCommand = @".\" + scriptCommand;
}
if (scriptCommand.EndsWith("\"") || scriptCommand.EndsWith("'"))
{
var endChar = scriptCommand.Last();
scriptCommand = $"{scriptCommand.TrimEnd(endChar)}$({scriptExtensionPropertyName}){endChar}";
}
else
{
scriptCommand += $"$({scriptExtensionPropertyName})";
}
var command = string.Join(" ", new[] {scriptCommand}.Concat(scriptArguments.Skip(1)));
return command;
}
internal string ReplaceScriptVariables(string command)
{
foreach (var scriptVariableEntry in ScriptVariableToMSBuildMap)
{
var scriptVariableName = scriptVariableEntry.Key;
var msbuildMapping = scriptVariableEntry.Value;
if (command.Contains($"%{scriptVariableName}%"))
{
if (msbuildMapping == null)
{
MigrationErrorCodes.MIGRATE1016(
$"{scriptVariableName} is currently an unsupported script variable for project migration")
.Throw();
}
command = command.Replace($"%{scriptVariableName}%", msbuildMapping);
}
}
return command;
}
private bool IsPathRootedForAnyOS(string path)
{
return path.StartsWith("/") || path.Substring(1).StartsWith(":\\");
}
private ProjectTargetElement CreateTarget(ProjectRootElement csproj, string scriptSetName)
{
var targetName = $"{scriptSetName[0].ToString().ToUpper()}{string.Concat(scriptSetName.Skip(1))}Script";
var targetHookInfo = ScriptSetToMSBuildHookTargetMap[scriptSetName];
var target = csproj.CreateTargetElement(targetName);
csproj.InsertBeforeChild(target, csproj.LastChild);
if (targetHookInfo.IsRunBefore)
{
target.BeforeTargets = targetHookInfo.TargetName;
}
else
{
target.AfterTargets = targetHookInfo.TargetName;
}
return target;
}
private void AddExec(ProjectTargetElement target, string command)
{
var task = target.AddTask("Exec");
task.SetParameter("Command", command);
}
// ProjectJson Script Set Name to
private static Dictionary<string, TargetHookInfo> ScriptSetToMSBuildHookTargetMap => new Dictionary<string, TargetHookInfo>()
{
{ "precompile", new TargetHookInfo(true, "Build") },
{ "postcompile", new TargetHookInfo(false, "Build") },
{ "prepublish", new TargetHookInfo(true, "Publish") },
{ "postpublish", new TargetHookInfo(false, "Publish") }
};
private static Dictionary<string, string> ScriptVariableToMSBuildMap => new Dictionary<string, string>()
{
{ "compile:TargetFramework", null }, // TODO: Need Short framework name in CSProj
{ "compile:ResponseFile", null }, // Not migrated
{ "compile:CompilerExitCode", null }, // Not migrated
{ "compile:RuntimeOutputDir", null }, // Not migrated
{ "compile:RuntimeIdentifier", null },// Not Migrated
{ "publish:TargetFramework", null }, // TODO: Need Short framework name in CSProj
{ "publish:Runtime", "$(RuntimeIdentifier)" },
{ "compile:FullTargetFramework", "$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion)" },
{ "compile:Configuration", "$(Configuration)" },
{ "compile:OutputFile", "$(TargetPath)" },
{ "compile:OutputDir", "$(TargetDir)" },
{ "publish:ProjectPath", "$(MSBuildThisFileDirectory)" },
{ "publish:Configuration", "$(Configuration)" },
{ "publish:OutputPath", "$(TargetDir)" },
{ "publish:FullTargetFramework", "$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion)" },
{ "project:Directory", "$(MSBuildProjectDirectory)" },
{ "project:Name", "$(AssemblyName)" },
{ "project:Version", "$(Version)" }
};
private class TargetHookInfo
{
public bool IsRunBefore { get; }
public string TargetName { get; }
public TargetHookInfo(bool isRunBefore, string targetName)
{
IsRunBefore = isRunBefore;
TargetName = targetName;
}
}
}
}

View file

@ -0,0 +1,94 @@
// 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.Globalization;
using System.Linq;
using System.Text;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration.Transforms;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
// TODO: Support Multi-TFM
public class MigrateTFMRule : IMigrationRule
{
private readonly ITransformApplicator _transformApplicator;
private readonly AddPropertyTransform<NuGetFramework>[] _transforms;
public MigrateTFMRule(ITransformApplicator transformApplicator = null)
{
_transformApplicator = transformApplicator ?? new TransformApplicator();
_transforms = new AddPropertyTransform<NuGetFramework>[]
{
OutputPathTransform,
FrameworkIdentifierTransform,
FrameworkVersionTransform
};
}
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
var csproj = migrationRuleInputs.OutputMSBuildProject;
var propertyGroup = migrationRuleInputs.CommonPropertyGroup;
CleanExistingProperties(csproj);
foreach (var transform in _transforms)
{
_transformApplicator.Execute(
transform.Transform(migrationRuleInputs.DefaultProjectContext.TargetFramework),
propertyGroup);
}
}
private void CleanExistingProperties(ProjectRootElement csproj)
{
var existingPropertiesToRemove = new string[] { "TargetFrameworkIdentifier", "TargetFrameworkVersion" };
var properties = csproj.Properties.Where(p => existingPropertiesToRemove.Contains(p.Name));
foreach (var property in properties)
{
property.Parent.RemoveChild(property);
}
}
// Taken from private NuGet.Frameworks method
// https://github.com/NuGet/NuGet.Client/blob/33b8f85a94b01f805f1e955f9b68992b297fad6e/src/NuGet.Core/NuGet.Frameworks/NuGetFramework.cs#L234
private static string GetDisplayVersion(Version version)
{
var sb = new StringBuilder(String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.Major, version.Minor));
if (version.Build > 0
|| version.Revision > 0)
{
sb.AppendFormat(CultureInfo.InvariantCulture, ".{0}", version.Build);
if (version.Revision > 0)
{
sb.AppendFormat(CultureInfo.InvariantCulture, ".{0}", version.Revision);
}
}
return sb.ToString();
}
// TODO: When we have this inferred in the sdk targets, we won't need this
private AddPropertyTransform<NuGetFramework> OutputPathTransform =>
new AddPropertyTransform<NuGetFramework>("OutputPath",
f => $"bin/$(Configuration)/{f.GetShortFolderName()}",
f => true);
private AddPropertyTransform<NuGetFramework> FrameworkIdentifierTransform =>
new AddPropertyTransform<NuGetFramework>("TargetFrameworkIdentifier",
f => f.Framework,
f => true);
private AddPropertyTransform<NuGetFramework> FrameworkVersionTransform =>
new AddPropertyTransform<NuGetFramework>("TargetFrameworkVersion",
f => "v" + GetDisplayVersion(f.Version),
f => true);
}
}

View file

@ -0,0 +1,12 @@
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

@ -0,0 +1,20 @@
// 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.IO;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
public class SaveOutputProjectRule : IMigrationRule
{
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
var outputName = Path.GetFileNameWithoutExtension(
migrationRuleInputs.DefaultProjectContext.GetOutputPaths("_").CompilationFiles.Assembly);
var outputProject = Path.Combine(migrationSettings.OutputDirectory, outputName + ".csproj");
migrationRuleInputs.OutputMSBuildProject.Save(outputProject);
}
}
}

View file

@ -0,0 +1,93 @@
// 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.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
{
/// <summary>
/// This rule is temporary while project.json still exists in the new project system.
/// It renames your existing project.json (if output directory is the current project directory),
/// creates a copy, then mutates that copy.
///
/// Mutations:
/// - inject a dependency on the Microsoft.SDK targets
/// - removing the "runtimes" node.
/// </summary>
public class TemporaryMutateProjectJsonRule : IMigrationRule
{
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
bool shouldRenameOldProject = PathsAreEqual(migrationSettings.OutputDirectory, migrationSettings.ProjectDirectory);
if (!shouldRenameOldProject && File.Exists(Path.Combine(migrationSettings.OutputDirectory, "project.json")))
{
// TODO: should there be a setting to overwrite anything in output directory?
throw new Exception("Existing project.json found in output directory.");
}
var sourceProjectFile = Path.Combine(migrationSettings.ProjectDirectory, "project.json");
var destinationProjectFile = Path.Combine(migrationSettings.OutputDirectory, "project.json");
if (shouldRenameOldProject)
{
var renamedProjectFile = Path.Combine(migrationSettings.ProjectDirectory, "project.migrated.json");
File.Move(sourceProjectFile, renamedProjectFile);
sourceProjectFile = renamedProjectFile;
}
var json = CreateDestinationProjectFile(sourceProjectFile, destinationProjectFile);
InjectSdkReference(json, ConstantPackageNames.CSdkPackageName, migrationSettings.SdkPackageVersion);
RemoveRuntimesNode(json);
File.WriteAllText(destinationProjectFile, json.ToString());
}
private JObject CreateDestinationProjectFile(string sourceProjectFile, string destinationProjectFile)
{
File.Copy(sourceProjectFile, destinationProjectFile);
return JObject.Parse(File.ReadAllText(destinationProjectFile));
}
private void InjectSdkReference(JObject json, string sdkPackageName, string sdkPackageVersion)
{
JToken dependenciesNode;
if (json.TryGetValue("dependencies", out dependenciesNode))
{
var dependenciesNodeObject = dependenciesNode.Value<JObject>();
dependenciesNodeObject.Add(sdkPackageName, sdkPackageVersion);
}
else
{
var dependenciesNodeObject = new JObject();
dependenciesNodeObject.Add(sdkPackageName, sdkPackageVersion);
json.Add("dependencies", dependenciesNodeObject);
}
}
private void RemoveRuntimesNode(JObject json)
{
json.Remove("runtimes");
}
private bool PathsAreEqual(params string[] paths)
{
var normalizedPaths = paths.Select(path => Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar)).ToList();
for (int i=1; i<normalizedPaths.Count(); ++i)
{
var path1 = normalizedPaths[i - 1];
var path2 = normalizedPaths[i];
if (!string.Equals(path1, path2, StringComparison.Ordinal))
{
return false;
}
}
return true;
}
}
}

View file

@ -0,0 +1,24 @@
{
"version": "1.0.0-featmsbuild-*",
"buildOptions": {
"warningsAsErrors": true
},
"dependencies": {
"Microsoft.DotNet.Compiler.Common": {
"target": "project"
},
"Microsoft.DotNet.Cli.Utils": {
"target": "project"
},
"Microsoft.DotNet.ProjectModel": {
"target": "project"
},
"Microsoft.Build.Framework": "0.1.0-preview-00029-160805",
"Microsoft.Build.Utilities.Core": "0.1.0-preview-00029-160805",
"Microsoft.Build": "0.1.0-preview-00029-160805",
},
"frameworks": {
"netcoreapp1.0": {
}
}
}

View file

@ -0,0 +1,120 @@
// 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;
using Microsoft.DotNet.ProjectJsonMigration.Models;
using Newtonsoft.Json;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{
public class AddItemTransform<T> : ConditionalTransform<T, ProjectItemElement>
{
private readonly ProjectRootElement _itemObjectGenerator = ProjectRootElement.Create();
private readonly string _itemName;
private readonly string _includeValue;
private readonly string _excludeValue;
private readonly Func<T, string> _includeValueFunc;
private readonly Func<T, string> _excludeValueFunc;
private readonly List<ItemMetadataValue<T>> _metadata = new List<ItemMetadataValue<T>>();
public AddItemTransform(
string itemName,
IEnumerable<string> includeValues,
IEnumerable<string> excludeValues,
Func<T, bool> condition)
: this(itemName, string.Join(";", includeValues), string.Join(";", excludeValues), condition) { }
public AddItemTransform(
string itemName,
Func<T, string> includeValueFunc,
Func<T, string> excludeValueFunc,
Func<T, bool> condition)
: base(condition)
{
_itemName = itemName;
_includeValueFunc = includeValueFunc;
_excludeValueFunc = excludeValueFunc;
}
public AddItemTransform(
string itemName,
string includeValue,
Func<T, string> excludeValueFunc,
Func<T, bool> condition)
: base(condition)
{
_itemName = itemName;
_includeValue = includeValue;
_excludeValueFunc = excludeValueFunc;
}
public AddItemTransform(
string itemName,
Func<T, string> includeValueFunc,
string excludeValue,
Func<T, bool> condition)
: base(condition)
{
_itemName = itemName;
_includeValueFunc = includeValueFunc;
_excludeValue = excludeValue;
}
public AddItemTransform(
string itemName,
string includeValue,
string excludeValue,
Func<T, bool> condition)
: base(condition)
{
_itemName = itemName;
_includeValue = includeValue;
_excludeValue = excludeValue;
}
public AddItemTransform<T> WithMetadata(string metadataName, string metadataValue)
{
_metadata.Add(new ItemMetadataValue<T>(metadataName, metadataValue));
return this;
}
public AddItemTransform<T> WithMetadata(string metadataName, Func<T, string> metadataValueFunc)
{
_metadata.Add(new ItemMetadataValue<T>(metadataName, metadataValueFunc));
return this;
}
public AddItemTransform<T> WithMetadata(ItemMetadataValue<T> metadata)
{
_metadata.Add(metadata);
return this;
}
public override ProjectItemElement ConditionallyTransform(T source)
{
string includeValue = _includeValue ?? _includeValueFunc(source);
string excludeValue = _excludeValue ?? _excludeValueFunc(source);
var item = _itemObjectGenerator.AddItem(_itemName, includeValue);
item.Exclude = excludeValue;
foreach (var metadata in _metadata)
{
item.AddMetadata(metadata.MetadataName, metadata.GetMetadataValue(source));
}
return item;
}
}
}

View file

@ -0,0 +1,54 @@
// 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;
using Newtonsoft.Json;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{
public class AddPropertyTransform<T> : ConditionalTransform<T, ProjectPropertyElement>
{
public string PropertyName { get; }
private readonly ProjectRootElement _propertyObjectGenerator = ProjectRootElement.Create();
private readonly string _propertyValue;
private readonly Func<T,string> _propertyValueFunc;
public AddPropertyTransform(string propertyName, string propertyValue, Func<T,bool> condition)
: base(condition)
{
PropertyName = propertyName;
_propertyValue = propertyValue;
}
public AddPropertyTransform(string propertyName, Func<T, string> propertyValueFunc, Func<T,bool> condition)
: base(condition)
{
PropertyName = propertyName;
_propertyValueFunc = propertyValueFunc;
}
public override ProjectPropertyElement ConditionallyTransform(T source)
{
string propertyValue = GetPropertyValue(source);
var property = _propertyObjectGenerator.CreatePropertyElement(PropertyName);
property.Value = propertyValue;
return property;
}
public string GetPropertyValue(T source)
{
return _propertyValue ?? _propertyValueFunc(source);
}
}
}

View file

@ -0,0 +1,38 @@
// 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;
using Newtonsoft.Json;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{
public abstract class ConditionalTransform<T, U> : ITransform<T, U>
{
private Func<T, bool> _condition;
public ConditionalTransform(Func<T,bool> condition)
{
_condition = condition;
}
public U Transform(T source)
{
if (_condition == null || _condition(source))
{
return ConditionallyTransform(source);
}
return default(U);
}
public abstract U ConditionallyTransform(T source);
}
}

View file

@ -0,0 +1,9 @@
using Microsoft.Build.Construction;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{
public interface ITransform<T, U>
{
U Transform(T source);
}
}

View file

@ -0,0 +1,26 @@
using System.Collections.Generic;
using Microsoft.Build.Construction;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{
public interface ITransformApplicator
{
void Execute<T, U>(
T element,
U destinationElement) where T : ProjectElement where U : ProjectElementContainer;
void Execute<T, U>(
IEnumerable<T> elements,
U destinationElement) where T : ProjectElement where U : ProjectElementContainer;
void Execute(
ProjectItemElement item,
ProjectItemGroupElement destinationItemGroup,
bool mergeExisting);
void Execute(
IEnumerable<ProjectItemElement> items,
ProjectItemGroupElement destinationItemGroup,
bool mergeExisting);
}
}

View file

@ -0,0 +1,145 @@
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;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{
public class IncludeContextTransform : ConditionalTransform<IncludeContext, IEnumerable<ProjectItemElement>>
{
// TODO: If a directory is specified in project.json does this need to be replaced with a glob in msbuild?
// - Partially solved, what if the resolved glob is a directory?
// TODO: Support mappings
private readonly string _itemName;
private bool _transformMappings;
private readonly List<ItemMetadataValue<IncludeContext>> _metadata = new List<ItemMetadataValue<IncludeContext>>();
private AddItemTransform<IncludeContext>[] _transformSet;
public IncludeContextTransform(
string itemName,
bool transformMappings = true,
Func<IncludeContext, bool> condition = null) : base(condition)
{
_itemName = itemName;
_transformMappings = transformMappings;
}
public IncludeContextTransform WithMetadata(string metadataName, string metadataValue)
{
_metadata.Add(new ItemMetadataValue<IncludeContext>(metadataName, metadataValue));
return this;
}
public IncludeContextTransform WithMetadata(string metadataName, Func<IncludeContext, string> metadataValueFunc)
{
_metadata.Add(new ItemMetadataValue<IncludeContext>(metadataName, metadataValueFunc));
return this;
}
private void CreateTransformSet()
{
var includeFilesExcludeFilesTransformation = new AddItemTransform<IncludeContext>(
_itemName,
includeContext => FormatPatterns(includeContext.IncludeFiles, includeContext.SourceBasePath),
includeContext => FormatPatterns(includeContext.ExcludeFiles, includeContext.SourceBasePath),
includeContext => includeContext != null && includeContext.IncludeFiles.Count > 0);
var includeExcludeTransformation = new AddItemTransform<IncludeContext>(
_itemName,
includeContext =>
{
var fullIncludeSet = includeContext.IncludePatterns.OrEmptyIfNull()
.Union(includeContext.BuiltInsInclude.OrEmptyIfNull());
return FormatPatterns(fullIncludeSet, includeContext.SourceBasePath);
},
includeContext =>
{
var fullExcludeSet = includeContext.ExcludePatterns.OrEmptyIfNull()
.Union(includeContext.BuiltInsExclude.OrEmptyIfNull())
.Union(includeContext.ExcludeFiles.OrEmptyIfNull());
return FormatPatterns(fullExcludeSet, includeContext.SourceBasePath);
},
includeContext =>
{
return includeContext != null &&
(
(includeContext.IncludePatterns != null && includeContext.IncludePatterns.Count > 0)
||
(includeContext.BuiltInsInclude != null && includeContext.BuiltInsInclude.Count > 0)
);
});
foreach (var metadata in _metadata)
{
includeFilesExcludeFilesTransformation.WithMetadata(metadata);
includeExcludeTransformation.WithMetadata(metadata);
}
_transformSet = new []
{
includeFilesExcludeFilesTransformation,
includeExcludeTransformation
};
}
private string FormatPatterns(IEnumerable<string> patterns, string projectDirectory)
{
List<string> mutatedPatterns = new List<string>(patterns.Count());
foreach (var pattern in patterns)
{
// Do not use forward slashes
// https://github.com/Microsoft/msbuild/issues/724
var mutatedPattern = pattern.Replace('/', '\\');
// MSBuild cannot copy directories
mutatedPattern = ReplaceDirectoriesWithGlobs(mutatedPattern, projectDirectory);
mutatedPatterns.Add(mutatedPattern);
}
return string.Join(";", mutatedPatterns);
}
private string ReplaceDirectoriesWithGlobs(string pattern, string projectDirectory)
{
if (PatternIsDirectory(pattern, projectDirectory))
{
return $"{pattern.TrimEnd(new char[] { '\\' })}\\**\\*";
}
else
{
return pattern;
}
}
private bool PatternIsDirectory(string pattern, string projectDirectory)
{
// TODO: what about /some/path/**/somedir?
// Should this even be migrated?
var path = pattern;
if (!Path.IsPathRooted(path))
{
path = Path.Combine(projectDirectory, path);
}
return Directory.Exists(path);
}
public override IEnumerable<ProjectItemElement> ConditionallyTransform(IncludeContext source)
{
CreateTransformSet();
return _transformSet.Select(t => t.Transform(source));
}
}
}

View file

@ -0,0 +1,170 @@
// 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;
using Newtonsoft.Json;
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
{
public class TransformApplicator : ITransformApplicator
{
private readonly ProjectRootElement _projectElementGenerator = ProjectRootElement.Create();
public void Execute<T, U>(
T element,
U destinationElement) where T : ProjectElement where U : ProjectElementContainer
{
if (element != null)
{
if (typeof(T) == typeof(ProjectItemElement))
{
var item = destinationElement.ContainingProject.CreateItemElement("___TEMP___");
item.CopyFrom(element);
destinationElement.AppendChild(item);
item.AddMetadata((element as ProjectItemElement).Metadata);
}
else if (typeof(T) == typeof(ProjectPropertyElement))
{
var property = destinationElement.ContainingProject.CreatePropertyElement("___TEMP___");
property.CopyFrom(element);
destinationElement.AppendChild(property);
}
else
{
throw new Exception("Unable to add unknown project element to project");
}
}
}
public void Execute<T, U>(
IEnumerable<T> elements,
U destinationElement) where T : ProjectElement where U : ProjectElementContainer
{
foreach (var element in elements)
{
Execute(element, destinationElement);
}
}
public void Execute(
ProjectItemElement item,
ProjectItemGroupElement destinationItemGroup,
bool mergeExisting)
{
if (item == null)
{
return;
}
if (mergeExisting)
{
var existingItems = FindExistingItems(item, destinationItemGroup.ContainingProject);
foreach (var existingItem in existingItems)
{
var mergeResult = MergeItems(item, existingItem);
item = mergeResult.InputItem;
// Existing Item is null when it's entire set of includes has been merged with the MergeItem
if (mergeResult.ExistingItem == null)
{
existingItem.Parent.RemoveChild(existingItem);
}
Execute(mergeResult.MergedItem, destinationItemGroup);
}
// Item will be null only when it's entire set of includes is merged with existing items
if (item != null)
{
Execute(item, destinationItemGroup);
}
}
else
{
Execute(item, destinationItemGroup);
}
}
public void Execute(
IEnumerable<ProjectItemElement> items,
ProjectItemGroupElement destinationItemGroup,
bool mergeExisting)
{
foreach (var item in items)
{
Execute(item, destinationItemGroup, mergeExisting);
}
}
/// <summary>
/// Merges two items on their common sets of includes.
/// The output is 3 items, the 2 input items and the merged items. If the common
/// set of includes spans the entirety of the includes of either of the 2 input
/// items, that item will be returned as null.
///
/// The 3rd output item, the merged item, will have the Union of the excludes and
/// metadata from the 2 input items. If any metadata between the 2 input items is different,
/// this will throw.
///
/// This function will mutate the Include property of the 2 input items, removing the common subset.
/// </summary>
private MergeResult MergeItems(ProjectItemElement item, ProjectItemElement existingItem)
{
if (!string.Equals(item.ItemType, existingItem.ItemType, StringComparison.Ordinal))
{
throw new InvalidOperationException("Cannot merge items of different types.");
}
if (!item.IntersectIncludes(existingItem).Any())
{
throw new InvalidOperationException("Cannot merge items without a common include.");
}
var commonIncludes = item.IntersectIncludes(existingItem).ToList();
item.RemoveIncludes(commonIncludes);
existingItem.RemoveIncludes(commonIncludes);
var mergedItem = _projectElementGenerator.AddItem(item.ItemType, string.Join(";", commonIncludes));
mergedItem.UnionExcludes(existingItem.Excludes());
mergedItem.UnionExcludes(item.Excludes());
mergedItem.AddMetadata(existingItem.Metadata);
mergedItem.AddMetadata(item.Metadata);
var mergeResult = new MergeResult
{
InputItem = string.IsNullOrEmpty(item.Include) ? null : item,
ExistingItem = string.IsNullOrEmpty(existingItem.Include) ? null : existingItem,
MergedItem = mergedItem
};
return mergeResult;
}
private IEnumerable<ProjectItemElement> FindExistingItems(ProjectItemElement item, ProjectRootElement project)
{
return project.ItemsWithoutConditions()
.Where(i => string.Equals(i.ItemType, item.ItemType, StringComparison.Ordinal))
.Where(i => i.IntersectIncludes(item).Any());
}
private class MergeResult
{
public ProjectItemElement InputItem { get; set; }
public ProjectItemElement ExistingItem { get; set; }
public ProjectItemElement MergedItem { get; set; }
}
}
}

View file

@ -6,7 +6,7 @@
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>1c599ffd-fb52-4279-a8e5-465d3ec499e1</ProjectGuid>
<ProjectGuid>651DDD55-4819-4F20-B619-4F8A5BC2DF6D</ProjectGuid>
<RootNamespace>Microsoft.DotNet.ProjectModel.Loader</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin</OutputPath>

View file

@ -38,7 +38,15 @@ namespace Microsoft.DotNet.ProjectModel.Files
SourceBasePath = sourceBasePath;
Option = option;
var token = rawObject.Value<JToken>(option);
if (token.Type != JTokenType.Object)
if (token == null)
{
IncludePatterns = new List<string>();
ExcludePatterns = new List<string>();
IncludeFiles = new List<string>();
ExcludeFiles = new List<string>();
}
else if (token.Type != JTokenType.Object)
{
IncludePatterns = CreateCollection(
sourceBasePath, option, ExtractValues(token), literalPath: false);

View file

@ -21,6 +21,7 @@ using Microsoft.DotNet.Tools.Restore;
using Microsoft.DotNet.Tools.Restore3;
using Microsoft.DotNet.Tools.Run;
using Microsoft.DotNet.Tools.Test;
using Microsoft.DotNet.Tools.Migrate;
using NuGet.Frameworks;
namespace Microsoft.DotNet.Cli
@ -42,6 +43,7 @@ namespace Microsoft.DotNet.Cli
["run3"] = Run3Command.Run,
["restore3"] = Restore3Command.Run,
["pack3"] = Pack3Command.Run,
["migrate"] = MigrateCommand.Run
};
public static int Main(string[] args)

View file

@ -2,4 +2,4 @@ using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyMetadataAttribute("Serviceable", "True")]
[assembly: InternalsVisibleTo("dotnet.Tests")]
[assembly: InternalsVisibleTo("dotnet.Tests")]

View file

@ -15,6 +15,5 @@ namespace Microsoft.DotNet.Cli
{
return new MSBuildForwardingApp(args).Execute();
}
}
}

View file

@ -0,0 +1,78 @@
// 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.IO;
using Microsoft.Build.Construction;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.ProjectJsonMigration;
using Microsoft.DotNet.ProjectModel;
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 TemporaryDotnetNewTemplateProject _temporaryDotnetNewProject;
public MigrateCommand(string templateFile, string outputDirectory, string projectJson, string sdkVersion)
{
_templateFile = templateFile;
_outputDirectory = outputDirectory;
_projectJson = projectJson;
_sdkVersion = sdkVersion;
_temporaryDotnetNewProject = new TemporaryDotnetNewTemplateProject();
}
public int Execute()
{
var project = GetProjectJsonPath(_projectJson) ?? GetProjectJsonPath(Directory.GetCurrentDirectory());
EnsureNotNull(project, "Unable to find project.json");
var projectDirectory = Path.GetDirectoryName(project);
var msBuildTemplate = _templateFile != null ?
ProjectRootElement.TryOpen(_templateFile) : _temporaryDotnetNewProject.MSBuildProject;
var outputDirectory = _outputDirectory ?? projectDirectory;
EnsureNotNull(outputDirectory, "Null output directory");
var sdkVersion = _sdkVersion ?? new ProjectJsonParser(_temporaryDotnetNewProject.ProjectJson).SdkPackageVersion;
EnsureNotNull(sdkVersion, "Null Sdk Version");
var migrationSettings = new MigrationSettings(projectDirectory, outputDirectory, sdkVersion, msBuildTemplate);
new ProjectMigrator().Migrate(migrationSettings);
return 0;
}
private void EnsureNotNull(string variable, string message)
{
if (variable == null)
{
throw new Exception(message);
}
}
private string GetProjectJsonPath(string projectJson)
{
if (projectJson == null)
{
return null;
}
projectJson = ProjectPathHelper.NormalizeProjectFilePath(projectJson);
if (File.Exists(projectJson))
{
return projectJson;
}
throw new Exception($"Unable to find project file at {projectJson}");
}
}
}

View file

@ -0,0 +1,54 @@
// 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 Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Utils;
namespace Microsoft.DotNet.Tools.Migrate
{
public partial class MigrateCommand
{
public static int Run(string[] args)
{
DebugHelper.HandleDebugSwitch(ref args);
CommandLineApplication app = new CommandLineApplication();
app.Name = "dotnet migrate";
app.FullName = ".NET Migrate Command";
app.Description = "Command used to migrate project.json projects to msbuild";
app.HandleResponseFiles = true;
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);
app.OnExecute(() =>
{
MigrateCommand migrateCommand = new MigrateCommand(
template.Value(),
output.Value(),
project.Value(),
sdkVersion.Value());
return migrateCommand.Execute();
});
try
{
return app.Execute(args);
}
catch (Exception ex)
{
#if DEBUG
Reporter.Error.WriteLine(ex.ToString());
#else
Reporter.Error.WriteLine(ex.Message);
#endif
return 1;
}
}
}
}

View file

@ -0,0 +1,80 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.DotNet.ProjectJsonMigration;
namespace Microsoft.DotNet.Tools.Migrate
{
/// <summary>
/// Parses select data from a project.json without relying on ProjectModel.
/// Used to parse simple information.
/// </summary>
internal class ProjectJsonParser
{
public string SdkPackageVersion { get; }
public ProjectJsonParser(JObject projectJson)
{
SdkPackageVersion = GetPackageVersion(projectJson, ConstantPackageNames.CSdkPackageName);
}
private string GetPackageVersion(JObject projectJson, string packageName)
{
var sdkPackageNode = SelectJsonNodes(projectJson, property => property.Name == packageName).First();
if (sdkPackageNode.Value.Type == JTokenType.String)
{
return (string)sdkPackageNode.Value;
}
else if (sdkPackageNode.Type == JTokenType.Object)
{
var sdkPackageNodeValue = (JObject)sdkPackageNode.Value;
JToken versionNode;
if (sdkPackageNodeValue.TryGetValue("version", out versionNode))
{
return versionNode.Value<string>();
}
else
{
throw new Exception("Unable to determine sdk version, no version node in default template.");
}
}
else
{
throw new Exception("Unable to determine sdk version, no version information found");
}
}
private IEnumerable<JProperty> SelectJsonNodes(
JToken jsonNode,
Func<JProperty, bool> condition,
List<JProperty> nodeAccumulator = null)
{
nodeAccumulator = nodeAccumulator ?? new List<JProperty>();
if (jsonNode.Type == JTokenType.Object)
{
var eligibleNodes = jsonNode.Children<JProperty>().Where(j => condition(j));
nodeAccumulator.AddRange(eligibleNodes);
foreach (var child in jsonNode.Children<JProperty>())
{
SelectJsonNodes(child.Value, condition, nodeAccumulator: nodeAccumulator);
}
}
else if (jsonNode.Type == JTokenType.Array)
{
foreach (var child in jsonNode.Children())
{
SelectJsonNodes(child, condition, nodeAccumulator: nodeAccumulator);
}
}
return nodeAccumulator;
}
}
}

View file

@ -0,0 +1,88 @@
using Microsoft.Build.Construction;
using Microsoft.DotNet.Cli;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.DotNet.ProjectJsonMigration;
namespace Microsoft.DotNet.Tools.Migrate
{
internal class TemporaryDotnetNewTemplateProject
{
private const string c_temporaryDotnetNewMSBuildProjectName = "p";
private readonly string _projectDirectory;
public ProjectRootElement MSBuildProject { get; }
public JObject ProjectJson { get; }
public TemporaryDotnetNewTemplateProject()
{
_projectDirectory = CreateDotnetNewMSBuild(c_temporaryDotnetNewMSBuildProjectName);
MSBuildProject = GetMSBuildProject(_projectDirectory);
ProjectJson = GetProjectJson(_projectDirectory);
Clean();
}
public void Clean()
{
Directory.Delete(Path.Combine(_projectDirectory, ".."), true);
}
private string CreateDotnetNewMSBuild(string projectName)
{
var tempDir = Path.Combine(
Path.GetTempPath(),
this.GetType().Namespace,
Path.GetRandomFileName(),
c_temporaryDotnetNewMSBuildProjectName);
if (Directory.Exists(tempDir))
{
Directory.Delete(tempDir, true);
}
Directory.CreateDirectory(tempDir);
RunCommand("new", new string[] { "-t", "msbuild" }, tempDir);
return tempDir;
}
private ProjectRootElement GetMSBuildProject(string temporaryDotnetNewMSBuildDirectory)
{
var templateProjPath = Path.Combine(temporaryDotnetNewMSBuildDirectory,
c_temporaryDotnetNewMSBuildProjectName + ".csproj");
return ProjectRootElement.Open(templateProjPath);
}
private JObject GetProjectJson(string temporaryDotnetNewMSBuildDirectory)
{
var projectJsonFile = Path.Combine(temporaryDotnetNewMSBuildDirectory, "project.json");
return JObject.Parse(File.ReadAllText(projectJsonFile));
}
private void RunCommand(string commandToExecute, IEnumerable<string> args, string workingDirectory)
{
var command = new DotNetCommandFactory()
.Create(commandToExecute, args)
.WorkingDirectory(workingDirectory)
.CaptureStdOut()
.CaptureStdErr();
var commandResult = command.Execute();
if (commandResult.ExitCode != 0)
{
MigrationTrace.Instance.WriteLine(commandResult.StdOut);
MigrationTrace.Instance.WriteLine(commandResult.StdErr);
throw new Exception($"Failed to run {commandToExecute} in directory: {workingDirectory}");
}
}
}
}

View file

@ -45,6 +45,9 @@
"Microsoft.DotNet.Configurer": {
"target": "project"
},
"Microsoft.DotNet.ProjectJsonMigration": {
"target": "project"
},
"Microsoft.DotNet.Tools.Test": {
"target": "project"
},

View file

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Tools.Common;
using Microsoft.DotNet.Tools.Test.Utilities;
using NuGet.Frameworks;
using Xunit;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
public class GivenAProjectMigrator : TestBase
{
[Fact]
public void It_copies_ProjectDirectory_contents_to_OutputDirectory_when_the_directories_are_different()
{
var testProjectDirectory = TestAssetsManager.CreateTestInstance("TestAppSimple", callingMethod: "z")
.WithLockFiles().Path;
var outputDirectory = Temp.CreateDirectory().Path;
var projectDirectoryRelativeFilePaths = EnumerateFilesWithRelativePath(testProjectDirectory);
var mockProj = ProjectRootElement.Create();
var testSettings = new MigrationSettings(testProjectDirectory, outputDirectory, "1.0.0", mockProj);
var projectMigrator = new ProjectMigrator(new FakeEmptyMigrationRule());
projectMigrator.Migrate(testSettings);
foreach (var projectDirectoryRelativeFilePath in projectDirectoryRelativeFilePaths)
{
File.Exists(Path.Combine(outputDirectory, projectDirectoryRelativeFilePath)).Should().BeTrue();
}
}
[Fact]
public void It_throws_when_migrating_a_deprecated_projectJson()
{
var testProjectDirectory =
TestAssetsManager.CreateTestInstance("TestLibraryWithDeprecatedProjectFile", callingMethod: "z")
.WithLockFiles().Path;
var mockProj = ProjectRootElement.Create();
var testSettings = new MigrationSettings(testProjectDirectory, testProjectDirectory, "1.0.0", mockProj);
var projectMigrator = new ProjectMigrator(new FakeEmptyMigrationRule());
Action migrateAction = () => projectMigrator.Migrate(testSettings);
migrateAction.ShouldThrow<Exception>().Where(
e => e.Message.Contains("MIGRATE1011::Deprecated Project:")
&& e.Message.Contains("The 'packInclude' option is deprecated. Use 'files' in 'packOptions' instead.")
&& e.Message.Contains("The 'compilationOptions' option is deprecated. Use 'buildOptions' instead."));
}
[Fact]
public void It_throws_when_migrating_a_non_csharp_app()
{
var testProjectDirectory =
TestAssetsManager.CreateTestInstance("FSharpTestProjects/TestApp", callingMethod: "z")
.WithLockFiles().Path;
var mockProj = ProjectRootElement.Create();
var testSettings = new MigrationSettings(testProjectDirectory, testProjectDirectory, "1.0.0", mockProj);
var projectMigrator = new ProjectMigrator(new FakeEmptyMigrationRule());
Action migrateAction = () => projectMigrator.Migrate(testSettings);
migrateAction.ShouldThrow<Exception>().Where(
e => e.Message.Contains("MIGRATE20013::Non-Csharp App: Cannot migrate project"));
}
private IEnumerable<string> EnumerateFilesWithRelativePath(string testProjectDirectory)
{
return
Directory.EnumerateFiles(testProjectDirectory, "*", SearchOption.AllDirectories)
.Select(file => PathUtility.GetRelativePath(testProjectDirectory, file));
}
private class FakeEmptyMigrationRule : IMigrationRule
{
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
{
return;
}
}
}
}

View file

@ -0,0 +1,130 @@
using System.Linq;
using FluentAssertions;
using Microsoft.Build.Construction;
using Xunit;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
public class GivenMSBuildExtensions
{
[Fact]
public void Includes_returns_include_value_split_by_semicolon()
{
var project = ProjectRootElement.Create();
var item = project.CreateItemElement("test");
item.Include = "include1;include2;aaa";
var includes = item.Includes().ToArray();
includes.Should().HaveCount(3);
includes[0].Should().Be("include1");
includes[1].Should().Be("include2");
includes[2].Should().Be("aaa");
}
[Fact]
public void Excludes_returns_include_value_split_by_semicolon()
{
var project = ProjectRootElement.Create();
var item = project.CreateItemElement("test");
item.Exclude = "include1;include2;aaa";
var excludes = item.Excludes().ToArray();
excludes.Should().HaveCount(3);
excludes[0].Should().Be("include1");
excludes[1].Should().Be("include2");
excludes[2].Should().Be("aaa");
}
[Fact]
public void ItemsWithoutConditions_returns_items_without_a_condition()
{
var project = ProjectRootElement.Create();
var item = project.AddItem("test", "include1");
project.ItemsWithoutConditions().Count().Should().Be(1);
project.ItemsWithoutConditions().First().Should().Be(item);
}
[Fact]
public void ItemsWithoutConditions_doesnt_return_items_with_a_condition()
{
var project = ProjectRootElement.Create();
var conditionlessItems = project.AddItem("test", "include1");
var conditionItem = project.AddItem("test2", "include2");
conditionItem.Condition = "SomeCondition";
project.ItemsWithoutConditions().Count().Should().Be(1);
project.ItemsWithoutConditions().First().Should().Be(conditionlessItems);
}
[Fact]
public void ItemsWithoutConditions_doesnt_return_items_with_a_parent_with_a_condition()
{
var project = ProjectRootElement.Create();
var conditionlessItems = project.AddItem("test", "include1");
var conditionItemGroup = project.AddItemGroup();
conditionItemGroup.Condition = "SomeCondition";
conditionItemGroup.AddItem("test2", "include2");
project.ItemsWithoutConditions().Count().Should().Be(1);
project.ItemsWithoutConditions().First().Should().Be(conditionlessItems);
}
[Fact]
public void AddIncludes_merges_include_sets()
{
var project = ProjectRootElement.Create();
var item1 = project.AddItem("test", "include1;include2");
item1.UnionIncludes(new string[] {"include2", "include3"});
item1.Include.Should().Be("include1;include2;include3");
}
[Fact]
public void AddExcludes_merges_include_sets()
{
var project = ProjectRootElement.Create();
var item1 = project.AddItem("test", "include1");
item1.Exclude = "exclude1;exclude2";
item1.UnionExcludes(new string[] {"exclude2", "exclude3"});
item1.Exclude.Should().Be("exclude1;exclude2;exclude3");
}
[Fact]
public void AddMetadata_adds_metadata_available_via_Metadata_on_an_item()
{
var project = ProjectRootElement.Create();
var item1 = project.AddItem("test", "include1");
item1.AddMetadata("name", "value");
item1.HasMetadata.Should().BeTrue();
var item2 = project.AddItem("test1", "include1");
item2.AddMetadata(item1.Metadata);
item2.HasMetadata.Should().BeTrue();
item2.Metadata.First().Name.Should().Be("name");
item2.Metadata.First().Value.Should().Be("value");
}
[Fact]
public void AddMetadata_adds_metadata_from_an_item_generated_from_another_project()
{
var project = ProjectRootElement.Create();
var item1 = project.AddItem("test", "include1");
item1.AddMetadata("name", "value");
item1.HasMetadata.Should().BeTrue();
var project2 = ProjectRootElement.Create();
var item2 = project2.AddItem("test1", "include1");
item2.AddMetadata(item1.Metadata);
item2.HasMetadata.Should().BeTrue();
item2.Metadata.First().Name.Should().Be("name");
item2.Metadata.First().Value.Should().Be("value");
}
}
}

View file

@ -0,0 +1 @@
https://github.com/Microsoft/msbuild/issues/927

View file

@ -0,0 +1 @@
https://github.com/Microsoft/msbuild/issues/927

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0.25123" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25123</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>1F2EF070-AC5F-4078-AFB0-65745AC691B9</ProjectGuid>
<RootNamespace>Microsoft.DotNet.ProjectJsonMigration.Tests</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' “>..\artifact\obj\$(RootNamespace)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' “>..\artifacts\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View file

@ -0,0 +1,110 @@
using Microsoft.DotNet.TestFramework;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
/// <summary>
/// Used to build up test scenario project.jsons without needing to add a new test asset.
/// </summary>
public class ProjectJsonBuilder
{
private readonly TestAssetsManager _testAssetsManager;
private JObject _projectJson;
private bool _baseDefined = false;
private bool _baseProjectDirectory;
public ProjectJsonBuilder(TestAssetsManager testAssetsManager)
{
_testAssetsManager = testAssetsManager;
}
public string SaveToDisk(string outputDirectory)
{
EnsureBaseIsSet();
var projectPath = Path.Combine(outputDirectory, "project.json");
File.WriteAllText(projectPath, _projectJson.ToString());
return projectPath;
}
public JObject Build()
{
EnsureBaseIsSet();
return _projectJson;
}
public ProjectJsonBuilder FromTestAssetBase(string testAssetName)
{
var testProjectDirectory = _testAssetsManager.CreateTestInstance(testAssetName).Path;
var testProject = Path.Combine(testProjectDirectory, "project.json");
SetBase(JObject.Parse(File.ReadAllText(testProject)));
return this;
}
public ProjectJsonBuilder FromStringBase(string jsonString)
{
SetBase(JObject.Parse(jsonString));
return this;
}
public ProjectJsonBuilder FromEmptyBase()
{
SetBase(new JObject());
return this;
}
public ProjectJsonBuilder WithCustomProperty(string propertyName, Dictionary<string, string> value)
{
EnsureBaseIsSet();
_projectJson[propertyName] = JObject.FromObject(value);
return this;
}
public ProjectJsonBuilder WithCustomProperty(string propertyName, string value)
{
EnsureBaseIsSet();
_projectJson[propertyName] = value;
return this;
}
public ProjectJsonBuilder WithCustomProperty(string propertyName, string[] value)
{
EnsureBaseIsSet();
_projectJson[propertyName] = JArray.FromObject(value);
return this;
}
private void SetBase(JObject project)
{
if (_baseDefined)
{
throw new Exception("Base was already defined.");
}
_baseDefined = true;
_projectJson = project;
}
private void EnsureBaseIsSet()
{
if (!_baseDefined)
{
throw new Exception("Cannot build without base set");
}
}
}
}

View file

@ -0,0 +1,452 @@
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Tools.Test.Utilities;
using NuGet.Frameworks;
using System;
using System.IO;
using System.Linq;
using Xunit;
using FluentAssertions;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
using Microsoft.DotNet.ProjectModel.Files;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
public class GivenThatIWantToMigrateBuildOptions : TestBase
{
[Fact]
public void Specified_default_properties_are_removed_when_they_exists_in_the_csproj_template()
{
// Setup project with default properties
var defaultPropertiesExpectedToBeRemoved = new string[]
{
"OutputType",
"TargetExt"
};
var defaultValue = "defaultValue";
var templateProj = ProjectRootElement.Create();
var defaultPropertyGroup = templateProj.AddPropertyGroup();
foreach (var defaultPropertyName in defaultPropertiesExpectedToBeRemoved)
{
defaultPropertyGroup.AddProperty(defaultPropertyName, defaultValue);
}
// Setup projectcontext
var testProjectDirectory = TestAssetsManager.CreateTestInstance("TestAppWithRuntimeOptions").Path;
var projectContext = ProjectContext.Create(testProjectDirectory, FrameworkConstants.CommonFrameworks.NetCoreApp10);
var testSettings = new MigrationSettings(testProjectDirectory, testProjectDirectory, "1.0.0", templateProj);
var testInputs = new MigrationRuleInputs(new[] {projectContext}, templateProj, templateProj.AddItemGroup(),
templateProj.AddPropertyGroup());
new MigrateBuildOptionsRule().Apply(testSettings, testInputs);
defaultPropertyGroup.Properties.Count.Should().Be(0);
}
[Fact]
public void Migrating_empty_buildOptions_populates_only_AssemblyName_and_OutputType()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": { }
}");
mockProj.Properties.Count().Should().Be(2);
mockProj.Properties.Any(
p =>
!(p.Name.Equals("AssemblyName", StringComparison.Ordinal) ||
p.Name.Equals("OutputType", StringComparison.Ordinal))).Should().BeFalse();
mockProj.Items.Count().Should().Be(2);
mockProj.Items.First(i => i.ItemType == "Compile").Include.Should().Be(@"**\*.cs");
mockProj.Items.First(i => i.ItemType == "Compile").Exclude.Should().Be(@"bin\**;obj\**;**\*.xproj;packages\**");
mockProj.Items.First(i => i.ItemType == "EmbeddedResource").Include.Should().Be(@"compiler\resources\**\*;**\*.resx");
mockProj.Items.First(i => i.ItemType == "EmbeddedResource").Exclude.Should().Be(@"bin\**;obj\**;**\*.xproj;packages\**");
}
[Fact]
public void Migrating_EmitEntryPoint_true_populates_OutputType_field()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""emitEntryPoint"": ""true""
}
}");
mockProj.Properties.Count(p => p.Name == "OutputType").Should().Be(1);
mockProj.Properties.First(p => p.Name == "OutputType").Value.Should().Be("Exe");
}
[Fact]
public void Migrating_EmitEntryPoint_false_populates_OutputType_fields()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""emitEntryPoint"": ""false""
}
}");
mockProj.Properties.Count(p => p.Name == "OutputType").Should().Be(1);
mockProj.Properties.First(p => p.Name == "OutputType").Value.Should().Be("Library");
}
[Fact]
public void Migrating_define_populates_DefineConstants()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""define"": [ ""DEBUG"", ""TRACE"" ]
}
}");
mockProj.Properties.Count(p => p.Name == "DefineConstants").Should().Be(1);
mockProj.Properties.First(p => p.Name == "DefineConstants").Value.Should().Be("DEBUG;TRACE");
}
[Fact]
public void Migrating_nowarn_populates_NoWarn()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""nowarn"": [ ""CS0168"", ""CS0219"" ]
}
}");
mockProj.Properties.Count(p => p.Name == "NoWarn").Should().Be(1);
mockProj.Properties.First(p => p.Name == "NoWarn").Value.Should().Be("CS0168;CS0219");
}
[Fact]
public void Migrating_warningsAsErrors_populates_WarningsAsErrors()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""warningsAsErrors"": ""true""
}
}");
mockProj.Properties.Count(p => p.Name == "WarningsAsErrors").Should().Be(1);
mockProj.Properties.First(p => p.Name == "WarningsAsErrors").Value.Should().Be("true");
mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""warningsAsErrors"": ""false""
}
}");
mockProj.Properties.Count(p => p.Name == "WarningsAsErrors").Should().Be(0);
}
[Fact]
public void Migrating_allowUnsafe_populates_AllowUnsafeBlocks()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""allowUnsafe"": ""true""
}
}");
mockProj.Properties.Count(p => p.Name == "AllowUnsafeBlocks").Should().Be(1);
mockProj.Properties.First(p => p.Name == "AllowUnsafeBlocks").Value.Should().Be("true");
mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""allowUnsafe"": ""false""
}
}");
mockProj.Properties.Count(p => p.Name == "AllowUnsafeBlocks").Should().Be(0);
}
[Fact]
public void Migrating_optimize_populates_Optimize()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""optimize"": ""true""
}
}");
mockProj.Properties.Count(p => p.Name == "Optimize").Should().Be(1);
mockProj.Properties.First(p => p.Name == "Optimize").Value.Should().Be("true");
mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""optimize"": ""false""
}
}");
mockProj.Properties.Count(p => p.Name == "Optimize").Should().Be(0);
}
[Fact]
public void Migrating_platform_populates_PlatformTarget()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""platform"": ""x64""
}
}");
mockProj.Properties.Count(p => p.Name == "PlatformTarget").Should().Be(1);
mockProj.Properties.First(p => p.Name == "PlatformTarget").Value.Should().Be("x64");
mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""platform"": ""x86""
}
}");
mockProj.Properties.Count(p => p.Name == "PlatformTarget").Should().Be(1);
mockProj.Properties.First(p => p.Name == "PlatformTarget").Value.Should().Be("x86");
mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""platform"": ""foo""
}
}");
mockProj.Properties.Count(p => p.Name == "PlatformTarget").Should().Be(1);
mockProj.Properties.First(p => p.Name == "PlatformTarget").Value.Should().Be("foo");
}
[Fact]
public void Migrating_languageVersion_populates_LangVersion()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""languageVersion"": ""5""
}
}");
mockProj.Properties.Count(p => p.Name == "LangVersion").Should().Be(1);
mockProj.Properties.First(p => p.Name == "LangVersion").Value.Should().Be("5");
}
[Fact]
public void Migrating_keyFile_populates_AssemblyOriginatorKeyFile_and_SignAssembly()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""keyFile"": ""../keyfile.snk""
}
}");
mockProj.Properties.Count(p => p.Name == "AssemblyOriginatorKeyFile").Should().Be(1);
mockProj.Properties.First(p => p.Name == "AssemblyOriginatorKeyFile").Value.Should().Be("../keyfile.snk");
mockProj.Properties.Count(p => p.Name == "SignAssembly").Should().Be(1);
mockProj.Properties.First(p => p.Name == "SignAssembly").Value.Should().Be("true");
}
[Fact]
public void Migrating_delaySign_populates_DelaySign()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""delaySign"": ""true""
}
}");
mockProj.Properties.Count(p => p.Name == "DelaySign").Should().Be(1);
mockProj.Properties.First(p => p.Name == "DelaySign").Value.Should().Be("true");
mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""delaySign"": ""false""
}
}");
mockProj.Properties.Count(p => p.Name == "DelaySign").Should().Be(0);
}
[Fact]
public void Migrating_publicSign_populates_PublicSign()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""publicSign"": ""true""
}
}");
mockProj.Properties.Count(p => p.Name == "PublicSign").Should().Be(1);
mockProj.Properties.First(p => p.Name == "PublicSign").Value.Should().Be("true");
mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""publicSign"": ""false""
}
}");
mockProj.Properties.Count(p => p.Name == "PublicSign").Should().Be(0);
}
[Fact]
public void Migrating_debugType_populates_DebugType()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""debugType"": ""full""
}
}");
mockProj.Properties.Count(p => p.Name == "DebugType").Should().Be(1);
mockProj.Properties.First(p => p.Name == "DebugType").Value.Should().Be("full");
mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""debugType"": ""foo""
}
}");
mockProj.Properties.Count(p => p.Name == "DebugType").Should().Be(1);
mockProj.Properties.First(p => p.Name == "DebugType").Value.Should().Be("foo");
}
[Fact]
public void Migrating_outputName_populates_AssemblyName()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""outputName"": ""ARandomName""
}
}");
mockProj.Properties.Count(p => p.Name == "AssemblyName").Should().Be(1);
mockProj.Properties.First(p => p.Name == "AssemblyName").Value.Should().Be("ARandomName");
}
[Fact]
public void Migrating_xmlDoc_populates_GenerateDocumentationFile()
{
var mockProj = RunBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""xmlDoc"": ""true""
}
}");
mockProj.Properties.Count(p => p.Name == "GenerateDocumentationFile").Should().Be(1);
mockProj.Properties.First(p => p.Name == "GenerateDocumentationFile").Value.Should().Be("true");
}
[Theory]
[InlineData("compile", "Compile")]
[InlineData("embed", "EmbeddedResource")]
[InlineData("copyToOutput", "Content")]
private void Migrating_group_include_exclude_Populates_appropriate_ProjectItemElement(
string group,
string itemName)
{
var testDirectory = Temp.CreateDirectory().Path;
Directory.CreateDirectory(Path.Combine(testDirectory, "root"));
Directory.CreateDirectory(Path.Combine(testDirectory, "src"));
File.WriteAllText(Path.Combine(testDirectory, "root", "file1.txt"), "content");
File.WriteAllText(Path.Combine(testDirectory, "root", "file2.txt"), "content");
File.WriteAllText(Path.Combine(testDirectory, "root", "file3.txt"), "content");
File.WriteAllText(Path.Combine(testDirectory, "src", "file1.cs"), "content");
File.WriteAllText(Path.Combine(testDirectory, "src", "file2.cs"), "content");
File.WriteAllText(Path.Combine(testDirectory, "src", "file3.cs"), "content");
File.WriteAllText(Path.Combine(testDirectory, "rootfile.cs"), "content");
var pj = @"
{
""buildOptions"": {
""<group>"": {
""include"": [""root"", ""src"", ""rootfile.cs""],
""exclude"": [""src"", ""rootfile.cs""],
""includeFiles"": [""src/file1.cs"", ""src/file2.cs""],
""excludeFiles"": [""src/file2.cs""]
}
}
}".Replace("<group>", group);
var mockProj = RunBuildOptionsRuleOnPj(pj,
testDirectory: testDirectory);
mockProj.Items.Count(i => i.ItemType.Equals(itemName, StringComparison.Ordinal)).Should().Be(2);
var defaultIncludePatterns = group == "compile" ? ProjectFilesCollection.DefaultCompileBuiltInPatterns
: group == "embed" ? ProjectFilesCollection.DefaultResourcesBuiltInPatterns
: Enumerable.Empty<string>();
var defaultExcludePatterns = group == "copyToOutput" ? ProjectFilesCollection.DefaultPublishExcludePatterns
: ProjectFilesCollection.DefaultBuiltInExcludePatterns;
foreach (var item in mockProj.Items.Where(i => i.ItemType.Equals(itemName, StringComparison.Ordinal)))
{
if (item.ItemType == "Content")
{
item.Metadata.Count(m => m.Name == "CopyToOutputDirectory").Should().Be(1);
}
if (item.Include.Contains(@"src\file1.cs"))
{
item.Include.Should().Be(@"src\file1.cs;src\file2.cs");
item.Exclude.Should().Be(@"src\file2.cs");
}
else
{
if (defaultIncludePatterns.Any())
{
item.Include.Should()
.Be(@"root\**\*;src\**\*;rootfile.cs;" + string.Join(";", defaultIncludePatterns).Replace("/", "\\"));
}
else
{
item.Include.Should()
.Be(@"root\**\*;src\**\*;rootfile.cs");
}
if (defaultExcludePatterns.Any())
{
item.Exclude.Should()
.Be(@"src\**\*;rootfile.cs;" + string.Join(";", defaultExcludePatterns).Replace("/", "\\") +
@";src\file2.cs");
}
else
{
item.Exclude.Should()
.Be(@"src\**\*;rootfile.cs;src\file2.cs");
}
}
}
}
private ProjectRootElement RunBuildOptionsRuleOnPj(string s, string testDirectory = null)
{
testDirectory = testDirectory ?? Temp.CreateDirectory().Path;
return TemporaryProjectFileRuleRunner.RunRules(new IMigrationRule[]
{
new MigrateBuildOptionsRule()
}, s, testDirectory);
}
}
}

View file

@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Build.Construction;
using Microsoft.DotNet.Migration.Tests;
using Microsoft.DotNet.ProjectJsonMigration;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Tools.Test.Utilities;
using NuGet.Frameworks;
using Xunit;
using FluentAssertions;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
using Microsoft.DotNet.ProjectJsonMigration.Tests;
namespace Microsoft.DotNet.ProjectJsonMigrationMigration.Tests
{
public class GivenThatIWantToMigrateConfigurations : TestBase
{
[Fact]
public void Configuration_buildOptions_produce_expected_properties_in_a_group_with_a_condition()
{
var mockProj = RunConfigurationsRuleOnPj(@"
{
""configurations"": {
""testconfig"": {
""buildOptions"": {
""emitEntryPoint"": ""true"",
""debugType"": ""full""
}
}
}
}");
mockProj.Properties.Count(
prop => prop.Name == "OutputType" || prop.Name == "DebugType").Should().Be(2);
mockProj.Properties.First(p => p.Name == "OutputType")
.Parent.Condition.Should()
.Contain("'$(Configuration)' == 'testconfig'");
}
[Fact]
public void Configuration_buildOptions_properties_are_not_written_when_they_overlap_with_buildOptions()
{
var mockProj = RunConfigurationsAndBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""emitEntryPoint"": ""true"",
""debugType"": ""full""
},
""configurations"": {
""testconfig"": {
""buildOptions"": {
""emitEntryPoint"": ""true"",
""debugType"": ""full""
}
}
}
}");
mockProj.Properties.Count(property =>
property.Name == "OutputType" || property.Name == "DebugType")
.Should().Be(2);
foreach (var property in mockProj.Properties.Where(property =>
property.Name == "OutputType" || property.Name == "DebugType"))
{
property.Parent.Condition.Should().Be(string.Empty);
}
}
[Fact]
public void Configuration_buildOptions_includes_are_not_written_when_they_overlap_with_buildOptions()
{
var mockProj = RunConfigurationsAndBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""copyToOutput"": {
""include"": [""src""],
""exclude"": [""src"", ""rootfile.cs""],
""includeFiles"": [""src/file1.cs"", ""src/file2.cs""],
""excludeFiles"": [""src/file2.cs""]
}
},
""configurations"": {
""testconfig"": {
""buildOptions"": {
""copyToOutput"": {
""include"": [""root"", ""src"", ""rootfile.cs""],
""exclude"": [""src"", ""rootfile.cs""],
""includeFiles"": [""src/file1.cs"", ""src/file2.cs""],
""excludeFiles"": [""src/file2.cs""]
}
}
}
}
}");
mockProj.Items.Count(item => item.ItemType == "Content").Should().Be(3);
mockProj.Items.Where(item => item.ItemType == "Content")
.Count(item => !string.IsNullOrEmpty(item.Parent.Condition))
.Should()
.Be(1);
var configContent = mockProj.Items
.Where(item => item.ItemType == "Content").First(item => !string.IsNullOrEmpty(item.Parent.Condition));
// Directories are not converted to globs in the result because we did not write the directory
configContent.Include.Should().Be(@"root;rootfile.cs");
configContent.Exclude.Should().Be(@"src;rootfile.cs;src\file2.cs");
}
[Fact]
public void Configuration_buildOptions_includes_which_have_different_excludes_than_buildOptions_throws()
{
Action action = () => RunConfigurationsRuleOnPj(@"
{
""buildOptions"": {
""copyToOutput"": {
""include"": [""src""],
""exclude"": [""rootfile.cs""],
""includeFiles"": [""src/file1.cs"", ""src/file2.cs""],
""excludeFiles"": [""src/file3.cs""]
}
},
""configurations"": {
""testconfig"": {
""buildOptions"": {
""copyToOutput"": {
""include"": [""root"", ""src"", ""rootfile.cs""],
""exclude"": [""src"", ""rootfile.cs""],
""includeFiles"": [""src/file1.cs"", ""src/file2.cs""],
""excludeFiles"": [""src/file2.cs""]
}
}
}
}
}");
action.ShouldThrow<Exception>()
.WithMessage(
"MIGRATE20012::Configuration Exclude: Unable to migrate projects with excluded files in configurations.");
}
private ProjectRootElement RunConfigurationsRuleOnPj(string s, string testDirectory = null)
{
testDirectory = testDirectory ?? Temp.CreateDirectory().Path;
return TemporaryProjectFileRuleRunner.RunRules(new[] {new MigrateConfigurationsRule()}, s, testDirectory);
}
private ProjectRootElement RunConfigurationsAndBuildOptionsRuleOnPj(string s, string testDirectory = null)
{
testDirectory = testDirectory ?? Temp.CreateDirectory().Path;
return TemporaryProjectFileRuleRunner.RunRules(new IMigrationRule[]
{
new MigrateBuildOptionsRule(),
new MigrateConfigurationsRule()
}, s, testDirectory);
}
}
}

View file

@ -0,0 +1,86 @@
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Tools.Test.Utilities;
using NuGet.Frameworks;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using FluentAssertions;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
namespace Microsoft.DotNet.Migration.Tests
{
public class GivenThatIWantToMigrateProjectDependencies : TestBase
{
// Workaround For P2P dependencies
// ISSUE: https://github.com/dotnet/sdk/issues/73
[Fact]
public void If_a_project_dependency_is_present_DesignTimeAutoUnify_and_AutoUnify_are_present()
{
var solutionDirectory =
TestAssetsManager.CreateTestInstance("TestAppWithLibrary", callingMethod: "p").WithLockFiles().Path;
var appDirectory = Path.Combine(solutionDirectory, "TestApp");
var libDirectory = Path.Combine(solutionDirectory, "TestLibrary");
var projectContext = ProjectContext.Create(appDirectory, FrameworkConstants.CommonFrameworks.NetCoreApp10);
var mockProj = ProjectRootElement.Create();
var testSettings = new MigrationSettings(appDirectory, appDirectory, "1.0.0", mockProj);
var testInputs = new MigrationRuleInputs(new[] {projectContext}, mockProj, mockProj.AddItemGroup(),
mockProj.AddPropertyGroup());
new MigrateProjectDependenciesRule().Apply(testSettings, testInputs);
var autoUnify = mockProj.Properties.Where(p => p.Name == "AutoUnify");
autoUnify.Count().Should().Be(1);
autoUnify.First().Value.Should().Be("true");
var designTimeAutoUnify = mockProj.Properties.Where(p => p.Name == "DesignTimeAutoUnify");
designTimeAutoUnify.Count().Should().Be(1);
designTimeAutoUnify.First().Value.Should().Be("true");
}
[Fact]
public void Project_dependencies_are_migrated_to_ProjectReference()
{
var solutionDirectory =
TestAssetsManager.CreateTestInstance("TestAppWithLibrary", callingMethod: "p").WithLockFiles().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 testInputs = new MigrationRuleInputs(new[] {projectContext}, mockProj, mockProj.AddItemGroup(),
mockProj.AddPropertyGroup());
new MigrateProjectDependenciesRule().Apply(testSettings, testInputs);
var projectReferences = mockProj.Items.Where(item => item.ItemType.Equals("ProjectReference", StringComparison.Ordinal));
projectReferences.Count().Should().Be(1);
projectReferences.First().Include.Should().Be(Path.Combine("..", "TestLibrary", "TestLibrary.csproj"));
}
[Fact]
public void It_throws_when_project_dependency_is_unresolved()
{
// No Lock file => unresolved
var solutionDirectory =
TestAssetsManager.CreateTestInstance("TestAppWithLibrary").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 testInputs = new MigrationRuleInputs(new[] {projectContext}, mockProj, mockProj.AddItemGroup(), mockProj.AddPropertyGroup());
Action action = () => new MigrateProjectDependenciesRule().Apply(testSettings, testInputs);
action.ShouldThrow<Exception>()
.Where(e => e.Message.Contains("MIGRATE1014::Unresolved Dependency: Unresolved project dependency (TestLibrary)"));
}
}
}

View file

@ -0,0 +1,148 @@
using System;
using System.IO;
using System.Linq;
using Microsoft.Build.Construction;
using Microsoft.DotNet.Migration.Tests;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.TestFramework;
using Microsoft.DotNet.Tools.Test.Utilities;
using NuGet.Frameworks;
using Xunit;
using FluentAssertions;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
public class GivenThatIWantToMigratePublishOptions : TestBase
{
[Fact]
private void Migrating_publishOptions_include_exclude_populates_Content_item()
{
var testDirectory = Temp.CreateDirectory().Path;
WriteFilesInProjectDirectory(testDirectory);
var mockProj = RunPublishOptionsRuleOnPj(@"
{
""publishOptions"": {
""include"": [""root"", ""src"", ""rootfile.cs""],
""exclude"": [""src"", ""rootfile.cs""],
""includeFiles"": [""src/file1.cs"", ""src/file2.cs""],
""excludeFiles"": [""src/file2.cs""]
}
}",
testDirectory: testDirectory);
mockProj.Items.Count(i => i.ItemType.Equals("Content", StringComparison.Ordinal)).Should().Be(2);
foreach (var item in mockProj.Items.Where(i => i.ItemType.Equals("Content", StringComparison.Ordinal)))
{
item.Metadata.Count(m => m.Name == "CopyToPublishDirectory").Should().Be(1);
if (item.Include.Contains(@"src\file1.cs"))
{
item.Include.Should().Be(@"src\file1.cs;src\file2.cs");
item.Exclude.Should().Be(@"src\file2.cs");
}
else
{
item.Include.Should()
.Be(@"root\**\*;src\**\*;rootfile.cs");
item.Exclude.Should()
.Be(@"src\**\*;rootfile.cs;src\file2.cs");
}
}
}
[Fact]
private void Migrating_publishOptions_and_buildOptions_CopyToOutput_merges_Content_items()
{
var testDirectory = Temp.CreateDirectory().Path;
WriteFilesInProjectDirectory(testDirectory);
var mockProj = RunPublishAndBuildOptionsRuleOnPj(@"
{
""buildOptions"": {
""copyToOutput"": {
""include"": [""src"", ""rootfile.cs""],
""exclude"": [""src"", ""rootfile.cs""],
""includeFiles"": [""src/file1.cs"", ""src/file2.cs""],
""excludeFiles"": [""src/file2.cs""]
}
},
""publishOptions"": {
""include"": [""root"", ""src"", ""rootfile.cs""],
""exclude"": [""src"", ""rootfile.cs""],
""includeFiles"": [""src/file1.cs"", ""src/file2.cs""],
""excludeFiles"": [""src/file3.cs""]
}
}",
testDirectory: testDirectory);
Console.WriteLine(string.Join(";", mockProj.Items.Select(i => " ;; " + i.ItemType)));
Console.WriteLine(string.Join(";", mockProj.Items.Select(i => " ;; " + i.Include)));
Console.WriteLine(string.Join(";", mockProj.Items.Select(i => " ;; " + i.Exclude)));
mockProj.Items.Count(i => i.ItemType.Equals("Content", StringComparison.Ordinal)).Should().Be(3);
// From ProjectReader #L725 (Both are empty)
var defaultIncludePatterns = Enumerable.Empty<string>();
var defaultExcludePatterns = ProjectFilesCollection.DefaultPublishExcludePatterns;
foreach (var item in mockProj.Items.Where(i => i.ItemType.Equals("Content", StringComparison.Ordinal)))
{
if (item.Include.Contains(@"root\**\*"))
{
item.Include.Should().Be(@"root\**\*");
item.Exclude.Should().Be(@"src\**\*;rootfile.cs;src\file3.cs");
}
else if (item.Include.Contains(@"src\file1.cs"))
{
item.Include.Should().Be(@"src\file1.cs;src\file2.cs");
item.Exclude.Should().Be(@"src\file2.cs;src\file3.cs");
}
else
{
item.Include.Should()
.Be(@"src\**\*;rootfile.cs");
item.Exclude.Should()
.Be(@"src\**\*;rootfile.cs;src\file2.cs;src\file3.cs");
}
}
}
private void WriteFilesInProjectDirectory(string testDirectory)
{
Directory.CreateDirectory(Path.Combine(testDirectory, "root"));
Directory.CreateDirectory(Path.Combine(testDirectory, "src"));
File.WriteAllText(Path.Combine(testDirectory, "root", "file1.txt"), "content");
File.WriteAllText(Path.Combine(testDirectory, "root", "file2.txt"), "content");
File.WriteAllText(Path.Combine(testDirectory, "root", "file3.txt"), "content");
File.WriteAllText(Path.Combine(testDirectory, "src", "file1.cs"), "content");
File.WriteAllText(Path.Combine(testDirectory, "src", "file2.cs"), "content");
File.WriteAllText(Path.Combine(testDirectory, "src", "file3.cs"), "content");
File.WriteAllText(Path.Combine(testDirectory, "rootfile.cs"), "content");
}
private ProjectRootElement RunPublishOptionsRuleOnPj(string s, string testDirectory = null)
{
testDirectory = testDirectory ?? Temp.CreateDirectory().Path;
return TemporaryProjectFileRuleRunner.RunRules(new IMigrationRule[]
{
new MigratePublishOptionsRule()
}, s, testDirectory);
}
private ProjectRootElement RunPublishAndBuildOptionsRuleOnPj(string s, string testDirectory = null)
{
testDirectory = testDirectory ?? Temp.CreateDirectory().Path;
return TemporaryProjectFileRuleRunner.RunRules(new IMigrationRule[]
{
new MigrateBuildOptionsRule(),
new MigratePublishOptionsRule()
}, s, testDirectory);
}
}
}

View file

@ -0,0 +1,67 @@
// 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.Linq;
using FluentAssertions;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
using Microsoft.DotNet.ProjectJsonMigration;
using System;
using System.IO;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectModel;
using NuGet.Frameworks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
public class GivenThatIWantToMigrateRuntimeOptions : TestBase
{
private static readonly string s_runtimeConfigFileName = "runtimeconfig.template.json";
[Fact]
public void RuntimeOptions_are_copied_from_projectJson_to_runtimeconfig_template_json_file()
{
var testInstance = TestAssetsManager.CreateTestInstance("TestAppWithRuntimeOptions").WithLockFiles();
var projectDir = testInstance.Path;
var projectPath = Path.Combine(testInstance.Path, "project.json");
var project = JObject.Parse(File.ReadAllText(projectPath));
var rawRuntimeOptions = (JObject)project.GetValue("runtimeOptions");
var projectContext = ProjectContext.Create(projectDir, FrameworkConstants.CommonFrameworks.NetCoreApp10);
var testSettings = new MigrationSettings(projectDir, projectDir, "1.0.0", default(ProjectRootElement));
var testInputs = new MigrationRuleInputs(new[] { projectContext }, null, null, null);
new MigrateRuntimeOptionsRule().Apply(testSettings, testInputs);
var migratedRuntimeOptionsPath = Path.Combine(projectDir, s_runtimeConfigFileName);
File.Exists(migratedRuntimeOptionsPath).Should().BeTrue();
Console.WriteLine(migratedRuntimeOptionsPath);
var migratedRuntimeOptionsContent = JObject.Parse(File.ReadAllText(migratedRuntimeOptionsPath));
JToken.DeepEquals(rawRuntimeOptions, migratedRuntimeOptionsContent).Should().BeTrue();
}
[Fact]
public void Migrating_ProjectJson_with_no_RuntimeOptions_produces_no_runtimeconfig_template_json_file()
{
var testInstance = TestAssetsManager.CreateTestInstance("TestAppSimple").WithLockFiles();
var projectDir = testInstance.Path;
var projectContext = ProjectContext.Create(projectDir, FrameworkConstants.CommonFrameworks.NetCoreApp10);
var testSettings = new MigrationSettings(projectDir, projectDir, "1.0.0", default(ProjectRootElement));
var testInputs = new MigrationRuleInputs(new[] { projectContext }, null, null, null);
new MigrateRuntimeOptionsRule().Apply(testSettings, testInputs);
var migratedRuntimeOptionsPath = Path.Combine(projectDir, s_runtimeConfigFileName);
File.Exists(migratedRuntimeOptionsPath).Should().BeFalse();
}
}
}

View file

@ -0,0 +1,206 @@
// 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.Linq;
using FluentAssertions;
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;
using Microsoft.DotNet.ProjectJsonMigration;
using System;
using System.IO;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
public class GivenThatIWantToMigrateScripts : TestBase
{
[Theory]
[InlineData("compile:FullTargetFramework", "$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion)")]
[InlineData("compile:Configuration", "$(Configuration)")]
[InlineData("compile:OutputFile", "$(TargetPath)")]
[InlineData("compile:OutputDir", "$(TargetDir)")]
[InlineData("publish:ProjectPath", "$(MSBuildThisFileDirectory)")]
[InlineData("publish:Configuration", "$(Configuration)")]
[InlineData("publish:OutputPath", "$(TargetDir)")]
[InlineData("publish:FullTargetFramework", "$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion)")]
[InlineData("project:Version", "$(Version)")]
[InlineData("project:Name", "$(AssemblyName)")]
[InlineData("project:Directory", "$(MSBuildProjectDirectory)")]
[InlineData("publish:Runtime", "$(RuntimeIdentifier)")]
public void Formatting_script_commands_replaces_variables_with_the_right_msbuild_properties(
string variable,
string msbuildReplacement)
{
var scriptMigrationRule = new MigrateScriptsRule();
scriptMigrationRule.ReplaceScriptVariables($"%{variable}%").Should().Be(msbuildReplacement);
}
[Theory]
[InlineData("compile:TargetFramework")]
[InlineData("compile:ResponseFile")]
[InlineData("compile:CompilerExitCode")]
[InlineData("compile:RuntimeOutputDir")]
[InlineData("compile:RuntimeIdentifier")]
[InlineData("publish:TargetFramework")]
public void Formatting_script_commands_throws_when_variable_is_unsupported(string unsupportedVariable)
{
var scriptMigrationRule = new MigrateScriptsRule();
Action formatScriptAction = () => scriptMigrationRule.ReplaceScriptVariables($"%{unsupportedVariable}%");
formatScriptAction.ShouldThrow<Exception>()
.Where(exc => exc.Message.Contains("is currently an unsupported script variable for project migration"));
}
[Theory]
[InlineData("precompile", "Build")]
[InlineData("prepublish", "Publish")]
public void Migrating_pre_scripts_populates_BeforeTargets_with_appropriate_target(string scriptName, string targetName)
{
var scriptMigrationRule = new MigrateScriptsRule();
ProjectRootElement mockProj = ProjectRootElement.Create();
var commands = new string[] { "fakecommand" };
var target = scriptMigrationRule.MigrateScriptSet(mockProj, mockProj.AddPropertyGroup(), commands, scriptName);
target.BeforeTargets.Should().Be(targetName);
}
[Theory]
[InlineData("postcompile", "Build")]
[InlineData("postpublish", "Publish")]
public void Migrating_post_scripts_populates_AfterTargets_with_appropriate_target(string scriptName, string targetName)
{
var scriptMigrationRule = new MigrateScriptsRule();
ProjectRootElement mockProj = ProjectRootElement.Create();
var commands = new string[] { "fakecommand" };
var target = scriptMigrationRule.MigrateScriptSet(mockProj, mockProj.AddPropertyGroup(), commands, scriptName);
target.AfterTargets.Should().Be(targetName);
}
[Theory]
[InlineData("precompile")]
[InlineData("postcompile")]
[InlineData("prepublish")]
[InlineData("postpublish")]
public void Migrating_scripts_with_multiple_commands_creates_Exec_task_for_each(string scriptName)
{
var scriptMigrationRule = new MigrateScriptsRule();
ProjectRootElement mockProj = ProjectRootElement.Create();
var commands = new string[] { "fakecommand1", "fakecommand2", "mockcommand3" };
var commandsInTask = commands.ToDictionary(c => c, c => false);
var target = scriptMigrationRule.MigrateScriptSet(mockProj, mockProj.AddPropertyGroup(), commands, scriptName);
foreach (var task in target.Tasks)
{
var taskCommand = task.GetParameter("Command");
var originalCommandCandidates = commands.Where(c => taskCommand.Contains(c));
originalCommandCandidates.Count().Should().Be(1);
var command = originalCommandCandidates.First();
commandsInTask[command].Should().Be(false, "Expected to find each element from commands Array once");
commandsInTask[command] = true;
}
commandsInTask.All(commandInTask => commandInTask.Value)
.Should()
.BeTrue("Expected each element from commands array to be found in a task");
}
[Theory]
[InlineData("precompile")]
[InlineData("postcompile")]
[InlineData("prepublish")]
[InlineData("postpublish")]
public void Migrated_ScriptSet_has_Exec_and_replaces_variables(string scriptName)
{
var scriptMigrationRule = new MigrateScriptsRule();
ProjectRootElement mockProj = ProjectRootElement.Create();
var commands = new string[] { "compile:FullTargetFramework", "compile:Configuration"};
var target = scriptMigrationRule.MigrateScriptSet(mockProj, mockProj.AddPropertyGroup(), commands, scriptName);
target.Tasks.Count().Should().Be(commands.Length);
foreach (var task in target.Tasks)
{
var taskCommand = task.GetParameter("Command");
var commandIndex = Array.IndexOf(commands, taskCommand);
commandIndex.Should().Be(-1, "Expected command array elements to be replaced by appropriate msbuild properties");
}
}
[Theory]
[InlineData("precompile")]
[InlineData("postcompile")]
[InlineData("prepublish")]
[InlineData("postpublish")]
public void Migrated_ScriptSet_has_two_MigratedScriptExtensionProperties_for_each_script(string scriptName)
{
var scriptMigrationRule = new MigrateScriptsRule();
ProjectRootElement mockProj = ProjectRootElement.Create();
var commands = new string[] {"compile:FullTargetFramework", "compile:Configuration"};
var propertyGroup = mockProj.AddPropertyGroup();
var target = scriptMigrationRule.MigrateScriptSet(mockProj, propertyGroup, commands,
scriptName);
Console.WriteLine(string.Join(";", propertyGroup.Properties.Select(n => n.Name)));
propertyGroup.Properties.Count().Should().Be(commands.Length * 2);
var count = 0;
foreach (var command in commands)
{
count += 1;
var scriptExtensionProperties =
propertyGroup.Properties.Where(p => p.Name.Contains($"MigratedScriptExtension_{scriptName}_{count}")).ToArray();
scriptExtensionProperties.All(p => p.Value == ".sh" || p.Value == ".cmd").Should().BeTrue();
scriptExtensionProperties.Count().Should().Be(2);
}
}
[Theory]
[InlineData("echo", ".\\echo$(MigratedScriptExtension_1)")]
[InlineData("echo hello world", ".\\echo$(MigratedScriptExtension_1) hello world")]
[InlineData("\"echo\"", ".\\\"echo$(MigratedScriptExtension_1)\"")]
[InlineData("\"echo space\"", ".\\\"echo space$(MigratedScriptExtension_1)\"")]
[InlineData("\"echo space\" other args", ".\\\"echo space$(MigratedScriptExtension_1)\" other args")]
[InlineData("\"echo space\" \"other space\"", ".\\\"echo space$(MigratedScriptExtension_1)\" \"other space\"")]
public void Migrated_ScriptSet_has_ScriptExtension_added_to_script_command(string scriptCommandline, string expectedOutputCommand)
{
var scriptMigrationRule = new MigrateScriptsRule();
ProjectRootElement mockProj = ProjectRootElement.Create();
var formattedCommand = scriptMigrationRule.AddScriptExtensionPropertyToCommandLine(scriptCommandline,
"MigratedScriptExtension_1");
formattedCommand.Should().Be(expectedOutputCommand);
}
[Theory]
[InlineData("echo", @".\echo")]
[InlineData("/usr/echo", "/usr/echo")]
[InlineData(@"C:\usr\echo", @"C:\usr\echo")]
[InlineData("\"echo\"", @".\""echo")]
[InlineData("\"/usr/echo\"", @"""/usr/echo")]
[InlineData(@"""C:\usr\echo", @"""C:\usr\echo")]
public void Migrated_ScriptSet_has_dotSlash_prepended_when_command_is_not_rooted(string scriptCommandline,
string expectedOutputCommandPrefix)
{
var scriptMigrationRule = new MigrateScriptsRule();
ProjectRootElement mockProj = ProjectRootElement.Create();
var formattedCommand = scriptMigrationRule.FormatScriptCommand(scriptCommandline,
"MigratedScriptExtension_1");
formattedCommand.Should().StartWith(expectedOutputCommandPrefix);
}
}
}

View file

@ -0,0 +1,44 @@
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Tools.Test.Utilities;
using NuGet.Frameworks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using FluentAssertions;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
public class GivenThatIWantToMigrateProjectFramework : TestBase
{
[Fact]
public void Migrating_netcoreapp_project_Populates_TargetFrameworkIdentifier_and_TargetFrameworkVersion()
{
var testDirectory = Temp.CreateDirectory().Path;
var testPJ = new ProjectJsonBuilder(TestAssetsManager)
.FromTestAssetBase("TestAppWithRuntimeOptions")
.WithCustomProperty("buildOptions", new Dictionary<string, string>
{
{ "emitEntryPoint", "false" }
})
.SaveToDisk(testDirectory);
var projectContext = ProjectContext.Create(testDirectory, FrameworkConstants.CommonFrameworks.NetCoreApp10);
var mockProj = ProjectRootElement.Create();
// Run BuildOptionsRule
var testSettings = new MigrationSettings(testDirectory, testDirectory, "1.0.0", mockProj);
var testInputs = new MigrationRuleInputs(new[] { projectContext }, mockProj, mockProj.AddItemGroup(), mockProj.AddPropertyGroup());
new MigrateTFMRule().Apply(testSettings, testInputs);
mockProj.Properties.Count(p => p.Name == "TargetFrameworkIdentifier").Should().Be(1);
mockProj.Properties.Count(p => p.Name == "TargetFrameworkVersion").Should().Be(1);
mockProj.Properties.First(p => p.Name == "TargetFrameworkIdentifier").Value.Should().Be(".NETCoreApp");
mockProj.Properties.First(p => p.Name == "TargetFrameworkVersion").Value.Should().Be("v1.0");
}
}
}

View file

@ -0,0 +1,44 @@
using System.Collections.Generic;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration.Rules;
using Microsoft.DotNet.ProjectModel;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
public class TemporaryProjectFileRuleRunner
{
public static ProjectRootElement RunRules(IEnumerable<IMigrationRule> rules, string projectJson,
string testDirectory)
{
var projectContext = GenerateProjectContextFromString(testDirectory, projectJson);
return RunMigrationRulesOnGeneratedProject(rules, projectContext, testDirectory);
}
private static ProjectContext GenerateProjectContextFromString(string projectDirectory, string json)
{
var testPj = new ProjectJsonBuilder(null)
.FromStringBase(json)
.SaveToDisk(projectDirectory);
return ProjectContext.Create(testPj, FrameworkConstants.CommonFrameworks.NetCoreApp10);
}
private static ProjectRootElement RunMigrationRulesOnGeneratedProject(IEnumerable<IMigrationRule> rules,
ProjectContext projectContext, string testDirectory)
{
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());
foreach (var rule in rules)
{
rule.Apply(testSettings, testInputs);
}
return project;
}
}
}

View file

@ -0,0 +1,45 @@
using Microsoft.DotNet.ProjectJsonMigration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using FluentAssertions;
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration.Transforms;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests
{
public class GivenAConditionalTransform
{
[Fact]
public void It_returns_null_when_condition_is_false()
{
var conditionalTransform = new TestConditionalTransform(t => false);
conditionalTransform.Transform("astring").Should().BeNull();
}
[Fact]
public void It_returns_result_of_ConditionallyTransform_when_condition_is_true()
{
var conditionalTransform = new TestConditionalTransform(t => true);
var property = conditionalTransform.Transform("astring");
property.Should().NotBeNull();
property.Name.Should().Be("astring");
property.Value.Should().Be("astring");
}
private class TestConditionalTransform : ConditionalTransform<string, ProjectPropertyElement>
{
public TestConditionalTransform(Func<string, bool> condition) : base(condition) { }
public override ProjectPropertyElement ConditionallyTransform(string source)
{
var property = ProjectRootElement.Create().CreatePropertyElement(source);
property.Value = source;
return property;
}
}
}
}

View file

@ -0,0 +1,68 @@
using System;
using Microsoft.Build.Construction;
using Xunit;
using Xunit.Runner.DotNet;
using FluentAssertions;
using System.Linq;
using Microsoft.DotNet.ProjectJsonMigration.Models;
using Microsoft.DotNet.ProjectJsonMigration.Transforms;
namespace Microsoft.DotNet.ProjectJsonMigration.Tests.Transforms
{
public class GivenATransformApplicator
{
[Fact]
public void It_merges_Metadata_and_Exclude_with_items_with_same_ItemType_and_Include_when_mergeExisting_is_true()
{
var metadata = new ItemMetadataValue<string>[]
{
new ItemMetadataValue<string>("metadata1", "value1"),
new ItemMetadataValue<string>("metadata2", "value2")
};
var fullItemTransformSetIncludeValue = "include1;include2";
var transform1 = new AddItemTransform<string>("item",
fullItemTransformSetIncludeValue,
"exclude1",
t => true)
.WithMetadata(metadata[0]);
var transform2 = new AddItemTransform<string>("item",
fullItemTransformSetIncludeValue,
"exclude2",
t => true)
.WithMetadata(metadata[1]);
var mockProj = ProjectRootElement.Create();
var itemGroup = mockProj.AddItemGroup();
var item1 = transform1.Transform("_");
item1.AddMetadata(metadata[0].MetadataName, metadata[0].GetMetadataValue(null));
var item2 = transform2.Transform("_");
item2.AddMetadata(metadata[1].MetadataName, metadata[1].GetMetadataValue(null));
var transformApplicator = new TransformApplicator();
transformApplicator.Execute(new ProjectItemElement[] {item1, item2}, itemGroup, mergeExisting:true);
itemGroup.Items.Count.Should().Be(1);
var item = itemGroup.Items.First();
item.Exclude.Should().Be("exclude1;exclude2");
item.Metadata.Count().Should().Be(2);
var foundMetadata = metadata.ToDictionary<ItemMetadataValue<string>, string, bool>(m => m.MetadataName,
m => false);
foreach (var metadataEntry in item.Metadata)
{
foundMetadata.Should().ContainKey(metadataEntry.Name);
foundMetadata[metadataEntry.Name].Should().BeFalse();
foundMetadata[metadataEntry.Name] = true;
}
foundMetadata.All(kv => kv.Value).Should().BeTrue();
}
}
}

View file

@ -0,0 +1,86 @@
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using FluentAssertions;
using Microsoft.DotNet.ProjectJsonMigration.Models;
using Microsoft.DotNet.ProjectJsonMigration.Transforms;
namespace Microsoft.DotNet.Migration.Tests
{
public class GivenAnAddItemTransform
{
[Fact]
public void It_returns_an_item_with_Include_Exclude_and_Metadata_to_project_when_condition_is_true()
{
var itemTransforms = GetFullItemTransformSet(true);
foreach (var transform in itemTransforms)
{
var item = transform.Transform("_");
item.Should().NotBeNull();
item.Include.Should().Be(FullItemTransformSetIncludeValue);
item.Exclude.Should().Be(FullItemTransformSetExcludeValue);
item.HasMetadata.Should().BeTrue();
var metadata = item.Metadata.First();
metadata.Name.Should().Be(FullItemTransformSetMetadataName);
metadata.Value.Should().Be(FullItemTransformSetMetadataValue);
}
}
[Fact]
public void It_returns_null_when_condition_is_false()
{
var itemTransforms = GetFullItemTransformSet(false);
foreach (var transform in itemTransforms)
{
transform.Transform("_").Should().BeNull();
}
}
private static string FullItemTransformSetItemNamePrefix => "item";
private static string FullItemTransformSetIncludeValue => "include1;include2";
private static string FullItemTransformSetExcludeValue => "exclude1;exclude2";
private static string FullItemTransformSetMetadataName => "SomeName";
private static string FullItemTransformSetMetadataValue => "SomeValue";
private AddItemTransform<string>[] GetFullItemTransformSet(bool condition)
{
return new AddItemTransform<string>[]
{
new AddItemTransform<string>(FullItemTransformSetItemNamePrefix + "1",
FullItemTransformSetIncludeValue.Split(';'),
FullItemTransformSetExcludeValue.Split(';'),
t => condition)
.WithMetadata(FullItemTransformSetMetadataName, FullItemTransformSetMetadataValue),
new AddItemTransform<string>(FullItemTransformSetItemNamePrefix + "2",
t => FullItemTransformSetIncludeValue,
t => FullItemTransformSetExcludeValue,
t => condition)
.WithMetadata(FullItemTransformSetMetadataName, t => FullItemTransformSetMetadataValue),
new AddItemTransform<string>(FullItemTransformSetItemNamePrefix + "3",
FullItemTransformSetIncludeValue,
t => FullItemTransformSetExcludeValue,
t => condition)
.WithMetadata(new ItemMetadataValue<string>(FullItemTransformSetMetadataName, FullItemTransformSetMetadataValue)),
new AddItemTransform<string>(FullItemTransformSetItemNamePrefix + "4",
t => FullItemTransformSetIncludeValue,
FullItemTransformSetExcludeValue,
t => condition)
.WithMetadata(new ItemMetadataValue<string>(FullItemTransformSetMetadataName, t => FullItemTransformSetMetadataValue)),
new AddItemTransform<string>(FullItemTransformSetItemNamePrefix + "5",
FullItemTransformSetIncludeValue,
FullItemTransformSetExcludeValue,
t => condition)
.WithMetadata(FullItemTransformSetMetadataName, FullItemTransformSetMetadataValue)
};
}
}
}

View file

@ -0,0 +1,94 @@
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using FluentAssertions;
using Microsoft.DotNet.ProjectJsonMigration.Transforms;
namespace Microsoft.DotNet.Migration.Tests
{
public class GivenAnAddPropertyTransform
{
[Fact]
public void It_returns_a_property_with_specified_value()
{
var propertyName = "Property1";
var propertyValue = "Value1";
var propertyTransform = new AddPropertyTransform<string>(propertyName, propertyValue, t=>true);
var property = propertyTransform.Transform("_");
property.Name.Should().Be(propertyName);
property.Value.Should().Be(propertyValue);
}
[Fact]
public void It_returns_a_property_with_computed_value()
{
var propertyName = "Property1";
var propertyValue = "Value1";
var propertyTransform = new AddPropertyTransform<string>(propertyName, t => t.ToUpper(), t => true);
var property = propertyTransform.Transform(propertyValue);
property.Name.Should().Be(propertyName);
property.Value.Should().Be(propertyValue.ToUpper());
}
[Fact]
public void It_returns_null_when_condition_is_false()
{
var propertyName = "Property1";
var propertyValue = "Value1";
var propertyTransform = new AddPropertyTransform<string>(propertyName, propertyValue, t => false);
propertyTransform.Transform(propertyValue).Should().BeNull();
}
[Fact]
public void It_returns_a_property_when_source_is_null_and_propertyValue_is_a_string()
{
var propertyName = "Property1";
var propertyValue = "Value1";
var propertyTransform = new AddPropertyTransform<string>(
propertyName,
propertyValue,
t => true);
var property = propertyTransform.Transform(null);
property.Should().NotBeNull();
property.Value.Should().Be(propertyValue);
}
[Fact]
public void It_returns_a_property_when_source_is_null_and_propertyValue_is_a_Func_that_handles_null()
{
var propertyName = "Property1";
var propertyValue = "Value1";
var propertyTransform = new AddPropertyTransform<string>(
propertyName,
t=> t == null ? propertyValue.ToUpper() : propertyValue.ToLower(),
t => true);
var property = propertyTransform.Transform(null);
property.Value.Should().Be(propertyValue.ToUpper());
}
[Fact]
public void It_throws_when_source_is_null_and_propertyValue_is_a_Func_that_doesnt_handle_null()
{
var propertyName = "Property1";
var propertyTransform = new AddPropertyTransform<string>(
propertyName,
t => t.ToUpper(),
t => true);
Action transform = () => propertyTransform.Transform(null);
transform.ShouldThrow<Exception>();
}
}
}

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.DotNet.ProjectJsonMigration.Transforms;
namespace Microsoft.DotNet.Migration.Tests
{
public class GivenAnIncludeContextTransformation
{
}
}

View file

@ -0,0 +1,39 @@
{
"version": "1.0.0-*",
"buildOptions": {
"copyToOutput": ["MSBuild.exe", "MSBuild.exe.config"]
},
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
},
"Microsoft.DotNet.ProjectJsonMigration": {
"target": "project"
},
"xunit": "2.2.0-beta3-build3330",
"dotnet-test-xunit": "1.0.0-rc2-318883-21",
"FluentAssertions": "4.0.0",
"moq.netcore": "4.4.0-beta8",
"Microsoft.DotNet.Tools.Tests.Utilities": {
"target": "project"
},
"Microsoft.DotNet.Cli.Utils": {
"target": "project"
},
"dotnet": {
"target":"project"
}
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
"netstandardapp1.5",
"dotnet5.4",
"portable-net451+win8"
]
}
},
"testRunner": "xunit"
}

View file

@ -18,5 +18,11 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
args = $"new {args}";
return base.Execute(args);
}
public override CommandResult ExecuteWithCapturedOutput(string args = "")
{
args = $"new {args}";
return base.ExecuteWithCapturedOutput(args);
}
}
}

View file

@ -0,0 +1,253 @@
using Microsoft.Build.Construction;
using Microsoft.DotNet.ProjectJsonMigration;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Tools.Test.Utilities;
using NuGet.Frameworks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using FluentAssertions;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.DotNet.Tools.Common;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Tools.Migrate;
using Build3Command = Microsoft.DotNet.Tools.Test.Utilities.Build3Command;
namespace Microsoft.DotNet.Migration.Tests
{
public class GivenThatIWantToMigrateTestApps : TestBase
{
[Theory]
// TODO: Standalone apps [InlineData("TestAppSimple", false)]
// https://github.com/dotnet/sdk/issues/73 [InlineData("TestAppWithLibrary/TestApp", false)]
[InlineData("TestAppWithRuntimeOptions")]
public void It_migrates_apps(string projectName)
{
var projectDirectory = TestAssetsManager.CreateTestInstance(projectName, callingMethod: "i").WithLockFiles().Path;
var outputComparisonData = BuildProjectJsonMigrateBuildMSBuild(projectDirectory);
var outputsIdentical =
outputComparisonData.ProjectJsonBuildOutputs.SetEquals(outputComparisonData.MSBuildBuildOutputs);
if (!outputsIdentical)
{
OutputDiagnostics(outputComparisonData);
}
outputsIdentical.Should().BeTrue();
VerifyAllMSBuildOutputsRunnable(projectDirectory);
}
[Fact]
public void It_migrates_dotnet_new_console_with_identical_outputs()
{
var projectDirectory = Temp.CreateDirectory().Path;
var outputComparisonData = GetDotnetNewComparisonData(projectDirectory, "console");
var outputsIdentical =
outputComparisonData.ProjectJsonBuildOutputs.SetEquals(outputComparisonData.MSBuildBuildOutputs);
if (!outputsIdentical)
{
OutputDiagnostics(outputComparisonData);
}
outputsIdentical.Should().BeTrue();
VerifyAllMSBuildOutputsRunnable(projectDirectory);
}
[Fact]
// Outputs Not Identical: https://github.com/dotnet/sdk/issues/97
public void It_migrates_dotnet_new_web_with_outputs_containing_project_json_outputs()
{
var projectDirectory = Temp.CreateDirectory().Path;
var outputComparisonData = GetDotnetNewComparisonData(projectDirectory, "web");
var projectJsonOutputIsSubsetOfMSBuildOutput =
outputComparisonData.ProjectJsonBuildOutputs.IsProperSubsetOf(outputComparisonData.MSBuildBuildOutputs);
if (!projectJsonOutputIsSubsetOfMSBuildOutput)
{
OutputDiagnostics(outputComparisonData);
}
projectJsonOutputIsSubsetOfMSBuildOutput.Should().BeTrue();
}
[Theory]
[InlineData("TestAppWithLibrary/TestLibrary")]
[InlineData("TestLibraryWithAnalyzer")]
[InlineData("TestLibraryWithConfiguration")]
public void It_migrates_a_library(string projectName)
{
var projectDirectory =
TestAssetsManager.CreateTestInstance(projectName, callingMethod: "i").WithLockFiles().Path;
var outputComparisonData = BuildProjectJsonMigrateBuildMSBuild(projectDirectory);
var msBuildHasAdditionalOutputsButIncludesProjectJsonOutputs =
outputComparisonData.ProjectJsonBuildOutputs.IsProperSubsetOf(outputComparisonData.MSBuildBuildOutputs);
if (!msBuildHasAdditionalOutputsButIncludesProjectJsonOutputs)
{
OutputDiagnostics(outputComparisonData);
}
msBuildHasAdditionalOutputsButIncludesProjectJsonOutputs.Should().BeTrue();
}
[Fact]
public void It_migrates_an_app_with_scripts_and_the_scripts_run()
{
var projectDirectory =
TestAssetsManager.CreateTestInstance("TestAppWithMigrateableScripts", callingMethod: "i").WithLockFiles().Path;
BuildProjectJson(projectDirectory);
var projectJsonBuildOutputs = new HashSet<string>(CollectBuildOutputs(projectDirectory));
CleanBinObj(projectDirectory);
MigrateProject(projectDirectory);
Restore(projectDirectory);
var msBuildStdOut = BuildMSBuild(projectDirectory);
var msbuildBuildOutputs = new HashSet<string>(CollectBuildOutputs(projectDirectory));
var outputsIdentical = projectJsonBuildOutputs.SetEquals(msbuildBuildOutputs);
outputsIdentical.Should().BeTrue();
VerifyAllMSBuildOutputsRunnable(projectDirectory);
var outputDir =
PathUtility.EnsureTrailingSlash(Path.Combine(projectDirectory, "bin", "Debug", "netcoreapp1.0"));
msBuildStdOut.Should().Contain($"precompile_output ?Debug? ?{outputDir}? ?.NETCoreApp,Version=v1.0?");
msBuildStdOut.Should().Contain($"postcompile_output ?Debug? ?{outputDir}? ?.NETCoreApp,Version=v1.0?");
}
private MigratedBuildComparisonData GetDotnetNewComparisonData(string projectDirectory, string dotnetNewType)
{
DotnetNew(projectDirectory, dotnetNewType);
Restore(projectDirectory);
var outputComparisonData = BuildProjectJsonMigrateBuildMSBuild(projectDirectory);
return outputComparisonData;
}
private void VerifyAllMSBuildOutputsRunnable(string projectDirectory)
{
var dllFileName = Path.GetFileName(projectDirectory) + ".dll";
var runnableDlls = Directory.EnumerateFiles(Path.Combine(projectDirectory, "bin"), dllFileName,
SearchOption.AllDirectories);
foreach (var dll in runnableDlls)
{
new TestCommand("dotnet").ExecuteWithCapturedOutput(dll).Should().Pass();
}
}
private MigratedBuildComparisonData BuildProjectJsonMigrateBuildMSBuild(string projectDirectory)
{
BuildProjectJson(projectDirectory);
var projectJsonBuildOutputs = new HashSet<string>(CollectBuildOutputs(projectDirectory));
CleanBinObj(projectDirectory);
MigrateProject(projectDirectory);
Restore(projectDirectory);
BuildMSBuild(projectDirectory);
var msbuildBuildOutputs = new HashSet<string>(CollectBuildOutputs(projectDirectory));
return new MigratedBuildComparisonData(projectJsonBuildOutputs, msbuildBuildOutputs);
}
private IEnumerable<string> CollectBuildOutputs(string projectDirectory)
{
var fullBinPath = Path.GetFullPath(Path.Combine(projectDirectory, "bin"));
return Directory.EnumerateFiles(fullBinPath, "*", SearchOption.AllDirectories)
.Select(p => Path.GetFullPath(p).Substring(fullBinPath.Length));
}
private void CleanBinObj(string projectDirectory)
{
var dirs = new string[] { Path.Combine(projectDirectory, "bin"), Path.Combine(projectDirectory, "obj") };
foreach (var dir in dirs)
{
Directory.Delete(dir, true);
}
}
private void BuildProjectJson(string projectDirectory)
{
var projectFile = Path.Combine(projectDirectory, "project.json");
var result = new BuildCommand(projectPath: projectFile)
.ExecuteWithCapturedOutput();
result.Should().Pass();
}
private void MigrateProject(string projectDirectory)
{
var result =
MigrateCommand.Run(new [] { "-p", projectDirectory });
result.Should().Be(0);
}
private void DotnetNew(string projectDirectory, string dotnetNewType)
{
new NewCommand().WithWorkingDirectory(projectDirectory)
.ExecuteWithCapturedOutput($"-t {dotnetNewType}")
.Should()
.Pass();
}
private void Restore(string projectDirectory)
{
new TestCommand("dotnet")
.WithWorkingDirectory(projectDirectory)
.Execute("restore")
.Should()
.Pass();
}
private string BuildMSBuild(string projectDirectory, string configuration="Debug")
{
var result = new Build3Command()
.WithWorkingDirectory(projectDirectory)
.ExecuteWithCapturedOutput($"/p:Configuration={configuration}");
result
.Should()
.Pass();
return result.StdOut;
}
private void OutputDiagnostics(MigratedBuildComparisonData comparisonData)
{
OutputDiagnostics(comparisonData.MSBuildBuildOutputs, comparisonData.ProjectJsonBuildOutputs);
}
private void OutputDiagnostics(HashSet<string> msbuildBuildOutputs, HashSet<string> projectJsonBuildOutputs)
{
Console.WriteLine("Project.json Outputs:");
Console.WriteLine(string.Join("\n", projectJsonBuildOutputs));
Console.WriteLine("");
Console.WriteLine("MSBuild Outputs:");
Console.WriteLine(string.Join("\n", msbuildBuildOutputs));
}
private class MigratedBuildComparisonData
{
public HashSet<string> ProjectJsonBuildOutputs { get; }
public HashSet<string> MSBuildBuildOutputs { get; }
public MigratedBuildComparisonData(HashSet<string> projectJsonBuildOutputs,
HashSet<string> msBuildBuildOutputs)
{
ProjectJsonBuildOutputs = projectJsonBuildOutputs;
MSBuildBuildOutputs = msBuildBuildOutputs;
}
}
}
}

View file

@ -0,0 +1 @@
https://github.com/Microsoft/msbuild/issues/927

View file

@ -0,0 +1 @@
https://github.com/Microsoft/msbuild/issues/927

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0.25123" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0.25123</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>1F2EF070-AC5F-4078-AFB0-65745AC691B9</ProjectGuid>
<RootNamespace>Microsoft.DotNet.ProjectJsonMigration.Tests</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View file

@ -0,0 +1,39 @@
{
"version": "1.0.0-*",
"buildOptions": {
"copyToOutput": ["MSBuild.exe", "MSBuild.exe.config"]
},
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
},
"Microsoft.DotNet.ProjectJsonMigration": {
"target": "project"
},
"xunit": "2.2.0-beta3-build3330",
"dotnet-test-xunit": "1.0.0-rc2-318883-21",
"FluentAssertions": "4.0.0",
"moq.netcore": "4.4.0-beta8",
"Microsoft.DotNet.Tools.Tests.Utilities": {
"target": "project"
},
"Microsoft.DotNet.Cli.Utils": {
"target": "project"
},
"dotnet": {
"target":"project"
}
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
"netstandardapp1.5",
"dotnet5.4",
"portable-net451+win8"
]
}
},
"testRunner": "xunit"
}