Add support for Migration defaults (#4242)
* Add support for sdk props/targets defaults to Migration. * fix the transform applicator * remove transform applicator dependency on insertion order for item merges * defaults constructor msbuild version change
This commit is contained in:
parent
6373cdde60
commit
ecbc45098d
28 changed files with 1251 additions and 302 deletions
|
@ -9,6 +9,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
|||
{
|
||||
private IMigrationRule[] Rules => new IMigrationRule[]
|
||||
{
|
||||
new AddDefaultsToProjectRule(),
|
||||
new MigrateRootOptionsRule(),
|
||||
new MigrateTFMRule(),
|
||||
new MigrateBuildOptionsRule(),
|
||||
|
@ -20,6 +21,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
|||
new MigratePackageDependenciesAndToolsRule(),
|
||||
new MigrateConfigurationsRule(),
|
||||
new MigrateScriptsRule(),
|
||||
new RemoveDefaultsFromProjectRule(),
|
||||
new CleanOutputProjectRule(),
|
||||
new SaveOutputProjectRule()
|
||||
};
|
||||
|
||||
|
|
|
@ -12,19 +12,22 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
|||
public string OutputDirectory { get; }
|
||||
public string SdkPackageVersion { get; }
|
||||
public ProjectRootElement MSBuildProjectTemplate { get; }
|
||||
public string SdkDefaultsFilePath { get; }
|
||||
|
||||
public MigrationSettings(
|
||||
string projectDirectory,
|
||||
string outputDirectory,
|
||||
string sdkPackageVersion,
|
||||
ProjectRootElement msBuildProjectTemplate,
|
||||
string projectXprojFilePath=null)
|
||||
string projectXprojFilePath=null,
|
||||
string sdkDefaultsFilePath=null)
|
||||
{
|
||||
ProjectDirectory = projectDirectory;
|
||||
OutputDirectory = outputDirectory;
|
||||
SdkPackageVersion = sdkPackageVersion;
|
||||
MSBuildProjectTemplate = msBuildProjectTemplate != null ? msBuildProjectTemplate.DeepClone() : null;
|
||||
ProjectXProjFilePath = projectXprojFilePath;
|
||||
SdkDefaultsFilePath = sdkDefaultsFilePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.Collections.Generic;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectJsonMigration.Models
|
||||
{
|
||||
public class DefaultProjectItemInfo
|
||||
{
|
||||
public string ItemType {get; set;}
|
||||
public string Include {get; set;}
|
||||
public string Exclude {get; set;}
|
||||
public string Remove {get; set;}
|
||||
public string Condition {get; set;}
|
||||
public string ParentCondition {get; set;}
|
||||
}
|
||||
}
|
|
@ -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.Collections.Generic;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectJsonMigration.Models
|
||||
{
|
||||
public class DefaultProjectPropertyInfo
|
||||
{
|
||||
public string Name {get; set;}
|
||||
public string Value {get; set;}
|
||||
public string Condition {get; set;}
|
||||
public string ParentCondition {get; set;}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectJsonMigration.Models
|
||||
{
|
||||
public class SerializableMigrationDefaultsInfo
|
||||
{
|
||||
public IEnumerable<DefaultProjectItemInfo> Items { get; set; }
|
||||
public IEnumerable<DefaultProjectPropertyInfo> Properties { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
// 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 Microsoft.Build.Construction;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.DotNet.ProjectJsonMigration.Models;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||
{
|
||||
public class AddDefaultsToProjectRule : IMigrationRule
|
||||
{
|
||||
internal const string c_DefaultsProjectElementContainerLabel = "MigrationDefaultsTempContainer";
|
||||
internal const string c_SdkDefaultsJsonFileName = "sdkdefaults.json";
|
||||
|
||||
private readonly ITransformApplicator _transformApplicator;
|
||||
|
||||
public AddDefaultsToProjectRule(ITransformApplicator transformApplicator = null)
|
||||
{
|
||||
_transformApplicator = transformApplicator ?? new TransformApplicator();
|
||||
}
|
||||
|
||||
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
|
||||
{
|
||||
SerializableMigrationDefaultsInfo defaults = ResolveDefaults(migrationSettings);
|
||||
|
||||
var project = migrationRuleInputs.OutputMSBuildProject;
|
||||
var defaultsPropertyGroups = new Dictionary<string, ProjectPropertyGroupElement>();
|
||||
var defaultsItemGroups = new Dictionary<string, ProjectItemGroupElement>();
|
||||
|
||||
AddDefaultPropertiesToProject(defaults, project, defaultsPropertyGroups);
|
||||
AddDefaultItemsToProject(defaults, project, defaultsItemGroups);
|
||||
}
|
||||
|
||||
private void AddDefaultItemsToProject(
|
||||
SerializableMigrationDefaultsInfo defaults,
|
||||
ProjectRootElement project,
|
||||
Dictionary<string, ProjectItemGroupElement> defaultsItemGroups)
|
||||
{
|
||||
foreach (var itemInfo in defaults.Items)
|
||||
{
|
||||
ProjectItemGroupElement itemGroup;
|
||||
var parentCondition = itemInfo.ParentCondition ?? string.Empty;
|
||||
|
||||
if (!defaultsItemGroups.TryGetValue(parentCondition, out itemGroup))
|
||||
{
|
||||
itemGroup = project.AddItemGroup();
|
||||
itemGroup.Label = c_DefaultsProjectElementContainerLabel;
|
||||
itemGroup.Condition = parentCondition;
|
||||
|
||||
defaultsItemGroups[parentCondition] = itemGroup;
|
||||
}
|
||||
|
||||
var item = itemGroup.AddItem(itemInfo.ItemType, itemInfo.Include);
|
||||
item.Exclude = itemInfo.Exclude;
|
||||
item.Remove = itemInfo.Remove;
|
||||
item.Condition = itemInfo.Condition;
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddDefaultPropertiesToProject(
|
||||
SerializableMigrationDefaultsInfo defaults,
|
||||
ProjectRootElement project,
|
||||
Dictionary<string, ProjectPropertyGroupElement> defaultsPropertyGroups)
|
||||
{
|
||||
foreach (var propertyInfo in defaults.Properties)
|
||||
{
|
||||
ProjectPropertyGroupElement propertyGroup;
|
||||
var parentCondition = propertyInfo.ParentCondition ?? string.Empty;
|
||||
|
||||
if (!defaultsPropertyGroups.TryGetValue(parentCondition, out propertyGroup))
|
||||
{
|
||||
propertyGroup = project.AddPropertyGroup();
|
||||
propertyGroup.Label = c_DefaultsProjectElementContainerLabel;
|
||||
propertyGroup.Condition = parentCondition;
|
||||
|
||||
defaultsPropertyGroups[parentCondition] = propertyGroup;
|
||||
}
|
||||
|
||||
var property = propertyGroup.AddProperty(propertyInfo.Name, propertyInfo.Value);
|
||||
property.Condition = propertyInfo.Condition;
|
||||
}
|
||||
}
|
||||
|
||||
private SerializableMigrationDefaultsInfo ResolveDefaults(MigrationSettings migrationSettings)
|
||||
{
|
||||
var sdkDefaultFile = TryResolveSdkDefaultsFileFromSettings(migrationSettings);
|
||||
if (sdkDefaultFile != null)
|
||||
{
|
||||
return DeserializeDefaults(File.ReadAllText(sdkDefaultFile));
|
||||
}
|
||||
|
||||
var thisAssembly = typeof(AddDefaultsToProjectRule).GetTypeInfo().Assembly;
|
||||
using (var resource = thisAssembly.GetManifestResourceStream("Microsoft.DotNet.ProjectJsonMigration." + c_SdkDefaultsJsonFileName))
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(resource))
|
||||
{
|
||||
return DeserializeDefaults(reader.ReadToEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string TryResolveSdkDefaultsFileFromSettings(MigrationSettings migrationSettings)
|
||||
{
|
||||
var candidate = migrationSettings.SdkDefaultsFilePath;
|
||||
if (candidate == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
return candidate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private SerializableMigrationDefaultsInfo DeserializeDefaults(string sdkDefaultJson)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<SerializableMigrationDefaultsInfo>(sdkDefaultJson);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// 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 Microsoft.Build.Construction;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.DotNet.ProjectJsonMigration.Models;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||
{
|
||||
public class CleanOutputProjectRule : IMigrationRule
|
||||
{
|
||||
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
|
||||
{
|
||||
var outputProject = migrationRuleInputs.OutputMSBuildProject;
|
||||
|
||||
CleanEmptyPropertiesAndItems(outputProject);
|
||||
CleanPropertiesThatDontChangeValue(outputProject);
|
||||
CleanEmptyPropertyAndItemGroups(outputProject);
|
||||
}
|
||||
|
||||
private void CleanEmptyPropertyAndItemGroups(ProjectRootElement msbuildProject)
|
||||
{
|
||||
foreach (var propertyGroup in msbuildProject.PropertyGroups)
|
||||
{
|
||||
propertyGroup.RemoveIfEmpty();
|
||||
}
|
||||
|
||||
foreach (var itemGroup in msbuildProject.ItemGroups)
|
||||
{
|
||||
itemGroup.RemoveIfEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanEmptyPropertiesAndItems(ProjectRootElement msbuildProject)
|
||||
{
|
||||
foreach (var property in msbuildProject.Properties)
|
||||
{
|
||||
if (string.IsNullOrEmpty(property.Value))
|
||||
{
|
||||
property.Parent.RemoveChild(property);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in msbuildProject.Items)
|
||||
{
|
||||
if (string.IsNullOrEmpty(item.Include))
|
||||
{
|
||||
item.Parent.RemoveChild(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanPropertiesThatDontChangeValue(ProjectRootElement msbuildProject)
|
||||
{
|
||||
foreach (var property in msbuildProject.Properties)
|
||||
{
|
||||
var value = property.Value.Trim();
|
||||
var variableExpectedValue = "$(" + property.Name + ")";
|
||||
|
||||
if (value == variableExpectedValue)
|
||||
{
|
||||
property.Parent.RemoveChild(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -105,11 +105,6 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
@"$(OutputPath)\$(TargetFramework)\$(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);
|
||||
|
||||
|
@ -175,7 +170,6 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
DelaySignTransform,
|
||||
PublicSignTransform,
|
||||
DebugTypeTransform,
|
||||
OutputNameTransform,
|
||||
XmlDocTransform,
|
||||
XmlDocTransformFilePath,
|
||||
PreserveCompilationContextTransform
|
||||
|
@ -242,14 +236,14 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
|
||||
if (!PropertiesAreEqual(nonConfigurationOutput, configurationOutput))
|
||||
{
|
||||
transformApplicator.Execute(configurationOutput, propertyGroup);
|
||||
transformApplicator.Execute(configurationOutput, propertyGroup, mergeExisting: true);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var includeContextTransformExecute in _includeContextTransformExecutes)
|
||||
{
|
||||
var nonConfigurationOutput = includeContextTransformExecute(compilerOptions, projectDirectory);
|
||||
var configurationOutput = includeContextTransformExecute(configurationCompilerOptions, projectDirectory).ToArray();
|
||||
var configurationOutput = includeContextTransformExecute(configurationCompilerOptions, projectDirectory);
|
||||
|
||||
transformApplicator.Execute(configurationOutput, itemGroup, mergeExisting: true);
|
||||
}
|
||||
|
@ -293,7 +287,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
{
|
||||
foreach (var transform in _propertyTransforms)
|
||||
{
|
||||
transformApplicator.Execute(transform.Transform(compilerOptions), propertyGroup);
|
||||
transformApplicator.Execute(transform.Transform(compilerOptions), propertyGroup, mergeExisting: true);
|
||||
}
|
||||
|
||||
foreach (var includeContextTransformExecute in _includeContextTransformExecutes)
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
var token = rawProject.GetValue(prop.Key);
|
||||
if (token != null)
|
||||
{
|
||||
_transformApplicator.Execute(prop.Value.Transform(token), propertyGroup);
|
||||
_transformApplicator.Execute(prop.Value.Transform(token), propertyGroup, mergeExisting: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
|
||||
foreach (var propertyTransfrom in _propertyTransforms)
|
||||
{
|
||||
_transformApplicator.Execute(propertyTransfrom.Transform(packOptions), propertyGroup);
|
||||
_transformApplicator.Execute(propertyTransfrom.Transform(packOptions), propertyGroup, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
var tfmDependencyMap = new Dictionary<string, IEnumerable<ProjectLibraryDependency>>();
|
||||
var targetFrameworks = project.GetTargetFrameworks();
|
||||
|
||||
var noFrameworkPackageReferenceItemGroup = migrationRuleInputs.OutputMSBuildProject.AddItemGroup();
|
||||
|
||||
// Inject Sdk dependency
|
||||
_transformApplicator.Execute(
|
||||
PackageDependencyInfoTransform.Transform(
|
||||
|
@ -45,7 +47,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
Name = ConstantPackageNames.CSdkPackageName,
|
||||
Version = migrationSettings.SdkPackageVersion,
|
||||
PrivateAssets = "All"
|
||||
}), migrationRuleInputs.CommonItemGroup);
|
||||
}), noFrameworkPackageReferenceItemGroup, mergeExisting: false);
|
||||
|
||||
// Migrate Direct Deps first
|
||||
MigrateDependencies(
|
||||
|
@ -53,7 +55,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
migrationRuleInputs.OutputMSBuildProject,
|
||||
null,
|
||||
project.Dependencies,
|
||||
migrationRuleInputs.ProjectXproj);
|
||||
migrationRuleInputs.ProjectXproj,
|
||||
itemGroup: noFrameworkPackageReferenceItemGroup);
|
||||
|
||||
MigrationTrace.Instance.WriteLine($"Migrating {targetFrameworks.Count()} target frameworks");
|
||||
foreach (var targetFramework in targetFrameworks)
|
||||
|
@ -80,7 +83,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
if (transform != null)
|
||||
{
|
||||
transform.Condition = targetFramework.FrameworkName.GetMSBuildCondition();
|
||||
_transformApplicator.Execute(transform, commonPropertyGroup);
|
||||
_transformApplicator.Execute(transform, commonPropertyGroup, mergeExisting: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -113,7 +116,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
|
||||
foreach (var tool in project.Tools)
|
||||
{
|
||||
_transformApplicator.Execute(ToolTransform.Transform(tool), itemGroup);
|
||||
_transformApplicator.Execute(ToolTransform.Transform(tool), itemGroup, mergeExisting: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,13 +125,15 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
ProjectRootElement output,
|
||||
NuGetFramework framework,
|
||||
IEnumerable<ProjectLibraryDependency> dependencies,
|
||||
ProjectRootElement xproj)
|
||||
ProjectRootElement xproj,
|
||||
ProjectItemGroupElement itemGroup=null)
|
||||
{
|
||||
var projectDependencies = new HashSet<string>(GetAllProjectReferenceNames(project, framework, xproj));
|
||||
var packageDependencies = dependencies.Where(d => !projectDependencies.Contains(d.Name)).ToList();
|
||||
|
||||
string condition = framework?.GetMSBuildCondition() ?? "";
|
||||
var itemGroup = output.ItemGroups.FirstOrDefault(i => i.Condition == condition)
|
||||
itemGroup = itemGroup
|
||||
?? output.ItemGroups.FirstOrDefault(i => i.Condition == condition)
|
||||
?? output.AddItemGroup();
|
||||
itemGroup.Condition = condition;
|
||||
|
||||
|
@ -163,7 +168,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
}
|
||||
}
|
||||
|
||||
_transformApplicator.Execute(transform.Transform(packageDependency), itemGroup);
|
||||
_transformApplicator.Execute(transform.Transform(packageDependency), itemGroup, mergeExisting: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
|
||||
foreach (var csprojTransformedReference in csprojTransformedReferences)
|
||||
{
|
||||
_transformApplicator.Execute(csprojTransformedReference, migrationRuleInputs.CommonItemGroup);
|
||||
_transformApplicator.Execute(csprojTransformedReference, migrationRuleInputs.CommonItemGroup, true);
|
||||
}
|
||||
|
||||
return csprojTransformedReferences.SelectMany(r => r.Includes());
|
||||
|
@ -108,10 +108,10 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
|
||||
foreach (var projectDependencyTransformResult in projectDependencyTransformResults)
|
||||
{
|
||||
_transformApplicator.Execute(projectDependencyTransformResult, itemGroup);
|
||||
_transformApplicator.Execute(projectDependencyTransformResult, itemGroup, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private AddItemTransform<ProjectDependency> ProjectDependencyTransform => new AddItemTransform<ProjectDependency>(
|
||||
"ProjectReference",
|
||||
dep =>
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
|
||||
foreach (var transformResult in transformResults)
|
||||
{
|
||||
_transformApplicator.Execute(transformResult, propertyGroup);
|
||||
_transformApplicator.Execute(transformResult, propertyGroup, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,14 +35,16 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
_transformApplicator.Execute(
|
||||
FrameworkTransform.Transform(
|
||||
migrationRuleInputs.ProjectContexts.Single().TargetFramework),
|
||||
propertyGroup);
|
||||
propertyGroup,
|
||||
mergeExisting: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_transformApplicator.Execute(
|
||||
FrameworksTransform.Transform(
|
||||
migrationRuleInputs.ProjectContexts.Select(p => p.TargetFramework)),
|
||||
propertyGroup);
|
||||
propertyGroup,
|
||||
mergeExisting: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// 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 Microsoft.Build.Construction;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.DotNet.ProjectJsonMigration.Models;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||
{
|
||||
public class RemoveDefaultsFromProjectRule : IMigrationRule
|
||||
{
|
||||
public void Apply(MigrationSettings migrationSettings, MigrationRuleInputs migrationRuleInputs)
|
||||
{
|
||||
foreach (var element in
|
||||
migrationRuleInputs.OutputMSBuildProject.Children
|
||||
.Where(c => c.Label == AddDefaultsToProjectRule.c_DefaultsProjectElementContainerLabel))
|
||||
{
|
||||
element.Parent.RemoveChild(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,22 +14,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
|||
|
||||
var outputProject = Path.Combine(migrationSettings.OutputDirectory, outputName + ".csproj");
|
||||
|
||||
CleanEmptyPropertyAndItemGroups(migrationRuleInputs.OutputMSBuildProject);
|
||||
|
||||
migrationRuleInputs.OutputMSBuildProject.Save(outputProject);
|
||||
}
|
||||
|
||||
private void CleanEmptyPropertyAndItemGroups(ProjectRootElement msbuildProject)
|
||||
{
|
||||
foreach (var propertyGroup in msbuildProject.PropertyGroups)
|
||||
{
|
||||
propertyGroup.RemoveIfEmpty();
|
||||
}
|
||||
|
||||
foreach (var itemGroup in msbuildProject.ItemGroups)
|
||||
{
|
||||
itemGroup.RemoveIfEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,333 @@
|
|||
// 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.Construction;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||
{
|
||||
public class ItemTransformApplicator : ITransformApplicator
|
||||
{
|
||||
private readonly ProjectRootElement _projectElementGenerator = ProjectRootElement.Create();
|
||||
|
||||
public void Execute<T, U>(
|
||||
T element,
|
||||
U destinationElement,
|
||||
bool mergeExisting) where T : ProjectElement where U : ProjectElementContainer
|
||||
{
|
||||
if (typeof(T) != typeof(ProjectItemElement))
|
||||
{
|
||||
throw new ArgumentException($"Expected element to be of type {nameof(ProjectItemElement)}, but got {typeof(T)}");
|
||||
}
|
||||
|
||||
if (typeof(U) != typeof(ProjectItemGroupElement))
|
||||
{
|
||||
throw new ArgumentException($"Expected destinationElement to be of type {nameof(ProjectItemGroupElement)}, but got {typeof(U)}");
|
||||
}
|
||||
|
||||
if (element == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (destinationElement == null)
|
||||
{
|
||||
throw new ArgumentException("expected destinationElement to not be null");
|
||||
}
|
||||
|
||||
var item = element as ProjectItemElement;
|
||||
var destinationItemGroup = destinationElement as ProjectItemGroupElement;
|
||||
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(ItemTransformApplicator)}: Item {{ ItemType: {item.ItemType}, Condition: {item.Condition}, Include: {item.Include}, Exclude: {item.Exclude} }}");
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(ItemTransformApplicator)}: ItemGroup {{ Condition: {destinationItemGroup.Condition} }}");
|
||||
|
||||
if (mergeExisting)
|
||||
{
|
||||
// Don't duplicate items or includes
|
||||
item = MergeWithExistingItemsWithSameCondition(item, destinationItemGroup);
|
||||
if (item == null)
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(ItemTransformApplicator)}: Item completely merged");
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle duplicate includes between different conditioned items
|
||||
item = MergeWithExistingItemsWithNoCondition(item, destinationItemGroup);
|
||||
if (item == null)
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(ItemTransformApplicator)}: Item completely merged");
|
||||
return;
|
||||
}
|
||||
|
||||
item = MergeWithExistingItemsWithACondition(item, destinationItemGroup);
|
||||
if (item == null)
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(ItemTransformApplicator)}: Item completely merged");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AddItemToItemGroup(item, destinationItemGroup);
|
||||
}
|
||||
|
||||
public void Execute<T, U>(
|
||||
IEnumerable<T> elements,
|
||||
U destinationElement,
|
||||
bool mergeExisting) where T : ProjectElement where U : ProjectElementContainer
|
||||
{
|
||||
foreach (var element in elements)
|
||||
{
|
||||
Execute(element, destinationElement, mergeExisting);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddItemToItemGroup(ProjectItemElement item, ProjectItemGroupElement itemGroup)
|
||||
{
|
||||
var outputItem = itemGroup.ContainingProject.CreateItemElement("___TEMP___");
|
||||
outputItem.CopyFrom(item);
|
||||
|
||||
itemGroup.AppendChild(outputItem);
|
||||
outputItem.AddMetadata(item.Metadata);
|
||||
}
|
||||
|
||||
private ProjectItemElement MergeWithExistingItemsWithACondition(ProjectItemElement item, ProjectItemGroupElement destinationItemGroup)
|
||||
{
|
||||
// This logic only applies to conditionless items
|
||||
if (item.ConditionChain().Any() || destinationItemGroup.ConditionChain().Any())
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
var existingItemsWithACondition =
|
||||
FindExistingItemsWithACondition(item, destinationItemGroup.ContainingProject, destinationItemGroup);
|
||||
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(ItemTransformApplicator)}: Merging Item with {existingItemsWithACondition.Count()} existing items with a different condition chain.");
|
||||
|
||||
foreach (var existingItem in existingItemsWithACondition)
|
||||
{
|
||||
// If this item is encompassing items in a condition, remove the encompassed includes from the existing item
|
||||
var encompassedIncludes = item.GetEncompassedIncludes(existingItem);
|
||||
if (encompassedIncludes.Any())
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(ItemTransformApplicator)}: encompassed includes {string.Join(", ", encompassedIncludes)}");
|
||||
existingItem.RemoveIncludes(encompassedIncludes);
|
||||
}
|
||||
|
||||
// continue if the existing item is now empty
|
||||
if (!existingItem.Includes().Any())
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(ItemTransformApplicator)}: Removing Item {{ ItemType: {existingItem.ItemType}, Condition: {existingItem.Condition}, Include: {existingItem.Include}, Exclude: {existingItem.Exclude} }}");
|
||||
existingItem.Parent.RemoveChild(existingItem);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we haven't continued, the existing item may have includes
|
||||
// that need to be removed before being redefined, to avoid duplicate includes
|
||||
// Create or merge with existing remove
|
||||
var remainingIntersectedIncludes = existingItem.IntersectIncludes(item);
|
||||
|
||||
if (remainingIntersectedIncludes.Any())
|
||||
{
|
||||
var existingRemoveItem = destinationItemGroup.Items
|
||||
.Where(i =>
|
||||
string.IsNullOrEmpty(i.Include)
|
||||
&& string.IsNullOrEmpty(i.Exclude)
|
||||
&& !string.IsNullOrEmpty(i.Remove))
|
||||
.FirstOrDefault();
|
||||
|
||||
if (existingRemoveItem != null)
|
||||
{
|
||||
var removes = new HashSet<string>(existingRemoveItem.Remove.Split(';'));
|
||||
foreach (var include in remainingIntersectedIncludes)
|
||||
{
|
||||
removes.Add(include);
|
||||
}
|
||||
existingRemoveItem.Remove = string.Join(";", removes);
|
||||
}
|
||||
else
|
||||
{
|
||||
var clearPreviousItem = _projectElementGenerator.CreateItemElement(item.ItemType);
|
||||
clearPreviousItem.Remove = string.Join(";", remainingIntersectedIncludes);
|
||||
|
||||
AddItemToItemGroup(clearPreviousItem, existingItem.Parent as ProjectItemGroupElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private ProjectItemElement MergeWithExistingItemsWithNoCondition(ProjectItemElement item, ProjectItemGroupElement destinationItemGroup)
|
||||
{
|
||||
// This logic only applies to items being placed into a condition
|
||||
if (!item.ConditionChain().Any() && !destinationItemGroup.ConditionChain().Any())
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
var existingItemsWithNoCondition =
|
||||
FindExistingItemsWithNoCondition(item, destinationItemGroup.ContainingProject, destinationItemGroup);
|
||||
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(ItemTransformApplicator)}: Merging Item with {existingItemsWithNoCondition.Count()} existing items with a different condition chain.");
|
||||
|
||||
// Handle the item being placed inside of a condition, when it is overlapping with a conditionless item
|
||||
// If it is not definining new metadata or excludes, the conditioned item can be merged with the
|
||||
// conditionless item
|
||||
foreach (var existingItem in existingItemsWithNoCondition)
|
||||
{
|
||||
var encompassedIncludes = existingItem.GetEncompassedIncludes(item);
|
||||
if (encompassedIncludes.Any())
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(ItemTransformApplicator)}: encompassed includes {string.Join(", ", encompassedIncludes)}");
|
||||
item.RemoveIncludes(encompassedIncludes);
|
||||
if (!item.Includes().Any())
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(ItemTransformApplicator)}: Ignoring Item {{ ItemType: {existingItem.ItemType}, Condition: {existingItem.Condition}, Include: {existingItem.Include}, Exclude: {existingItem.Exclude} }}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't returned, and there are existing items with a separate condition, we need to
|
||||
// overwrite with those items inside the destinationItemGroup by using a Remove
|
||||
if (existingItemsWithNoCondition.Any())
|
||||
{
|
||||
// Merge with the first remove if possible
|
||||
var existingRemoveItem = destinationItemGroup.Items
|
||||
.Where(i =>
|
||||
string.IsNullOrEmpty(i.Include)
|
||||
&& string.IsNullOrEmpty(i.Exclude)
|
||||
&& !string.IsNullOrEmpty(i.Remove))
|
||||
.FirstOrDefault();
|
||||
|
||||
if (existingRemoveItem != null)
|
||||
{
|
||||
existingRemoveItem.Remove += ";" + item.Include;
|
||||
}
|
||||
else
|
||||
{
|
||||
var clearPreviousItem = _projectElementGenerator.CreateItemElement(item.ItemType);
|
||||
clearPreviousItem.Remove = item.Include;
|
||||
|
||||
AddItemToItemGroup(clearPreviousItem, destinationItemGroup);
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private ProjectItemElement MergeWithExistingItemsWithSameCondition(ProjectItemElement item, ProjectItemGroupElement destinationItemGroup)
|
||||
{
|
||||
var existingItemsWithSameCondition =
|
||||
FindExistingItemsWithSameCondition(item, destinationItemGroup.ContainingProject, destinationItemGroup);
|
||||
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(TransformApplicator)}: Merging Item with {existingItemsWithSameCondition.Count()} existing items with the same condition chain.");
|
||||
|
||||
foreach (var existingItem in existingItemsWithSameCondition)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(TransformApplicator)}: Adding Merged Item {{ ItemType: {mergeResult.MergedItem.ItemType}, Condition: {mergeResult.MergedItem.Condition}, Include: {mergeResult.MergedItem.Include}, Exclude: {mergeResult.MergedItem.Exclude} }}");
|
||||
AddItemToItemGroup(mergeResult.MergedItem, destinationItemGroup);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <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();
|
||||
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);
|
||||
|
||||
item.RemoveIncludes(commonIncludes);
|
||||
existingItem.RemoveIncludes(commonIncludes);
|
||||
|
||||
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> FindExistingItemsWithSameCondition(
|
||||
ProjectItemElement item,
|
||||
ProjectRootElement project,
|
||||
ProjectElementContainer destinationContainer)
|
||||
{
|
||||
return project.Items
|
||||
.Where(i => i.Condition == item.Condition)
|
||||
.Where(i => i.Parent.ConditionChainsAreEquivalent(destinationContainer))
|
||||
.Where(i => i.ItemType == item.ItemType)
|
||||
.Where(i => i.IntersectIncludes(item).Any());
|
||||
}
|
||||
|
||||
private IEnumerable<ProjectItemElement> FindExistingItemsWithNoCondition(
|
||||
ProjectItemElement item,
|
||||
ProjectRootElement project,
|
||||
ProjectElementContainer destinationContainer)
|
||||
{
|
||||
return project.Items
|
||||
.Where(i => !i.ConditionChain().Any())
|
||||
.Where(i => i.ItemType == item.ItemType)
|
||||
.Where(i => i.IntersectIncludes(item).Any());
|
||||
}
|
||||
|
||||
private IEnumerable<ProjectItemElement> FindExistingItemsWithACondition(
|
||||
ProjectItemElement item,
|
||||
ProjectRootElement project,
|
||||
ProjectElementContainer destinationContainer)
|
||||
{
|
||||
return project.Items
|
||||
.Where(i => i.ConditionChain().Any())
|
||||
.Where(i => i.ItemType == item.ItemType)
|
||||
.Where(i => i.IntersectIncludes(item).Any());
|
||||
}
|
||||
|
||||
private class MergeResult
|
||||
{
|
||||
public ProjectItemElement InputItem { get; set; }
|
||||
public ProjectItemElement ExistingItem { get; set; }
|
||||
public ProjectItemElement MergedItem { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
// 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.Construction;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||
{
|
||||
public class PropertyTransformApplicator : ITransformApplicator
|
||||
{
|
||||
private readonly ProjectRootElement _projectElementGenerator = ProjectRootElement.Create();
|
||||
|
||||
public void Execute<T, U>(
|
||||
T element,
|
||||
U destinationElement,
|
||||
bool mergeExisting) where T : ProjectElement where U : ProjectElementContainer
|
||||
{
|
||||
if (typeof(T) != typeof(ProjectPropertyElement))
|
||||
{
|
||||
throw new ArgumentException($"Expected element to be of type {nameof(ProjectPropertyElement)}, but got {nameof(T)}");
|
||||
}
|
||||
|
||||
if (typeof(U) != typeof(ProjectPropertyGroupElement))
|
||||
{
|
||||
throw new ArgumentException($"Expected element to be of type {nameof(ProjectPropertyGroupElement)}, but got {nameof(U)}");
|
||||
}
|
||||
|
||||
if (element == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var property = element as ProjectPropertyElement;
|
||||
var destinationPropertyGroup = destinationElement as ProjectPropertyGroupElement;
|
||||
|
||||
if (mergeExisting)
|
||||
{
|
||||
var mergedProperty = MergePropertyWithProject(property, destinationPropertyGroup);
|
||||
if (mergedProperty != null && !string.IsNullOrEmpty(mergedProperty.Value))
|
||||
{
|
||||
TracePropertyInfo("Merging property, output merged property", mergedProperty);
|
||||
AddPropertyToPropertyGroup(mergedProperty, destinationPropertyGroup);
|
||||
}
|
||||
else
|
||||
{
|
||||
TracePropertyInfo("Ignoring fully merged property", property);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddPropertyToPropertyGroup(property, destinationPropertyGroup);
|
||||
}
|
||||
}
|
||||
|
||||
private ProjectPropertyElement MergePropertyWithProject(
|
||||
ProjectPropertyElement property,
|
||||
ProjectPropertyGroupElement destinationPropertyGroup)
|
||||
{
|
||||
var propertiesToMergeWith = FindPropertiesWithSameNameAndSameOrNoCondition(
|
||||
property,
|
||||
destinationPropertyGroup.ContainingProject);
|
||||
|
||||
foreach (var propertyToMergeWith in propertiesToMergeWith)
|
||||
{
|
||||
property = MergeProperties(propertyToMergeWith, property);
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
private IEnumerable<ProjectPropertyElement> FindPropertiesWithSameNameAndSameOrNoCondition(
|
||||
ProjectPropertyElement property,
|
||||
ProjectRootElement project)
|
||||
{
|
||||
return project.Properties
|
||||
.Where(otherProperty =>
|
||||
property.Name == otherProperty.Name
|
||||
&& (property.Condition == otherProperty.Condition
|
||||
|| (property.Condition == otherProperty.Parent.Condition && string.IsNullOrEmpty(otherProperty.Condition))
|
||||
|| !otherProperty.ConditionChain().Any()));
|
||||
}
|
||||
|
||||
private ProjectPropertyElement MergeProperties(ProjectPropertyElement baseProperty, ProjectPropertyElement addedProperty)
|
||||
{
|
||||
var mergedProperty = _projectElementGenerator.AddProperty("___TEMP___", "___TEMP___");
|
||||
mergedProperty.CopyFrom(addedProperty);
|
||||
|
||||
var basePropertyValues = baseProperty.Value.Split(';');
|
||||
var addedPropertyValues = addedProperty.Value.Split(';');
|
||||
|
||||
var intersectedValues = basePropertyValues.Intersect(addedPropertyValues, StringComparer.Ordinal);
|
||||
intersectedValues = RemoveValuesWithVariable(intersectedValues);
|
||||
|
||||
mergedProperty.Value = string.Join(";", addedPropertyValues.Except(intersectedValues));
|
||||
|
||||
return mergedProperty;
|
||||
}
|
||||
|
||||
private IEnumerable<string> RemoveValuesWithVariable(IEnumerable<string> intersectedValues)
|
||||
{
|
||||
return intersectedValues.Where(v => ! Regex.IsMatch(v, @"\$\(.*?\)"));
|
||||
}
|
||||
|
||||
private void AddPropertyToPropertyGroup(ProjectPropertyElement property, ProjectPropertyGroupElement destinationPropertyGroup)
|
||||
{
|
||||
var outputProperty = destinationPropertyGroup.ContainingProject.CreatePropertyElement("___TEMP___");
|
||||
outputProperty.CopyFrom(property);
|
||||
|
||||
destinationPropertyGroup.AppendChild(outputProperty);
|
||||
}
|
||||
|
||||
public void Execute<T, U>(
|
||||
IEnumerable<T> elements,
|
||||
U destinationElement,
|
||||
bool mergeExisting) where T : ProjectElement where U : ProjectElementContainer
|
||||
{
|
||||
foreach (var element in elements)
|
||||
{
|
||||
Execute(element, destinationElement, mergeExisting);
|
||||
}
|
||||
}
|
||||
|
||||
private void TracePropertyInfo(string message, ProjectPropertyElement mergedProperty)
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(PropertyTransformApplicator)}: {message}, {{ Name={mergedProperty.Name}, Value={mergedProperty.Value} }}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,10 @@
|
|||
"version": "1.0.0-preview3-*",
|
||||
"buildOptions": {
|
||||
"warningsAsErrors": true,
|
||||
"keyFile": "../../tools/Key.snk"
|
||||
"keyFile": "../../tools/Key.snk",
|
||||
"embed" :{
|
||||
"include" : "sdkdefaults.json"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.DotNet.Compiler.Common": {
|
||||
|
@ -17,7 +20,6 @@
|
|||
"Microsoft.Build": "15.1.319-preview5"
|
||||
},
|
||||
"frameworks": {
|
||||
"netcoreapp1.0": {
|
||||
}
|
||||
"netcoreapp1.0": { }
|
||||
}
|
||||
}
|
||||
|
|
178
src/Microsoft.DotNet.ProjectJsonMigration/sdkdefaults.json
Executable file
178
src/Microsoft.DotNet.ProjectJsonMigration/sdkdefaults.json
Executable file
|
@ -0,0 +1,178 @@
|
|||
{
|
||||
"Items": [
|
||||
{
|
||||
"ItemType": "Compile",
|
||||
"Include": "**\\*.cs",
|
||||
"Exclude": "bin\\**;obj\\**",
|
||||
"Remove": null,
|
||||
"Condition": null,
|
||||
"ParentCondition": null
|
||||
},
|
||||
{
|
||||
"ItemType": "EmbeddedResource",
|
||||
"Include": "**\\*.resx",
|
||||
"Exclude": "bin\\**;obj\\**",
|
||||
"Remove": null,
|
||||
"Condition": null,
|
||||
"ParentCondition": null
|
||||
}
|
||||
],
|
||||
"Properties": [
|
||||
{
|
||||
"Name": "OutputType",
|
||||
"Value": "Library",
|
||||
"Condition": "",
|
||||
"ParentCondition": ""
|
||||
},
|
||||
{
|
||||
"Name": "Configuration",
|
||||
"Value": "Debug",
|
||||
"Condition": null,
|
||||
"ParentCondition": null
|
||||
},
|
||||
{
|
||||
"Name": "Platform",
|
||||
"Value": "AnyCPU",
|
||||
"Condition": "",
|
||||
"ParentCondition": ""
|
||||
},
|
||||
{
|
||||
"Name": "FileAlignment",
|
||||
"Value": "512",
|
||||
"Condition": "",
|
||||
"ParentCondition": ""
|
||||
},
|
||||
{
|
||||
"Name": "PlatformTarget",
|
||||
"Value": "AnyCPU",
|
||||
"Condition": "",
|
||||
"ParentCondition": ""
|
||||
},
|
||||
{
|
||||
"Name": "ErrorReport",
|
||||
"Value": "prompt",
|
||||
"Condition": "",
|
||||
"ParentCondition": ""
|
||||
},
|
||||
{
|
||||
"Name": "AssemblyName",
|
||||
"Value": "$(MSBuildProjectName)",
|
||||
"Condition": "",
|
||||
"ParentCondition": ""
|
||||
},
|
||||
{
|
||||
"Name": "RootNamespace",
|
||||
"Value": "$(MSBuildProjectName)",
|
||||
"Condition": "",
|
||||
"ParentCondition": ""
|
||||
},
|
||||
{
|
||||
"Name": "Deterministic",
|
||||
"Value": "true",
|
||||
"Condition": "",
|
||||
"ParentCondition": ""
|
||||
},
|
||||
{
|
||||
"Name": "WarningLevel",
|
||||
"Value": "4",
|
||||
"Condition": "",
|
||||
"ParentCondition": ""
|
||||
},
|
||||
{
|
||||
"Name": "NoWarn",
|
||||
"Value": "1701;1702;1705",
|
||||
"Condition": "",
|
||||
"ParentCondition": ""
|
||||
},
|
||||
{
|
||||
"Name": "PackageRequireLicenseAcceptance",
|
||||
"Value": "false",
|
||||
"Condition": "",
|
||||
"ParentCondition": ""
|
||||
},
|
||||
{
|
||||
"Name": "DebugSymbols",
|
||||
"Value": "true",
|
||||
"Condition": "",
|
||||
"ParentCondition": " '$(Configuration)' == 'Debug' "
|
||||
},
|
||||
{
|
||||
"Name": "Optimize",
|
||||
"Value": "false",
|
||||
"Condition": "",
|
||||
"ParentCondition": " '$(Configuration)' == 'Debug' "
|
||||
},
|
||||
{
|
||||
"Name": "OutputPath",
|
||||
"Value": "bin\\Debug\\",
|
||||
"Condition": "",
|
||||
"ParentCondition": " '$(Configuration)' == 'Debug' "
|
||||
},
|
||||
{
|
||||
"Name": "Optimize",
|
||||
"Value": "true",
|
||||
"Condition": "",
|
||||
"ParentCondition": " '$(Configuration)' == 'Release' "
|
||||
},
|
||||
{
|
||||
"Name": "OutputPath",
|
||||
"Value": "bin\\Release\\",
|
||||
"Condition": "",
|
||||
"ParentCondition": " '$(Configuration)' == 'Release' "
|
||||
},
|
||||
{
|
||||
"Name": "DefineConstants",
|
||||
"Value": "DEBUG;TRACE",
|
||||
"Condition": "",
|
||||
"ParentCondition": " '$(Configuration)' == 'Debug' "
|
||||
},
|
||||
{
|
||||
"Name": "DefineConstants",
|
||||
"Value": "TRACE",
|
||||
"Condition": "",
|
||||
"ParentCondition": " '$(Configuration)' == 'Release' "
|
||||
},
|
||||
{
|
||||
"Name": "VersionPrefix",
|
||||
"Value": "1.0.0",
|
||||
"Condition": null,
|
||||
"ParentCondition": null
|
||||
},
|
||||
{
|
||||
"Name": "AssemblyTitle",
|
||||
"Value": "$(AssemblyName)",
|
||||
"Condition": null,
|
||||
"ParentCondition": null
|
||||
},
|
||||
{
|
||||
"Name": "Product",
|
||||
"Value": "$(AssemblyName)",
|
||||
"Condition": null,
|
||||
"ParentCondition": null
|
||||
},
|
||||
{
|
||||
"Name": "NeutralLanguage",
|
||||
"Value": "en",
|
||||
"Condition": null,
|
||||
"ParentCondition": null
|
||||
},
|
||||
{
|
||||
"Name": "AutoUnifyAssemblyReferences",
|
||||
"Value": "true",
|
||||
"Condition": null,
|
||||
"ParentCondition": null
|
||||
},
|
||||
{
|
||||
"Name": "DesignTimeAutoUnify",
|
||||
"Value": "true",
|
||||
"Condition": null,
|
||||
"ParentCondition": null
|
||||
},
|
||||
{
|
||||
"Name": "TargetExt",
|
||||
"Value": ".dll",
|
||||
"Condition": null,
|
||||
"ParentCondition": null
|
||||
}
|
||||
]
|
||||
}
|
|
@ -10,20 +10,12 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
|||
{
|
||||
void Execute<T, U>(
|
||||
T element,
|
||||
U destinationElement) where T : ProjectElement where U : ProjectElementContainer;
|
||||
U destinationElement,
|
||||
bool mergeExisting) 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);
|
||||
U destinationElement,
|
||||
bool mergeExisting) where T : ProjectElement where U : ProjectElementContainer;
|
||||
}
|
||||
}
|
|
@ -10,248 +10,44 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
|||
{
|
||||
public class TransformApplicator : ITransformApplicator
|
||||
{
|
||||
private readonly ProjectRootElement _projectElementGenerator = ProjectRootElement.Create();
|
||||
private readonly ITransformApplicator _propertyTransformApplicator;
|
||||
|
||||
private readonly ITransformApplicator _itemTransformApplicator;
|
||||
|
||||
public TransformApplicator(ITransformApplicator propertyTransformApplicator=null, ITransformApplicator itemTransformApplicator=null)
|
||||
{
|
||||
_propertyTransformApplicator = propertyTransformApplicator ?? new PropertyTransformApplicator();
|
||||
_itemTransformApplicator = propertyTransformApplicator ?? new ItemTransformApplicator();
|
||||
}
|
||||
|
||||
public void Execute<T, U>(
|
||||
T element,
|
||||
U destinationElement) where T : ProjectElement where U : ProjectElementContainer
|
||||
U destinationElement,
|
||||
bool mergeExisting) where T : ProjectElement where U : ProjectElementContainer
|
||||
{
|
||||
if (element != null)
|
||||
if (typeof(T) == typeof(ProjectItemElement))
|
||||
{
|
||||
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))
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine(
|
||||
$"{nameof(TransformApplicator)}: Adding Property to project {(element as ProjectPropertyElement).Name}");
|
||||
var property = destinationElement.ContainingProject.CreatePropertyElement("___TEMP___");
|
||||
property.CopyFrom(element);
|
||||
|
||||
destinationElement.AppendChild(property);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Unable to add unknown project element to project");
|
||||
}
|
||||
_itemTransformApplicator.Execute(element, destinationElement, mergeExisting);
|
||||
}
|
||||
else if (typeof(T) == typeof(ProjectPropertyElement))
|
||||
{
|
||||
_propertyTransformApplicator.Execute(element, destinationElement, mergeExisting);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Unexpected type {nameof(T)}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute<T, U>(
|
||||
IEnumerable<T> elements,
|
||||
U destinationElement) where T : ProjectElement where U : ProjectElementContainer
|
||||
U destinationElement,
|
||||
bool mergeExisting) where T : ProjectElement where U : ProjectElementContainer
|
||||
{
|
||||
foreach (var element in elements)
|
||||
{
|
||||
Execute(element, destinationElement);
|
||||
Execute(element, destinationElement, mergeExisting);
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(
|
||||
ProjectItemElement item,
|
||||
ProjectItemGroupElement destinationItemGroup,
|
||||
bool mergeExisting)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(TransformApplicator)}: Item {{ ItemType: {item.ItemType}, Condition: {item.Condition}, Include: {item.Include}, Exclude: {item.Exclude} }}");
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(TransformApplicator)}: ItemGroup {{ Condition: {destinationItemGroup.Condition} }}");
|
||||
|
||||
if (mergeExisting)
|
||||
{
|
||||
item = MergeWithExistingItemsWithSameCondition(item, destinationItemGroup);
|
||||
|
||||
// Item will be null when it's entire set of includes has been merged.
|
||||
if (item == null)
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(TransformApplicator)}: Item completely merged");
|
||||
return;
|
||||
}
|
||||
|
||||
item = MergeWithExistingItemsWithDifferentCondition(item, destinationItemGroup);
|
||||
|
||||
// Item will be null when it is equivalent to a conditionless item
|
||||
if (item == null)
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(TransformApplicator)}: Item c");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Execute(item, destinationItemGroup);
|
||||
}
|
||||
|
||||
private ProjectItemElement MergeWithExistingItemsWithDifferentCondition(ProjectItemElement item, ProjectItemGroupElement destinationItemGroup)
|
||||
{
|
||||
var existingItemsWithDifferentCondition =
|
||||
FindExistingItemsWithDifferentCondition(item, destinationItemGroup.ContainingProject, destinationItemGroup);
|
||||
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(TransformApplicator)}: Merging Item with {existingItemsWithDifferentCondition.Count()} existing items with a different condition chain.");
|
||||
|
||||
foreach (var existingItem in existingItemsWithDifferentCondition)
|
||||
{
|
||||
var encompassedIncludes = existingItem.GetEncompassedIncludes(item);
|
||||
if (encompassedIncludes.Any())
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(TransformApplicator)}: encompassed includes {string.Join(", ", encompassedIncludes)}");
|
||||
item.RemoveIncludes(encompassedIncludes);
|
||||
if (!item.Includes().Any())
|
||||
{
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(TransformApplicator)}: Ignoring Item {{ ItemType: {existingItem.ItemType}, Condition: {existingItem.Condition}, Include: {existingItem.Include}, Exclude: {existingItem.Exclude} }}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't returned, and there are existing items with a separate condition, we need to
|
||||
// overwrite with those items inside the destinationItemGroup by using a Remove
|
||||
// Unless this is a conditionless item, in which case this the conditioned items should be doing the
|
||||
// overwriting.
|
||||
if (existingItemsWithDifferentCondition.Any() &&
|
||||
(item.ConditionChain().Count() > 0 || destinationItemGroup.ConditionChain().Count() > 0))
|
||||
{
|
||||
// Merge with the first remove if possible
|
||||
var existingRemoveItem = destinationItemGroup.Items
|
||||
.Where(i =>
|
||||
string.IsNullOrEmpty(i.Include)
|
||||
&& string.IsNullOrEmpty(i.Exclude)
|
||||
&& !string.IsNullOrEmpty(i.Remove))
|
||||
.FirstOrDefault();
|
||||
|
||||
if (existingRemoveItem != null)
|
||||
{
|
||||
existingRemoveItem.Remove += ";" + item.Include;
|
||||
}
|
||||
else
|
||||
{
|
||||
var clearPreviousItem = _projectElementGenerator.CreateItemElement(item.ItemType);
|
||||
clearPreviousItem.Remove = item.Include;
|
||||
|
||||
Execute(clearPreviousItem, destinationItemGroup);
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private ProjectItemElement MergeWithExistingItemsWithSameCondition(ProjectItemElement item, ProjectItemGroupElement destinationItemGroup)
|
||||
{
|
||||
var existingItemsWithSameCondition =
|
||||
FindExistingItemsWithSameCondition(item, destinationItemGroup.ContainingProject, destinationItemGroup);
|
||||
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(TransformApplicator)}: Merging Item with {existingItemsWithSameCondition.Count()} existing items with the same condition chain.");
|
||||
|
||||
foreach (var existingItem in existingItemsWithSameCondition)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
MigrationTrace.Instance.WriteLine($"{nameof(TransformApplicator)}: Adding Merged Item {{ ItemType: {mergeResult.MergedItem.ItemType}, Condition: {mergeResult.MergedItem.Condition}, Include: {mergeResult.MergedItem.Include}, Exclude: {mergeResult.MergedItem.Exclude} }}");
|
||||
Execute(mergeResult.MergedItem, destinationItemGroup);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
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();
|
||||
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);
|
||||
|
||||
item.RemoveIncludes(commonIncludes);
|
||||
existingItem.RemoveIncludes(commonIncludes);
|
||||
|
||||
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> FindExistingItemsWithSameCondition(
|
||||
ProjectItemElement item,
|
||||
ProjectRootElement project,
|
||||
ProjectElementContainer destinationContainer)
|
||||
{
|
||||
return project.Items
|
||||
.Where(i => i.Condition == item.Condition)
|
||||
.Where(i => i.Parent.ConditionChainsAreEquivalent(destinationContainer))
|
||||
.Where(i => i.ItemType == item.ItemType)
|
||||
.Where(i => i.IntersectIncludes(item).Any());
|
||||
}
|
||||
|
||||
private IEnumerable<ProjectItemElement> FindExistingItemsWithDifferentCondition(
|
||||
ProjectItemElement item,
|
||||
ProjectRootElement project,
|
||||
ProjectElementContainer destinationContainer)
|
||||
{
|
||||
return project.Items
|
||||
.Where(i => !i.ConditionChainsAreEquivalent(item) || !i.Parent.ConditionChainsAreEquivalent(destinationContainer))
|
||||
.Where(i => i.ItemType == item.ItemType)
|
||||
.Where(i => i.IntersectIncludes(item).Any());
|
||||
}
|
||||
|
||||
private class MergeResult
|
||||
{
|
||||
public ProjectItemElement InputItem { get; set; }
|
||||
public ProjectItemElement ExistingItem { get; set; }
|
||||
public ProjectItemElement MergedItem { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,16 +49,13 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Migrating_empty_buildOptions_populates_only_AssemblyName_Compile_and_EmbeddedResource()
|
||||
public void Migrating_empty_buildOptions_populates_only_Compile_and_EmbeddedResource()
|
||||
{
|
||||
var mockProj = RunBuildOptionsRuleOnPj(@"
|
||||
{
|
||||
""buildOptions"": { }
|
||||
}");
|
||||
|
||||
mockProj.Properties.Count().Should().Be(1);
|
||||
mockProj.Properties.Any(p => !p.Name.Equals("AssemblyName", 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\**");
|
||||
|
@ -346,20 +343,6 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
|
|||
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()
|
||||
{
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
|
|||
item2.AddMetadata(metadata[1].MetadataName, metadata[1].GetMetadataValue(null));
|
||||
|
||||
var transformApplicator = new TransformApplicator();
|
||||
transformApplicator.Execute(new ProjectItemElement[] {item1, item2}, itemGroup, mergeExisting:true);
|
||||
transformApplicator.Execute(new ProjectItemElement[] {item1, item2}.Select(i => i), itemGroup, mergeExisting:true);
|
||||
|
||||
itemGroup.Items.Count.Should().Be(1);
|
||||
|
||||
|
@ -64,5 +64,26 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
|
|||
|
||||
foundMetadata.All(kv => kv.Value).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_merges_Properties_value_split_by_semicolon_except_variables_when_mergeExisting_is_true()
|
||||
{
|
||||
var mockProj = ProjectRootElement.Create();
|
||||
var existingProperty = mockProj.AddProperty("property1","value1;$(Variable1);$(Variable2);value2");
|
||||
|
||||
var propertyGeneratorProject = ProjectRootElement.Create();
|
||||
var propertyToAdd = propertyGeneratorProject.AddProperty("property1", "$(Variable2);value1;value3;$(Variable3)");
|
||||
|
||||
var transformApplicator = new TransformApplicator();
|
||||
|
||||
transformApplicator.Execute(propertyToAdd, mockProj.AddPropertyGroup(), mergeExisting: true);
|
||||
|
||||
var outputProperties = mockProj.Properties.Where(p => p.Name == "property1");
|
||||
outputProperties.Should().HaveCount(2);
|
||||
|
||||
var mergedPropertyToAdd = outputProperties.Where(p => p.Value.Contains("value3")).First();
|
||||
|
||||
mergedPropertyToAdd.Value.Should().Be("$(Variable2);value3;$(Variable3)");
|
||||
}
|
||||
}
|
||||
}
|
193
tools/MigrationDefaultsConstructor/Program.cs
Executable file
193
tools/MigrationDefaultsConstructor/Program.cs
Executable file
|
@ -0,0 +1,193 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.DotNet.Cli;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.Build.Evaluation;
|
||||
using Microsoft.Build.Execution;
|
||||
using Newtonsoft.Json;
|
||||
using System.Threading;
|
||||
using Microsoft.Build.Construction;
|
||||
using Microsoft.DotNet.ProjectJsonMigration.Models;
|
||||
|
||||
namespace MigrationDefaultsConstructor
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
private const string c_temporaryDotnetNewMSBuildProjectName = "p";
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var sdkRootPath=args[0];
|
||||
|
||||
var beforeCommonSdkTargetsFilePath = Path.Combine(sdkRootPath, "src", "Tasks", "Microsoft.NET.Build.Tasks", "build", "Microsoft.NET.Sdk.BeforeCommon.targets");
|
||||
var commonSdkTargetsFilePath = Path.Combine(sdkRootPath, "src", "Tasks", "Microsoft.NET.Build.Tasks", "build", "Microsoft.NET.Sdk.Common.targets");
|
||||
var sdkTargetsFilePath = Path.Combine(sdkRootPath, "src", "Tasks", "Microsoft.NET.Build.Tasks", "build", "Microsoft.NET.Sdk.targets");
|
||||
var sdkPropsFilePath = Path.Combine(sdkRootPath, "src", "Tasks", "Microsoft.NET.Build.Tasks", "build", "Microsoft.NET.Sdk.props");
|
||||
var csharpTargetsFilePath = Path.Combine(sdkRootPath, "src", "Tasks", "Microsoft.NET.Build.Tasks", "build", "Microsoft.NET.Sdk.CSharp.targets");
|
||||
var csharpPropsFilePath = Path.Combine(sdkRootPath, "src", "Tasks", "Microsoft.NET.Build.Tasks", "build", "Microsoft.NET.Sdk.CSharp.props");
|
||||
|
||||
var beforeCommonSdkTargetsFile = ProjectRootElement.Open(beforeCommonSdkTargetsFilePath);
|
||||
var commonSdkTargetsFile = ProjectRootElement.Open(commonSdkTargetsFilePath);
|
||||
var sdkTargetsFile = ProjectRootElement.Open(sdkTargetsFilePath);
|
||||
var sdkPropsFile = ProjectRootElement.Open(sdkPropsFilePath);
|
||||
var csharpPropsFile = ProjectRootElement.Open(csharpPropsFilePath);
|
||||
var csharpTargetsFile = ProjectRootElement.Open(csharpTargetsFilePath);
|
||||
|
||||
var allProperties = new List<DefaultProjectPropertyInfo>();
|
||||
var allItems = new List<DefaultProjectItemInfo>();
|
||||
|
||||
AddPropertyDefault(allProperties, sdkPropsFile, "OutputType");
|
||||
AddPropertyDefault(allProperties, sdkPropsFile, "Configuration", ignoreConditions: true);
|
||||
AddPropertyDefault(allProperties, sdkPropsFile, "Platform");
|
||||
AddPropertyDefault(allProperties, sdkPropsFile, "FileAlignment");
|
||||
AddPropertyDefault(allProperties, sdkPropsFile, "PlatformTarget");
|
||||
AddPropertyDefault(allProperties, sdkPropsFile, "ErrorReport");
|
||||
AddPropertyDefault(allProperties, sdkPropsFile, "AssemblyName");
|
||||
AddPropertyDefault(allProperties, sdkPropsFile, "RootNamespace");
|
||||
AddPropertyDefault(allProperties, sdkPropsFile, "Deterministic");
|
||||
|
||||
AddPropertyDefault(allProperties, csharpPropsFile, "WarningLevel");
|
||||
AddPropertyDefault(allProperties, csharpPropsFile, "NoWarn");
|
||||
|
||||
AddHardcodedPropertyDefault(allProperties, "PackageRequireLicenseAcceptance", "false");
|
||||
|
||||
AddConfigurationPropertyDefaults(allProperties, sdkPropsFile, "Debug");
|
||||
AddConfigurationPropertyDefaults(allProperties, sdkPropsFile, "Release");
|
||||
|
||||
AddConfigurationPropertyDefaults(allProperties, csharpPropsFile, "Debug");
|
||||
AddConfigurationPropertyDefaults(allProperties, csharpPropsFile, "Release");
|
||||
|
||||
AddPropertyDefault(allProperties, commonSdkTargetsFile, "VersionPrefix", ignoreConditions: true);
|
||||
AddPropertyDefault(allProperties, commonSdkTargetsFile, "AssemblyTitle", ignoreConditions: true);
|
||||
AddPropertyDefault(allProperties, commonSdkTargetsFile, "Product", ignoreConditions: true);
|
||||
AddPropertyDefault(allProperties, commonSdkTargetsFile, "NeutralLanguage", ignoreConditions: true);
|
||||
|
||||
AddPropertyDefault(allProperties, beforeCommonSdkTargetsFile, "AutoUnifyAssemblyReferences", ignoreConditions: true);
|
||||
AddPropertyDefault(allProperties, beforeCommonSdkTargetsFile, "DesignTimeAutoUnify", ignoreConditions: true);
|
||||
AddPropertyDefault(allProperties, beforeCommonSdkTargetsFile, "TargetExt", ignoreConditions: true);
|
||||
|
||||
AddCompileAndEmbeddedResourceDefaults(allItems, sdkTargetsFile);
|
||||
|
||||
var wrapper = new SerializableMigrationDefaultsInfo()
|
||||
{
|
||||
Items = allItems,
|
||||
Properties = allProperties
|
||||
};
|
||||
|
||||
var output = Path.Combine(Directory.GetCurrentDirectory(), "sdkdefaults.json");
|
||||
string json = JsonConvert.SerializeObject(wrapper, Formatting.Indented);
|
||||
File.WriteAllText(output, json);
|
||||
}
|
||||
|
||||
private static void AddHardcodedPropertyDefault(List<DefaultProjectPropertyInfo> allProperties,
|
||||
string name,
|
||||
string value,
|
||||
string condition="",
|
||||
string parentCondition="")
|
||||
{
|
||||
var propertyInfo = new DefaultProjectPropertyInfo
|
||||
{
|
||||
Name = name,
|
||||
Value = value,
|
||||
Condition = condition,
|
||||
ParentCondition = parentCondition
|
||||
};
|
||||
|
||||
allProperties.Add(propertyInfo);
|
||||
}
|
||||
|
||||
private static void AddCompileAndEmbeddedResourceDefaults(List<DefaultProjectItemInfo> allItems, ProjectRootElement msbuild)
|
||||
{
|
||||
var exclude = msbuild.Properties.Where(p=>p.Name == "DefaultExcludes").First().Value;
|
||||
|
||||
var compileInclude = msbuild.Items.Where(i => i.ItemType == "Compile").First().Include;
|
||||
if (string.IsNullOrEmpty(compileInclude))
|
||||
{
|
||||
compileInclude = "**\\*.cs";
|
||||
}
|
||||
|
||||
var embedInclude = msbuild.Items.Where(i => i.ItemType == "EmbeddedResource").First().Include;
|
||||
if (string.IsNullOrEmpty(embedInclude))
|
||||
{
|
||||
embedInclude = "**\\*.resx";
|
||||
}
|
||||
|
||||
allItems.Add(new DefaultProjectItemInfo
|
||||
{
|
||||
ItemType = "Compile",
|
||||
Include=compileInclude,
|
||||
Exclude=exclude
|
||||
});
|
||||
|
||||
allItems.Add(new DefaultProjectItemInfo
|
||||
{
|
||||
ItemType = "EmbeddedResource",
|
||||
Include=embedInclude,
|
||||
Exclude=exclude
|
||||
});
|
||||
}
|
||||
|
||||
private static void AddConfigurationPropertyDefaults(List<DefaultProjectPropertyInfo> allProperties, ProjectRootElement msbuild, string config)
|
||||
{
|
||||
var configPropertyGroup = msbuild.PropertyGroups.Where(p => p.Condition.Contains("$(Configuration)") && p.Condition.Contains(config)).First();
|
||||
|
||||
configPropertyGroup.Condition = $" '$(Configuration)' == '{config}' ";
|
||||
|
||||
foreach (var property in configPropertyGroup.Properties)
|
||||
{
|
||||
var propertyInfo = new DefaultProjectPropertyInfo
|
||||
{
|
||||
Name = property.Name,
|
||||
Value = property.Value,
|
||||
Condition = property.Condition,
|
||||
ParentCondition = property.Parent.Condition
|
||||
};
|
||||
|
||||
allProperties.Add(propertyInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddPropertyDefault(List<DefaultProjectPropertyInfo> allProperties, ProjectRootElement msbuild, string propertyName, int? index=null, bool ignoreConditions=false)
|
||||
{
|
||||
var properties = msbuild.Properties.Where(p => p.Name == propertyName).ToList();
|
||||
if (!properties.Any())
|
||||
{
|
||||
throw new Exception("property not found:" + propertyName);
|
||||
}
|
||||
|
||||
if (properties.Count() > 1 && index == null)
|
||||
{
|
||||
throw new Exception("More than one property found but index is null:" + propertyName);
|
||||
}
|
||||
|
||||
var property = properties[index ?? 0];
|
||||
|
||||
if (ignoreConditions)
|
||||
{
|
||||
var propertyInfo = new DefaultProjectPropertyInfo
|
||||
{
|
||||
Name = property.Name,
|
||||
Value = property.Value,
|
||||
Condition = null,
|
||||
ParentCondition = null
|
||||
};
|
||||
|
||||
allProperties.Add(propertyInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
var propertyInfo = new DefaultProjectPropertyInfo
|
||||
{
|
||||
Name = property.Name,
|
||||
Value = property.Value,
|
||||
Condition = property.Condition,
|
||||
ParentCondition = property.Parent.Condition
|
||||
};
|
||||
|
||||
allProperties.Add(propertyInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
tools/MigrationDefaultsConstructor/README.md
Normal file
7
tools/MigrationDefaultsConstructor/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Migration Defaults Constructor
|
||||
|
||||
This pulls the migration property and item defaults from a clone of the dotnet/sdk repo.
|
||||
|
||||
Run `./run.sh` to generate an sdkdefaults.json
|
||||
|
||||
Move it to the Microsoft.DotNet.ProjectJsonMigration project under `src` to override the defaults in dotnet (it's embedded as a resource).
|
23
tools/MigrationDefaultsConstructor/project.json
Executable file
23
tools/MigrationDefaultsConstructor/project.json
Executable file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"version": "1.0.0-*",
|
||||
"buildOptions": {
|
||||
"debugType": "portable",
|
||||
"emitEntryPoint": true
|
||||
},
|
||||
"dependencies": {},
|
||||
"frameworks": {
|
||||
"netcoreapp1.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"type": "platform",
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"dotnet": {
|
||||
"target": "project"
|
||||
},
|
||||
"Microsoft.Build.Runtime": "15.1.319-preview5"
|
||||
},
|
||||
"imports": ["dnxcore50", "portable-net45+win8"]
|
||||
}
|
||||
}
|
||||
}
|
22
tools/MigrationDefaultsConstructor/run.sh
Executable file
22
tools/MigrationDefaultsConstructor/run.sh
Executable file
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
SOURCE="${BASH_SOURCE[0]}"
|
||||
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
|
||||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||
SOURCE="$(readlink "$SOURCE")"
|
||||
[[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
||||
done
|
||||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||
|
||||
rm -rf bin obj
|
||||
dotnet publish -o bin -f netcoreapp1.0
|
||||
cp -a "$DIR/bin/runtimes/any/native/." "$DIR/bin"
|
||||
|
||||
sdkRevision="cc1fc023e3375b3944dbedfdd4ba2b5d2cbd01f0"
|
||||
sdkRoot="$DIR/bin/sdk"
|
||||
(cd bin && \
|
||||
git clone https://github.com/dotnet/sdk.git && \
|
||||
cd sdk && \
|
||||
git reset --hard $sdkRevision)
|
||||
|
||||
dotnet "$DIR/bin/MigrationDefaultsConstructor.dll" "$sdkRoot"
|
Loading…
Add table
Reference in a new issue