summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs')
-rw-r--r--src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs108
1 files changed, 88 insertions, 20 deletions
diff --git a/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs b/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs
index 1a8a17d..d162cd6 100644
--- a/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs
+++ b/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs
@@ -7,18 +7,27 @@
namespace DotNetOpenAuth.BuildTasks {
using System;
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.IO;
using System.Linq;
using System.Text;
- using Microsoft.Build.Framework;
- using Microsoft.Build.Utilities;
using System.Xml.Linq;
- using System.IO;
using Microsoft.Build.BuildEngine;
- using System.Diagnostics.Contracts;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
public class MergeProjectWithVSTemplate : Task {
internal const string VSTemplateNamespace = "http://schemas.microsoft.com/developer/vstemplate/2005";
+ internal const string VsixNamespace = "http://schemas.microsoft.com/developer/vsx-schema/2010";
+
+ /// <summary>
+ /// A dictionary where the key is the project name and the value is the path contribution.
+ /// </summary>
+ private Dictionary<string, string> vsixContributionToPath = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
[Required]
public string[] ProjectItemTypes { get; set; }
@@ -26,13 +35,44 @@ namespace DotNetOpenAuth.BuildTasks {
public string[] ReplaceParametersExtensions { get; set; }
[Required]
- public ITaskItem[] Templates { get; set; }
+ public ITaskItem[] SourceTemplates { get; set; }
+
+ [Required]
+ public ITaskItem[] SourceProjects { get; set; }
+
+ [Required]
+ public ITaskItem[] DestinationTemplates { get; set; }
+
+ public ITaskItem[] SourcePathExceptions { get; set; }
+
+ /// <summary>
+ /// Gets or sets the maximum length a project item's relative path should
+ /// be limited to, artificially renaming them as necessary.
+ /// </summary>
+ /// <value>Use 0 to disable the renaming feature.</value>
+ public int MaximumRelativePathLength { get; set; }
+
+ /// <summary>
+ /// Gets or sets the project item paths from the source project to copy to the destination location.
+ /// </summary>
+ [Output]
+ public ITaskItem[] ProjectItems { get; set; }
/// <summary>
/// Executes this instance.
/// </summary>
public override bool Execute() {
- foreach(ITaskItem sourceTemplateTaskItem in this.Templates) {
+ if (this.DestinationTemplates.Length != this.SourceTemplates.Length) {
+ this.Log.LogError("SourceTemplates array has length {0} while DestinationTemplates array has length {1}, but must equal.", this.SourceTemplates.Length, this.DestinationTemplates.Length);
+ }
+ if (this.SourceProjects.Length != this.SourceTemplates.Length) {
+ this.Log.LogError("SourceTemplates array has length {0} while SourceProjects array has length {1}, but must equal.", this.SourceTemplates.Length, this.SourceProjects.Length);
+ }
+
+ var projectItemsToCopy = new List<ITaskItem>();
+
+ for (int iTemplate = 0; iTemplate < this.SourceTemplates.Length; iTemplate++) {
+ ITaskItem sourceTemplateTaskItem = this.SourceTemplates[iTemplate];
var template = XElement.Load(sourceTemplateTaskItem.ItemSpec);
var templateContentElement = template.Element(XName.Get("TemplateContent", VSTemplateNamespace));
var projectElement = templateContentElement.Element(XName.Get("Project", VSTemplateNamespace));
@@ -41,37 +81,65 @@ namespace DotNetOpenAuth.BuildTasks {
continue;
}
- var projectPath = Path.Combine(Path.GetDirectoryName(sourceTemplateTaskItem.ItemSpec), projectElement.Attribute("File").Value);
+ var projectPath = this.SourceProjects[iTemplate].ItemSpec;
+ var projectDirectory = Path.GetDirectoryName(Path.Combine(Path.GetDirectoryName(sourceTemplateTaskItem.GetMetadata("FullPath")), projectElement.Attribute("File").Value));
Log.LogMessage("Merging project \"{0}\" with \"{1}\".", projectPath, sourceTemplateTaskItem.ItemSpec);
var sourceProject = new Project();
sourceProject.Load(projectPath);
+ var projectItems = sourceProject.EvaluatedItems.Cast<BuildItem>().Where(item => this.ProjectItemTypes.Contains(item.Name));
+
+ // Figure out where every project item is in source, and where it will go in the destination,
+ // taking into account a given maximum path length that may require that we shorten the path.
+ PathSegment root = new PathSegment();
+ root.Add(projectItems.Select(item => item.Include));
+ root.EnsureSelfAndChildrenNoLongerThan(this.MaximumRelativePathLength);
// Collect the project items from the project that are appropriate
// to include in the .vstemplate file.
- var itemsByFolder = from item in sourceProject.EvaluatedItems.Cast<BuildItem>()
- where this.ProjectItemTypes.Contains(item.Name)
- orderby item.Include
- group item by Path.GetDirectoryName(item.Include);
- foreach (var folder in itemsByFolder) {
- XElement parentNode = FindOrCreateParent(folder.Key, projectElement);
-
- foreach (var item in folder) {
- bool replaceParameters = this.ReplaceParametersExtensions.Contains(Path.GetExtension(item.Include));
+ foreach (var folder in root.SelfAndDescendents.Where(path => !path.IsLeaf && path.LeafChildren.Any())) {
+ XElement parentNode = projectElement;
+ parentNode = FindOrCreateParent(folder.CurrentPath, projectElement);
+ if (folder.NameChanged) {
+ parentNode.SetAttributeValue("TargetFolderName", folder.OriginalName);
+ }
+
+ foreach (var item in folder.LeafChildren) {
var itemName = XName.Get("ProjectItem", VSTemplateNamespace);
- var projectItem = parentNode.Elements(itemName).FirstOrDefault(el => string.Equals(el.Value, Path.GetFileName(item.Include), StringComparison.OrdinalIgnoreCase));
+ // The project item MAY be hard-coded in the .vstemplate file, under the original name.
+ var projectItem = parentNode.Elements(itemName).FirstOrDefault(el => string.Equals(el.Value, Path.GetFileName(item.OriginalName), StringComparison.OrdinalIgnoreCase));
if (projectItem == null) {
- projectItem = new XElement(itemName, Path.GetFileName(item.Include));
+ projectItem = new XElement(itemName, item.CurrentName);
parentNode.Add(projectItem);
}
- if (replaceParameters) {
+ if (item.NameChanged) {
+ projectItem.Value = item.CurrentName; // set Value in case it was a hard-coded item in the .vstemplate file.
+ projectItem.SetAttributeValue("TargetFileName", item.OriginalName);
+ }
+ if (this.ReplaceParametersExtensions.Contains(Path.GetExtension(item.OriginalPath))) {
projectItem.SetAttributeValue("ReplaceParameters", "true");
}
}
}
- template.Save(sourceTemplateTaskItem.ItemSpec);
+ template.Save(this.DestinationTemplates[iTemplate].ItemSpec);
+ foreach (var pair in root.LeafDescendents) {
+ TaskItem item = new TaskItem(Path.Combine(Path.GetDirectoryName(this.SourceTemplates[iTemplate].ItemSpec), pair.OriginalPath));
+ string apparentSource = Path.Combine(Path.GetDirectoryName(this.SourceTemplates[iTemplate].ItemSpec), pair.OriginalPath);
+ var sourcePathException = this.SourcePathExceptions.FirstOrDefault(ex => string.Equals(ex.ItemSpec, apparentSource));
+ if (sourcePathException != null) {
+ item.SetMetadata("SourceFullPath", sourcePathException.GetMetadata("ActualSource"));
+ } else {
+ item.SetMetadata("SourceFullPath", Path.GetFullPath(apparentSource));
+ }
+ item.SetMetadata("DestinationFullPath", Path.GetFullPath(Path.Combine(Path.GetDirectoryName(this.DestinationTemplates[iTemplate].ItemSpec), pair.CurrentPath)));
+ item.SetMetadata("RecursiveDir", Path.GetDirectoryName(this.SourceTemplates[iTemplate].ItemSpec));
+ item.SetMetadata("Transform", this.ReplaceParametersExtensions.Contains(Path.GetExtension(pair.OriginalName)) ? "true" : "false");
+ projectItemsToCopy.Add(item);
+ }
}
+ this.ProjectItems = projectItemsToCopy.ToArray();
+
return !Log.HasLoggedErrors;
}