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[]
|
private IMigrationRule[] Rules => new IMigrationRule[]
|
||||||
{
|
{
|
||||||
|
new AddDefaultsToProjectRule(),
|
||||||
new MigrateRootOptionsRule(),
|
new MigrateRootOptionsRule(),
|
||||||
new MigrateTFMRule(),
|
new MigrateTFMRule(),
|
||||||
new MigrateBuildOptionsRule(),
|
new MigrateBuildOptionsRule(),
|
||||||
|
@ -20,6 +21,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
new MigratePackageDependenciesAndToolsRule(),
|
new MigratePackageDependenciesAndToolsRule(),
|
||||||
new MigrateConfigurationsRule(),
|
new MigrateConfigurationsRule(),
|
||||||
new MigrateScriptsRule(),
|
new MigrateScriptsRule(),
|
||||||
|
new RemoveDefaultsFromProjectRule(),
|
||||||
|
new CleanOutputProjectRule(),
|
||||||
new SaveOutputProjectRule()
|
new SaveOutputProjectRule()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,19 +12,22 @@ namespace Microsoft.DotNet.ProjectJsonMigration
|
||||||
public string OutputDirectory { get; }
|
public string OutputDirectory { get; }
|
||||||
public string SdkPackageVersion { get; }
|
public string SdkPackageVersion { get; }
|
||||||
public ProjectRootElement MSBuildProjectTemplate { get; }
|
public ProjectRootElement MSBuildProjectTemplate { get; }
|
||||||
|
public string SdkDefaultsFilePath { get; }
|
||||||
|
|
||||||
public MigrationSettings(
|
public MigrationSettings(
|
||||||
string projectDirectory,
|
string projectDirectory,
|
||||||
string outputDirectory,
|
string outputDirectory,
|
||||||
string sdkPackageVersion,
|
string sdkPackageVersion,
|
||||||
ProjectRootElement msBuildProjectTemplate,
|
ProjectRootElement msBuildProjectTemplate,
|
||||||
string projectXprojFilePath=null)
|
string projectXprojFilePath=null,
|
||||||
|
string sdkDefaultsFilePath=null)
|
||||||
{
|
{
|
||||||
ProjectDirectory = projectDirectory;
|
ProjectDirectory = projectDirectory;
|
||||||
OutputDirectory = outputDirectory;
|
OutputDirectory = outputDirectory;
|
||||||
SdkPackageVersion = sdkPackageVersion;
|
SdkPackageVersion = sdkPackageVersion;
|
||||||
MSBuildProjectTemplate = msBuildProjectTemplate != null ? msBuildProjectTemplate.DeepClone() : null;
|
MSBuildProjectTemplate = msBuildProjectTemplate != null ? msBuildProjectTemplate.DeepClone() : null;
|
||||||
ProjectXProjFilePath = projectXprojFilePath;
|
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",
|
@"$(OutputPath)\$(TargetFramework)\$(AssemblyName).xml",
|
||||||
compilerOptions => compilerOptions.GenerateXmlDocumentation != null && compilerOptions.GenerateXmlDocumentation.Value);
|
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 =>
|
private IncludeContextTransform CompileFilesTransform =>
|
||||||
new IncludeContextTransform("Compile", transformMappings: false);
|
new IncludeContextTransform("Compile", transformMappings: false);
|
||||||
|
|
||||||
|
@ -175,7 +170,6 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
DelaySignTransform,
|
DelaySignTransform,
|
||||||
PublicSignTransform,
|
PublicSignTransform,
|
||||||
DebugTypeTransform,
|
DebugTypeTransform,
|
||||||
OutputNameTransform,
|
|
||||||
XmlDocTransform,
|
XmlDocTransform,
|
||||||
XmlDocTransformFilePath,
|
XmlDocTransformFilePath,
|
||||||
PreserveCompilationContextTransform
|
PreserveCompilationContextTransform
|
||||||
|
@ -242,14 +236,14 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
|
|
||||||
if (!PropertiesAreEqual(nonConfigurationOutput, configurationOutput))
|
if (!PropertiesAreEqual(nonConfigurationOutput, configurationOutput))
|
||||||
{
|
{
|
||||||
transformApplicator.Execute(configurationOutput, propertyGroup);
|
transformApplicator.Execute(configurationOutput, propertyGroup, mergeExisting: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var includeContextTransformExecute in _includeContextTransformExecutes)
|
foreach (var includeContextTransformExecute in _includeContextTransformExecutes)
|
||||||
{
|
{
|
||||||
var nonConfigurationOutput = includeContextTransformExecute(compilerOptions, projectDirectory);
|
var nonConfigurationOutput = includeContextTransformExecute(compilerOptions, projectDirectory);
|
||||||
var configurationOutput = includeContextTransformExecute(configurationCompilerOptions, projectDirectory).ToArray();
|
var configurationOutput = includeContextTransformExecute(configurationCompilerOptions, projectDirectory);
|
||||||
|
|
||||||
transformApplicator.Execute(configurationOutput, itemGroup, mergeExisting: true);
|
transformApplicator.Execute(configurationOutput, itemGroup, mergeExisting: true);
|
||||||
}
|
}
|
||||||
|
@ -293,7 +287,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
{
|
{
|
||||||
foreach (var transform in _propertyTransforms)
|
foreach (var transform in _propertyTransforms)
|
||||||
{
|
{
|
||||||
transformApplicator.Execute(transform.Transform(compilerOptions), propertyGroup);
|
transformApplicator.Execute(transform.Transform(compilerOptions), propertyGroup, mergeExisting: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var includeContextTransformExecute in _includeContextTransformExecutes)
|
foreach (var includeContextTransformExecute in _includeContextTransformExecutes)
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
var token = rawProject.GetValue(prop.Key);
|
var token = rawProject.GetValue(prop.Key);
|
||||||
if (token != null)
|
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)
|
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 tfmDependencyMap = new Dictionary<string, IEnumerable<ProjectLibraryDependency>>();
|
||||||
var targetFrameworks = project.GetTargetFrameworks();
|
var targetFrameworks = project.GetTargetFrameworks();
|
||||||
|
|
||||||
|
var noFrameworkPackageReferenceItemGroup = migrationRuleInputs.OutputMSBuildProject.AddItemGroup();
|
||||||
|
|
||||||
// Inject Sdk dependency
|
// Inject Sdk dependency
|
||||||
_transformApplicator.Execute(
|
_transformApplicator.Execute(
|
||||||
PackageDependencyInfoTransform.Transform(
|
PackageDependencyInfoTransform.Transform(
|
||||||
|
@ -45,7 +47,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
Name = ConstantPackageNames.CSdkPackageName,
|
Name = ConstantPackageNames.CSdkPackageName,
|
||||||
Version = migrationSettings.SdkPackageVersion,
|
Version = migrationSettings.SdkPackageVersion,
|
||||||
PrivateAssets = "All"
|
PrivateAssets = "All"
|
||||||
}), migrationRuleInputs.CommonItemGroup);
|
}), noFrameworkPackageReferenceItemGroup, mergeExisting: false);
|
||||||
|
|
||||||
// Migrate Direct Deps first
|
// Migrate Direct Deps first
|
||||||
MigrateDependencies(
|
MigrateDependencies(
|
||||||
|
@ -53,7 +55,8 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
migrationRuleInputs.OutputMSBuildProject,
|
migrationRuleInputs.OutputMSBuildProject,
|
||||||
null,
|
null,
|
||||||
project.Dependencies,
|
project.Dependencies,
|
||||||
migrationRuleInputs.ProjectXproj);
|
migrationRuleInputs.ProjectXproj,
|
||||||
|
itemGroup: noFrameworkPackageReferenceItemGroup);
|
||||||
|
|
||||||
MigrationTrace.Instance.WriteLine($"Migrating {targetFrameworks.Count()} target frameworks");
|
MigrationTrace.Instance.WriteLine($"Migrating {targetFrameworks.Count()} target frameworks");
|
||||||
foreach (var targetFramework in targetFrameworks)
|
foreach (var targetFramework in targetFrameworks)
|
||||||
|
@ -80,7 +83,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
if (transform != null)
|
if (transform != null)
|
||||||
{
|
{
|
||||||
transform.Condition = targetFramework.FrameworkName.GetMSBuildCondition();
|
transform.Condition = targetFramework.FrameworkName.GetMSBuildCondition();
|
||||||
_transformApplicator.Execute(transform, commonPropertyGroup);
|
_transformApplicator.Execute(transform, commonPropertyGroup, mergeExisting: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -113,7 +116,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
|
|
||||||
foreach (var tool in project.Tools)
|
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,
|
ProjectRootElement output,
|
||||||
NuGetFramework framework,
|
NuGetFramework framework,
|
||||||
IEnumerable<ProjectLibraryDependency> dependencies,
|
IEnumerable<ProjectLibraryDependency> dependencies,
|
||||||
ProjectRootElement xproj)
|
ProjectRootElement xproj,
|
||||||
|
ProjectItemGroupElement itemGroup=null)
|
||||||
{
|
{
|
||||||
var projectDependencies = new HashSet<string>(GetAllProjectReferenceNames(project, framework, xproj));
|
var projectDependencies = new HashSet<string>(GetAllProjectReferenceNames(project, framework, xproj));
|
||||||
var packageDependencies = dependencies.Where(d => !projectDependencies.Contains(d.Name)).ToList();
|
var packageDependencies = dependencies.Where(d => !projectDependencies.Contains(d.Name)).ToList();
|
||||||
|
|
||||||
string condition = framework?.GetMSBuildCondition() ?? "";
|
string condition = framework?.GetMSBuildCondition() ?? "";
|
||||||
var itemGroup = output.ItemGroups.FirstOrDefault(i => i.Condition == condition)
|
itemGroup = itemGroup
|
||||||
|
?? output.ItemGroups.FirstOrDefault(i => i.Condition == condition)
|
||||||
?? output.AddItemGroup();
|
?? output.AddItemGroup();
|
||||||
itemGroup.Condition = condition;
|
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)
|
foreach (var csprojTransformedReference in csprojTransformedReferences)
|
||||||
{
|
{
|
||||||
_transformApplicator.Execute(csprojTransformedReference, migrationRuleInputs.CommonItemGroup);
|
_transformApplicator.Execute(csprojTransformedReference, migrationRuleInputs.CommonItemGroup, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return csprojTransformedReferences.SelectMany(r => r.Includes());
|
return csprojTransformedReferences.SelectMany(r => r.Includes());
|
||||||
|
@ -108,10 +108,10 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
|
|
||||||
foreach (var projectDependencyTransformResult in projectDependencyTransformResults)
|
foreach (var projectDependencyTransformResult in projectDependencyTransformResults)
|
||||||
{
|
{
|
||||||
_transformApplicator.Execute(projectDependencyTransformResult, itemGroup);
|
_transformApplicator.Execute(projectDependencyTransformResult, itemGroup, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AddItemTransform<ProjectDependency> ProjectDependencyTransform => new AddItemTransform<ProjectDependency>(
|
private AddItemTransform<ProjectDependency> ProjectDependencyTransform => new AddItemTransform<ProjectDependency>(
|
||||||
"ProjectReference",
|
"ProjectReference",
|
||||||
dep =>
|
dep =>
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Rules
|
||||||
|
|
||||||
foreach (var transformResult in transformResults)
|
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(
|
_transformApplicator.Execute(
|
||||||
FrameworkTransform.Transform(
|
FrameworkTransform.Transform(
|
||||||
migrationRuleInputs.ProjectContexts.Single().TargetFramework),
|
migrationRuleInputs.ProjectContexts.Single().TargetFramework),
|
||||||
propertyGroup);
|
propertyGroup,
|
||||||
|
mergeExisting: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_transformApplicator.Execute(
|
_transformApplicator.Execute(
|
||||||
FrameworksTransform.Transform(
|
FrameworksTransform.Transform(
|
||||||
migrationRuleInputs.ProjectContexts.Select(p => p.TargetFramework)),
|
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");
|
var outputProject = Path.Combine(migrationSettings.OutputDirectory, outputName + ".csproj");
|
||||||
|
|
||||||
CleanEmptyPropertyAndItemGroups(migrationRuleInputs.OutputMSBuildProject);
|
|
||||||
|
|
||||||
migrationRuleInputs.OutputMSBuildProject.Save(outputProject);
|
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-*",
|
"version": "1.0.0-preview3-*",
|
||||||
"buildOptions": {
|
"buildOptions": {
|
||||||
"warningsAsErrors": true,
|
"warningsAsErrors": true,
|
||||||
"keyFile": "../../tools/Key.snk"
|
"keyFile": "../../tools/Key.snk",
|
||||||
|
"embed" :{
|
||||||
|
"include" : "sdkdefaults.json"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.DotNet.Compiler.Common": {
|
"Microsoft.DotNet.Compiler.Common": {
|
||||||
|
@ -17,7 +20,6 @@
|
||||||
"Microsoft.Build": "15.1.319-preview5"
|
"Microsoft.Build": "15.1.319-preview5"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"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>(
|
void Execute<T, U>(
|
||||||
T element,
|
T element,
|
||||||
U destinationElement) where T : ProjectElement where U : ProjectElementContainer;
|
U destinationElement,
|
||||||
|
bool mergeExisting) where T : ProjectElement where U : ProjectElementContainer;
|
||||||
|
|
||||||
void Execute<T, U>(
|
void Execute<T, U>(
|
||||||
IEnumerable<T> elements,
|
IEnumerable<T> elements,
|
||||||
U destinationElement) where T : ProjectElement where U : ProjectElementContainer;
|
U destinationElement,
|
||||||
|
bool mergeExisting) where T : ProjectElement where U : ProjectElementContainer;
|
||||||
void Execute(
|
|
||||||
ProjectItemElement item,
|
|
||||||
ProjectItemGroupElement destinationItemGroup,
|
|
||||||
bool mergeExisting);
|
|
||||||
|
|
||||||
void Execute(
|
|
||||||
IEnumerable<ProjectItemElement> items,
|
|
||||||
ProjectItemGroupElement destinationItemGroup,
|
|
||||||
bool mergeExisting);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,248 +10,44 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Transforms
|
||||||
{
|
{
|
||||||
public class TransformApplicator : ITransformApplicator
|
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>(
|
public void Execute<T, U>(
|
||||||
T element,
|
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))
|
_itemTransformApplicator.Execute(element, destinationElement, mergeExisting);
|
||||||
{
|
}
|
||||||
var item = destinationElement.ContainingProject.CreateItemElement("___TEMP___");
|
else if (typeof(T) == typeof(ProjectPropertyElement))
|
||||||
item.CopyFrom(element);
|
{
|
||||||
|
_propertyTransformApplicator.Execute(element, destinationElement, mergeExisting);
|
||||||
destinationElement.AppendChild(item);
|
}
|
||||||
item.AddMetadata((element as ProjectItemElement).Metadata);
|
else
|
||||||
}
|
{
|
||||||
else if (typeof(T) == typeof(ProjectPropertyElement))
|
throw new ArgumentException($"Unexpected type {nameof(T)}");
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute<T, U>(
|
public void Execute<T, U>(
|
||||||
IEnumerable<T> elements,
|
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)
|
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]
|
[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(@"
|
var mockProj = RunBuildOptionsRuleOnPj(@"
|
||||||
{
|
{
|
||||||
""buildOptions"": { }
|
""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.Count().Should().Be(2);
|
||||||
mockProj.Items.First(i => i.ItemType == "Compile").Include.Should().Be(@"**\*.cs");
|
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 == "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");
|
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]
|
[Fact]
|
||||||
public void Migrating_xmlDoc_populates_GenerateDocumentationFile()
|
public void Migrating_xmlDoc_populates_GenerateDocumentationFile()
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
|
||||||
item2.AddMetadata(metadata[1].MetadataName, metadata[1].GetMetadataValue(null));
|
item2.AddMetadata(metadata[1].MetadataName, metadata[1].GetMetadataValue(null));
|
||||||
|
|
||||||
var transformApplicator = new TransformApplicator();
|
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);
|
itemGroup.Items.Count.Should().Be(1);
|
||||||
|
|
||||||
|
@ -64,5 +64,26 @@ namespace Microsoft.DotNet.ProjectJsonMigration.Tests
|
||||||
|
|
||||||
foundMetadata.All(kv => kv.Value).Should().BeTrue();
|
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
Add a link
Reference in a new issue