Skip to content

Commit 2fe1ac9

Browse files
committed
Fixes project template creation's long filenames and lib dependencies.
1 parent 48fd92d commit 2fe1ac9

File tree

5 files changed

+337
-102
lines changed

5 files changed

+337
-102
lines changed

projecttemplates/projecttemplates.proj

+18-20
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,22 @@
5555
<!-- ... and log4net -->
5656
<ProjectTemplateLibraries Include="$(ProjectRoot)lib\log4net.dll" />
5757
<ProjectTemplateLibraries Include="$(ProjectRoot)lib\log4net.xml" />
58-
<ProjectTemplateLibrariesTargets Include="@(ProjectTemplateLibraries->'$(ProjectTemplatesLayoutPath)RelyingPartyLogic\lib\%(CultureDir)%(FileName)%(Extension)')" />
58+
<ProjectTemplateLibrariesTargets Include="@(ProjectTemplateLibraries->'$(ProjectTemplatesLayoutPath)RelyingPartyLogic\lib\%(CultureDir)%(FileName)%(Extension)')">
59+
<ApparentSource>RelyingPartyLogic\lib\%(ProjectTemplateLibraries.CultureDir)%(FileName)%(Extension)</ApparentSource>
60+
<ActualSource>%(Identity)</ActualSource>
61+
</ProjectTemplateLibrariesTargets>
62+
<ProjectTemplateLibrariesSourceExceptions Include="@(ProjectTemplateLibrariesTargets->'%(ApparentSource)')"/>
5963

6064
<FixupReferenceAssemblies Include="@(ProjectTemplateLibrariesTargets)" Condition="'%(Extension)' == '.dll'" />
61-
<InjectedLibraryItems Include="@(ProjectTemplateLibrariesTargets->'lib\%(FileName)%(Extension)')" />
65+
<InjectedLibraryItems Include="@(ProjectTemplateLibraries->'lib\%(CultureDir)%(FileName)%(Extension)')">
66+
<Visible/>
67+
<UnsignedAssemblyPath/>
68+
<SymbolPath/>
69+
<OriginalItemSpec/>
70+
<MSBuildSourceProjectFile/>
71+
<MSBuildSourceTargetName/>
72+
<CultureDir/>
73+
</InjectedLibraryItems>
6274

6375
<VSProjectTemplates Include="**\*.vstemplate" Exclude="*.vstemplate" />
6476
<VSProjectTemplatesLayout Include="@(VSProjectTemplates->'$(ProjectTemplatesLayoutPath)%(RecursiveDir)%(FileName)%(Extension)')" />
@@ -90,6 +102,7 @@
90102
SourceTemplates="@(VSProjectTemplates)"
91103
SourceProjects="@(TemplateProjectsLayout)"
92104
DestinationTemplates="@(VSProjectTemplatesLayout)"
105+
SourcePathExceptions="@(ProjectTemplateLibrariesSourceExceptions)"
93106
MaximumRelativePathLength="$(ProjectTemplateMaxPath)"
94107
>
95108
<Output TaskParameter="ProjectItems" ItemName="TemplateProjectItems"/>
@@ -98,34 +111,19 @@
98111

