summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build.proj36
-rw-r--r--lib/DotNetOpenAuth.BuildTasks.dllbin67072 -> 71680 bytes
-rw-r--r--lib/DotNetOpenAuth.BuildTasks.pdbbin167424 -> 161280 bytes
-rw-r--r--src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj1
-rw-r--r--src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs134
-rw-r--r--src/DotNetOpenAuth.BuildTasks/Utilities.cs31
6 files changed, 187 insertions, 15 deletions
diff --git a/build.proj b/build.proj
index 11e5e68..4dbd083 100644
--- a/build.proj
+++ b/build.proj
@@ -158,7 +158,7 @@
<MSBuild Projects="@(ProjectTemplates)" />
</Target>
- <Target Name="ProjectTemplatesLayout" DependsOnTargets="GetBuildVersion;BuildUnifiedProduct;ReSignDelaySignedAssemblies;BuildProjectTemplates">
+ <Target Name="ProjectTemplatesLayout" DependsOnTargets="GetBuildVersion;BuildUnifiedProduct;ReSignDelaySignedAssemblies;BuildProjectTemplates;VsixManifestLayout">
<ItemGroup>
<ProjectTemplatesSource Include="$(ProjectRoot)projecttemplates\**\*"
Exclude="
@@ -228,11 +228,6 @@
<CopyWithTokenSubstitution SourceFiles="@(ProjectTemplatesTransformSource)" DestinationFiles="@(ProjectTemplatesTransformLayout)">
<Output TaskParameter="CopiedFiles" ItemName="CopiedProjectTemplateFiles" />
</CopyWithTokenSubstitution>
- <Purge Directories="$(ProjectTemplatesLayoutPath)"
- IntendedFiles="
- @(ProjectTemplatesLayout);
- @(ProjectTemplatesTransformLayout);
- " />
<ChangeProjectReferenceToAssemblyReference
Projects="@(CopiedProjectTemplateFiles)"
Condition="'%(Extension)' == '.csproj'"
@@ -252,7 +247,23 @@
ProjectItemTypes="@(VsTemplateProjectItemTypes)"
ReplaceParametersExtensions="@(VsTemplateParameterReplaceExtensions)"
Templates="@(VSProjectTemplates)"
- />
+ VsixManifest="@(ProjectTemplates2010TransformLayout)"
+ EnsureMaxPath="240"
+ Condition=" '%(ProjectTemplates2010TransformLayout.Extension)' == '.vsixmanifest' "
+ >
+ <Output TaskParameter="MaxPathAdjustedPaths" ItemName="ProjectItemShortPathAdjustments"/>
+ </MergeProjectWithVSTemplate>
+
+ <ItemGroup>
+ <ProjectTemplateIntendedFiles Include="
+ @(ProjectTemplatesLayout);
+ @(ProjectTemplatesTransformLayout);
+ %(ProjectItemShortPathAdjustments.ShortPath);
+ " />
+ <ProjectTemplateIntendedFiles Remove="@(ProjectItemShortPathAdjustments)" />
+ </ItemGroup>
+ <Purge Directories="$(ProjectTemplatesLayoutPath)"
+ IntendedFiles="@(ProjectTemplateIntendedFiles)" />
</Target>
<Target Name="ProjectTemplates2008" DependsOnTargets="ProjectTemplatesLayout">
@@ -394,7 +405,7 @@
/>
</Target>
- <Target Name="VsixLayout" DependsOnTargets="ProjectTemplates2010">
+ <Target Name="VsixManifestLayout">
<ItemGroup>
<ProjectTemplates2010TransformSource Include="
$(ProjectRoot)vsix\extension.vsixmanifest;
@@ -405,7 +416,12 @@
<SkipUnchangedFiles>false</SkipUnchangedFiles>
</ProjectTemplates2010TransformSource>
<ProjectTemplates2010TransformLayout Include="@(ProjectTemplates2010TransformSource->'$(ExtensionVsixLayoutDirectory)%(RecursiveDir)%(FileName)%(Extension)')" />
-
+ </ItemGroup>
+ <CopyWithTokenSubstitution SourceFiles="@(ProjectTemplates2010TransformSource)" DestinationFiles="@(ProjectTemplates2010TransformLayout)" />
+ </Target>
+
+ <Target Name="VsixLayout" DependsOnTargets="ProjectTemplates2010;VsixManifestLayout">
+ <ItemGroup>
<ExtensionVsixSources Include="
$(ProjectRoot)vsix\*;
" Exclude="
@@ -423,8 +439,6 @@
</ItemGroup>
<Copy SourceFiles="@(ExtensionVsixSources)" DestinationFiles="@(ExtensionVsixTargets)" SkipUnchangedFiles="true" />
- <CopyWithTokenSubstitution SourceFiles="@(ProjectTemplates2010TransformSource)" DestinationFiles="@(ProjectTemplates2010TransformLayout)" />
-
<Purge Directories="$(ExtensionVsixLayoutDirectory)" IntendedFiles="@(ExtensionVsixContents)" />
</Target>
diff --git a/lib/DotNetOpenAuth.BuildTasks.dll b/lib/DotNetOpenAuth.BuildTasks.dll
index 8edc333..7882acc 100644
--- a/lib/DotNetOpenAuth.BuildTasks.dll
+++ b/lib/DotNetOpenAuth.BuildTasks.dll
Binary files differ
diff --git a/lib/DotNetOpenAuth.BuildTasks.pdb b/lib/DotNetOpenAuth.BuildTasks.pdb
index e9ed7e3..88025fc 100644
--- a/lib/DotNetOpenAuth.BuildTasks.pdb
+++ b/lib/DotNetOpenAuth.BuildTasks.pdb
Binary files differ
diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj
index 3f76f78..f107e81 100644
--- a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj
+++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj
@@ -131,6 +131,7 @@
<DependentUpon>TaskStrings.resx</DependentUpon>
</Compile>
<Compile Include="Trim.cs" />
+ <Compile Include="Utilities.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="TaskStrings.resx">
diff --git a/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs b/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs
index 1a8a17d..c24d634 100644
--- a/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs
+++ b/src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs
@@ -7,17 +7,25 @@
namespace DotNetOpenAuth.BuildTasks {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ 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;
+ using System.Globalization;
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; }
@@ -29,9 +37,36 @@ namespace DotNetOpenAuth.BuildTasks {
public ITaskItem[] Templates { get; set; }
/// <summary>
+ /// Gets or sets the path to the .vsixmanifest file that will be used to assist
+ /// in calculating the actual full path of project items.
+ /// </summary>
+ /// <remarks>
+ /// This property is required if <see cref="EnsureMaxPath"/> &gt; 0;
+ /// </remarks>
+ public ITaskItem VsixManifest { 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 EnsureMaxPath { get; set; }
+
+ /// <summary>
+ /// Gets or sets the project items that had to be renamed to comply with maximum path length requirements.
+ /// </summary>
+ /// <remarks>
+ /// The item name is the original full path. The ShortPath metadata contains the shortened full path.
+ /// </remarks>
+ [Output]
+ public ITaskItem[] MaxPathAdjustedPaths { get; set; }
+
+ /// <summary>
/// Executes this instance.
/// </summary>
public override bool Execute() {
+ var shortenedItems = new List<ITaskItem>();
+
foreach(ITaskItem sourceTemplateTaskItem in this.Templates) {
var template = XElement.Load(sourceTemplateTaskItem.ItemSpec);
var templateContentElement = template.Element(XName.Get("TemplateContent", VSTemplateNamespace));
@@ -42,6 +77,7 @@ namespace DotNetOpenAuth.BuildTasks {
}
var projectPath = Path.Combine(Path.GetDirectoryName(sourceTemplateTaskItem.ItemSpec), projectElement.Attribute("File").Value);
+ 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);
@@ -66,15 +102,74 @@ namespace DotNetOpenAuth.BuildTasks {
if (replaceParameters) {
projectItem.SetAttributeValue("ReplaceParameters", "true");
}
+
+ if (this.EnsureMaxPath > 0) {
+ string estimatedFullPath = EstimateFullPathInProjectCache(Path.GetFileNameWithoutExtension(sourceProject.FullFileName), item.Include);
+ if (estimatedFullPath.Length > this.EnsureMaxPath) {
+ string leafName = Path.GetFileName(item.Include);
+ int targetLeafLength = leafName.Length - (estimatedFullPath.Length - this.EnsureMaxPath);
+ string shortenedFileName = CreateUniqueShortFileName(leafName, targetLeafLength);
+ string shortenedRelativePath = Path.Combine(Path.GetDirectoryName(item.Include), shortenedFileName);
+ string shortenedEstimatedFullPath = EstimateFullPathInProjectCache(Path.GetFileNameWithoutExtension(sourceProject.FullFileName), shortenedRelativePath);
+ if (shortenedEstimatedFullPath.Length <= this.EnsureMaxPath) {
+ this.Log.LogMessage(
+ "Renaming long project item '{0}' to '{1}' within project template to avoid MAX_PATH issues. The instantiated project will remain unchanged.",
+ item.Include,
+ shortenedRelativePath);
+ projectItem.SetAttributeValue("TargetFileName", Path.GetFileName(item.Include));
+ projectItem.Value = shortenedFileName;
+ string originalFullPath = Path.Combine(projectDirectory, item.Include);
+ string shortenedFullPath = Path.Combine(projectDirectory, shortenedRelativePath);
+ if (File.Exists(shortenedFullPath)) {
+ File.Delete(shortenedFullPath); // File.Move can't overwrite files
+ }
+ File.Move(originalFullPath, shortenedFullPath);
+
+ // Document the change so the build system can account for it.
+ TaskItem shortChange = new TaskItem(originalFullPath);
+ shortChange.SetMetadata("ShortPath", shortenedFullPath);
+ shortenedItems.Add(shortChange);
+ } else {
+ this.Log.LogError(
+ "Project item '{0}' exceeds maximum allowable length {1} by {2} characters and it cannot be sufficiently shortened. Estimated full path is: '{3}'.",
+ item.Include,
+ this.EnsureMaxPath,
+ estimatedFullPath.Length - this.EnsureMaxPath,
+ estimatedFullPath);
+ }
+ }
+ }
}
}
template.Save(sourceTemplateTaskItem.ItemSpec);
}
+ this.MaxPathAdjustedPaths = shortenedItems.ToArray();
return !Log.HasLoggedErrors;
}
+ private static string CreateUniqueShortFileName(string fileName, int targetLength) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(fileName));
+ Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));
+
+ // The filename may already full within the target length.
+ if (fileName.Length <= targetLength) {
+ return fileName;
+ }
+
+ string hashSuffix = Utilities.SuppressCharacters(fileName.GetHashCode().ToString("x"), Path.GetInvalidFileNameChars(), '_');
+ string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
+ string extension = Path.GetExtension(fileName);
+ string hashSuffixWithExtension = hashSuffix + extension;
+
+ // If the target length is itself shorter than the hash code, then we won't meet our goal,
+ // but at least put the hash in there so it's unique, and we'll return a string that's too long.
+ string shortenedFileName = fileName.Substring(0, Math.Max(0, targetLength - hashSuffixWithExtension.Length)) + hashSuffixWithExtension;
+
+ return shortenedFileName;
+ }
+
private static XElement FindOrCreateParent(string directoryName, XElement projectElement) {
Contract.Requires<ArgumentNullException>(projectElement != null);
@@ -99,5 +194,36 @@ namespace DotNetOpenAuth.BuildTasks {
return parent;
}
+
+ private string EstimateFullPathInProjectCache(string projectName, string itemRelativePath) {
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(projectName));
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(itemRelativePath));
+
+ const string PathRoot = @"c:\documents and settings\usernameZZZ\AppData\Local\Microsoft\VisualStudio\10.0\Extensions\";
+ string subPath;
+ if (!vsixContributionToPath.TryGetValue(projectName, out subPath)) {
+ if (this.VsixManifest == null) {
+ this.Log.LogError("The task parameter VsixManifest is required but missing.");
+ }
+ var vsixDocument = XDocument.Load(this.VsixManifest.ItemSpec);
+ XElement vsix = vsixDocument.Element(XName.Get("Vsix", VsixNamespace));
+ XElement identifier = vsix.Element(XName.Get("Identifier", VsixNamespace));
+ XElement content = vsix.Element(XName.Get("Content", VsixNamespace));
+ string author = identifier.Element(XName.Get("Author", VsixNamespace)).Value;
+ string name = identifier.Element(XName.Get("Name", VsixNamespace)).Value;
+ string version = identifier.Element(XName.Get("Version", VsixNamespace)).Value;
+ string pt = content.Element(XName.Get("ProjectTemplate", VsixNamespace)).Value;
+ vsixContributionToPath[projectName] = subPath = string.Format(
+ CultureInfo.InvariantCulture,
+ @"{0}\{1}\{2}\~PC\{3}\CSharp\Web\{4}.zip\",
+ author,
+ name,
+ version,
+ pt,
+ projectName
+ );
+ }
+ return Path.Combine(PathRoot + subPath + Path.GetFileNameWithoutExtension(projectName), itemRelativePath);
+ }
}
}
diff --git a/src/DotNetOpenAuth.BuildTasks/Utilities.cs b/src/DotNetOpenAuth.BuildTasks/Utilities.cs
new file mode 100644
index 0000000..80e1733
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/Utilities.cs
@@ -0,0 +1,31 @@
+//-----------------------------------------------------------------------
+// <copyright file="Utilities.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+
+ internal static class Utilities {
+ internal static string SuppressCharacters(string fileName, char[] suppress, char replacement) {
+ Contract.Requires<ArgumentNullException>(fileName != null);
+ Contract.Requires<ArgumentNullException>(suppress != null);
+
+ if (fileName.IndexOfAny(suppress) < 0) {
+ return fileName;
+ }
+
+ StringBuilder builder = new StringBuilder(fileName);
+ foreach (char ch in suppress) {
+ builder.Replace(ch, replacement);
+ }
+
+ return builder.ToString();
+ }
+ }
+}