99112
<Target Name="Layout" DependsOnTargets="$(LayoutDependsOn)">
100113
<ItemGroup>
101-
<TemplateProjectItems Condition="
102-
'%(Extension)' == '.cs'
103-
or '%(Extension)' == '.csproj'
104-
or '%(Extension)' == '.sql'
105-
or '%(Extension)' == '.config'
106-
or '%(Extension)' == '.Master'
107-
or '%(Extension)' == '.aspx'
108-
or '%(Extension)' == '.ascx'
109-
or '%(Extension)' == '.asax'
110-
or '%(Extension)' == '.ashx'
111-
">
114+
<TemplateProjectItems Condition=" '%(Transform)' == 'true' ">
112115
<BeforeTokens>%(RecursiveDir)</BeforeTokens>
113116
<AfterTokens>$safeprojectname$</AfterTokens>
114117
</TemplateProjectItems>
115118
<TemplateProjectItems>
116119
<SkipUnchangedFiles>true</SkipUnchangedFiles>
117120
</TemplateProjectItems>
118-
<TemplateProjectItemsForTransformSource Include="@(TemplateProjectItems->'%(SourceFullPath)')"
119-
Condition=" '%(TemplateProjectItems.RelativeDir)' != 'RelyingPartyLogic\lib\' "/>
120-
<TemplateProjectItemsForTransformLayout Include="@(TemplateProjectItems->'%(DestinationFullPath)')"
121-
Condition=" '%(TemplateProjectItems.RelativeDir)' != 'RelyingPartyLogic\lib\' "/>
121+
<TemplateProjectItemsForTransformSource Include="@(TemplateProjectItems->'%(SourceFullPath)')" />
122+
<TemplateProjectItemsForTransformLayout Include="@(TemplateProjectItems->'%(DestinationFullPath)')" />
122123
<RootVsTemplateSource Include="*.vstemplate" />
123124
<ProjectTemplatesSource Include="@(RootVsTemplateSource)" />
124125
<ProjectTemplatesLayout Include="@(RootVsTemplateSource->'$(ProjectTemplatesLayoutPath)%(FileName)%(Extension)')" />
125126

126-
<ProjectTemplatesSource Include="@(ProjectTemplateLibraries)" />
127-
<ProjectTemplatesLayout Include="@(ProjectTemplateLibrariesTargets)" />
128-
129127
<!-- Include the template icon -->
130128
<ProjectTemplatesSource Include="$(ProjectRoot)doc\logo\favicon.ico" />
131129
<ProjectTemplatesLayout Include="$(ProjectTemplatesLayoutPath)__TemplateIcon.ico" />

src/DotNetOpenAuth.BuildTasks/AddProjectItems.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66

77
namespace DotNetOpenAuth.BuildTasks {
88
using System;
9+
using System.Collections;
910
using System.Collections.Generic;
1011
using System.Linq;
1112
using System.Text;
1213
using Microsoft.Build.BuildEngine;
1314
using Microsoft.Build.Framework;
1415
using Microsoft.Build.Utilities;
15-
using System.Collections;
1616

1717
public class AddProjectItems : Task {
1818
/// <summary>
@@ -49,7 +49,10 @@ public override bool Execute() {
4949
BuildItem newItem = project.AddNewItem(itemType, projectItem.ItemSpec, false);
5050
var customMetadata = projectItem.CloneCustomMetadata();
5151
foreach (DictionaryEntry entry in customMetadata) {
52-
newItem.SetMetadata((string)entry.Key, (string)entry.Value);
52+
string value = (string)entry.Value;
53+
if (value.Length > 0) {
54+
newItem.SetMetadata((string)entry.Key, value);
55+
}
5356
}
5457
}
5558

src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
<Compile Include="JsPack.cs" />
119119
<Compile Include="NativeMethods.cs" />
120120
<Compile Include="ParseMaster.cs" />
121+
<Compile Include="PathSegment.cs" />
121122
<Compile Include="Publicize.cs" />
122123
<Compile Include="Purge.cs" />
123124
<Compile Include="ReSignDelaySignedAssemblies.cs" />

src/DotNetOpenAuth.BuildTasks/MergeProjectWithVSTemplate.cs

+31-80
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
namespace DotNetOpenAuth.BuildTasks {
88
using System;
99
using System.Collections.Generic;
10+
using System.Collections.ObjectModel;
1011
using System.Diagnostics.Contracts;
1112
using System.Globalization;
1213
using System.IO;
@@ -42,6 +43,8 @@ public class MergeProjectWithVSTemplate : Task {
4243
[Required]
4344
public ITaskItem[] DestinationTemplates { get; set; }
4445

46+
public ITaskItem[] SourcePathExceptions { get; set; }
47+
4548
/// <summary>
4649
/// Gets or sets the maximum length a project item's relative path should
4750
/// be limited to, artificially renaming them as necessary.
@@ -87,81 +90,50 @@ public override bool Execute() {
8790

8891
// Figure out where every project item is in source, and where it will go in the destination,
8992
// taking into account a given maximum path length that may require that we shorten the path.
90-
var sourceToDestinationProjectItemMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
91-
//var oversizedItemPaths = projectItems.Where(item => item.Include.Length > this.MaximumRelativePathLength);
92-
foreach (var item in projectItems) {
93-
var source = item.Include;
94-
var dest = item.Include;
95-
96-
// if (this.MaximumRelativePathLength > 0) {
97-
// if (item.Include.Length > this.MaximumRelativePathLength) {
98-
// string leafName = Path.GetFileName(item.Include);
99-
// int targetLeafLength = leafName.Length - (item.Include.Length - this.MaximumRelativePathLength);
100-
// string shortenedFileName = CreateUniqueShortFileName(leafName, targetLeafLength);
101-
// string shortenedRelativePath = Path.Combine(Path.GetDirectoryName(item.Include), shortenedFileName);
102-
// if (shortenedRelativePath.Length <= this.MaximumRelativePathLength) {
103-
// this.Log.LogMessage(
104-
// "Renaming long project item '{0}' to '{1}' within project template to avoid MAX_PATH issues. The instantiated project will remain unchanged.",
105-
// item.Include,
106-
// shortenedRelativePath);
107-
// projectItem.SetAttributeValue("TargetFileName", Path.GetFileName(item.Include));
108-
// projectItem.Value = shortenedFileName;
109-
// string originalFullPath = Path.Combine(projectDirectory, item.Include);
110-
// string shortenedFullPath = Path.Combine(projectDirectory, shortenedRelativePath);
111-
// if (File.Exists(shortenedFullPath)) {
112-
// File.Delete(shortenedFullPath); // File.Move can't overwrite files
113-
// }
114-
// File.Move(originalFullPath, shortenedFullPath);
115-
116-
// // Document the change so the build system can account for it.
117-
// TaskItem shortChange = new TaskItem(originalFullPath);
118-
// shortChange.SetMetadata("ShortPath", shortenedFullPath);
119-
// shortenedItems.Add(shortChange);
120-
// } else {
121-
// this.Log.LogError(
122-
// "Project item '{0}' exceeds maximum allowable length {1} by {2} characters and it cannot be sufficiently shortened. Estimated full path is: '{3}'.",
123-
// item.Include,
124-
// this.MaximumRelativePathLength,
125-
// item.Include.Length - this.MaximumRelativePathLength,
126-
// item.Include);
127-
// }
128-
// }
129-
//}
130-
131-
sourceToDestinationProjectItemMap[source] = dest;
132-
}
93+
PathSegment root = new PathSegment();
94+
root.Add(projectItems.Select(item => item.Include));
95+
root.EnsureSelfAndChildrenNoLongerThan(this.MaximumRelativePathLength);
13396

13497
// Collect the project items from the project that are appropriate
13598
// to include in the .vstemplate file.
136-
var itemsByFolder = from item in projectItems
137-
orderby item.Include
138-
group item by Path.GetDirectoryName(item.Include);
139-
140-
foreach (var folder in itemsByFolder) {
99+
foreach (var folder in root.SelfAndDescendents.Where(path => !path.IsLeaf && path.LeafChildren.Any())) {
141100
XElement parentNode = projectElement;
142-
parentNode = FindOrCreateParent(folder.Key, projectElement);
143-
//parentNode.SetAttributeValue("TargetFolderName", folder.Key);
101+
parentNode = FindOrCreateParent(folder.CurrentPath, projectElement);
102+
if (folder.NameChanged) {
103+
parentNode.SetAttributeValue("TargetFolderName", folder.OriginalName);
104+
}
144105

145-
foreach (var item in folder) {
146-
bool replaceParameters = this.ReplaceParametersExtensions.Contains(Path.GetExtension(item.Include));
106+
foreach (var item in folder.LeafChildren) {
147107
var itemName = XName.Get("ProjectItem", VSTemplateNamespace);
148-
var projectItem = parentNode.Elements(itemName).FirstOrDefault(el => string.Equals(el.Value, Path.GetFileName(item.Include), StringComparison.OrdinalIgnoreCase));
108+
// The project item MAY be hard-coded in the .vstemplate file, under the original name.
109+
var projectItem = parentNode.Elements(itemName).FirstOrDefault(el => string.Equals(el.Value, Path.GetFileName(item.OriginalName), StringComparison.OrdinalIgnoreCase));
149110
if (projectItem == null) {
150-
projectItem = new XElement(itemName, Path.GetFileName(item.Include));
111+
projectItem = new XElement(itemName, item.CurrentName);
151112
parentNode.Add(projectItem);
152113
}
153-
if (replaceParameters) {
114+
if (item.NameChanged) {
115+
projectItem.Value = item.CurrentName; // set Value in case it was a hard-coded item in the .vstemplate file.
116+
projectItem.SetAttributeValue("TargetFileName", item.OriginalName);
117+
}
118+
if (this.ReplaceParametersExtensions.Contains(Path.GetExtension(item.OriginalPath))) {
154119
projectItem.SetAttributeValue("ReplaceParameters", "true");
155120
}
156121
}
157122
}
158123

159124
template.Save(this.DestinationTemplates[iTemplate].ItemSpec);
160-
foreach (var pair in sourceToDestinationProjectItemMap) {
161-
TaskItem item = new TaskItem(Path.Combine(Path.GetDirectoryName(this.SourceTemplates[iTemplate].ItemSpec), pair.Key));
162-
item.SetMetadata("SourceFullPath", Path.GetFullPath(Path.Combine(Path.GetDirectoryName(this.SourceTemplates[iTemplate].ItemSpec), pair.Key)));
163-
item.SetMetadata("DestinationFullPath", Path.GetFullPath(Path.Combine(Path.GetDirectoryName(this.DestinationTemplates[iTemplate].ItemSpec), pair.Value)));
125+
foreach (var pair in root.LeafDescendents) {
126+
TaskItem item = new TaskItem(Path.Combine(Path.GetDirectoryName(this.SourceTemplates[iTemplate].ItemSpec), pair.OriginalPath));
127+
string apparentSource = Path.Combine(Path.GetDirectoryName(this.SourceTemplates[iTemplate].ItemSpec), pair.OriginalPath);
128+
var sourcePathException = this.SourcePathExceptions.FirstOrDefault(ex => string.Equals(ex.ItemSpec, apparentSource));
129+
if (sourcePathException != null) {
130+
item.SetMetadata("SourceFullPath", sourcePathException.GetMetadata("ActualSource"));
131+
} else {
132+
item.SetMetadata("SourceFullPath", Path.GetFullPath(apparentSource));
133+
}
134+
item.SetMetadata("DestinationFullPath", Path.GetFullPath(Path.Combine(Path.GetDirectoryName(this.DestinationTemplates[iTemplate].ItemSpec), pair.CurrentPath)));
164135
item.SetMetadata("RecursiveDir", Path.GetDirectoryName(this.SourceTemplates[iTemplate].ItemSpec));
136+
item.SetMetadata("Transform", this.ReplaceParametersExtensions.Contains(Path.GetExtension(pair.OriginalName)) ? "true" : "false");
165137
projectItemsToCopy.Add(item);
166138
}
167139
}
@@ -171,27 +143,6 @@ orderby item.Include
171143
return !Log.HasLoggedErrors;
172144
}
173145

174-
private static string CreateUniqueShortFileName(string fileName, int targetLength) {
175-
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(fileName));
176-
Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));
177-
178-
// The filename may already full within the target length.
179-
if (fileName.Length <= targetLength) {
180-
return fileName;
181-
}
182-
183-
string hashSuffix = Utilities.SuppressCharacters(Math.Abs(fileName.GetHashCode() % 0xfff).ToString("x"), Path.GetInvalidFileNameChars(), '_');
184-
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
185-
string extension = Path.GetExtension(fileName);
186-
string hashSuffixWithExtension = hashSuffix + extension;
187-
188-
// If the target length is itself shorter than the hash code, then we won't meet our goal,
189-
// but at least put the hash in there so it's unique, and we'll return a string that's too long.
190-
string shortenedFileName = fileName.Substring(0, Math.Max(0, targetLength - hashSuffixWithExtension.Length)) + hashSuffixWithExtension;
191-
192-
return shortenedFileName;
193-
}
194-
195146
private static XElement FindOrCreateParent(string directoryName, XElement projectElement) {
196147
Contract.Requires<ArgumentNullException>(projectElement != null);
197148

0 commit comments

Comments
 (0)