我找到了解决这个问题的方法。我认为没有人遇到过这种情况,所以我怀疑是否会有“正确”的解决方案。我将在这里发布我所做的以允许我的 .wsp 文件在解决方案中构建。
如果您认为有更好的解决方案或者我解决问题的方式不符合标准,请务必发布答案(或对此答案或原始问题发表评论)。
我将按照我想出的解决问题的步骤来解释这一点。
第一步PackageFiles
任务给了我这个问题。此任务找不到要调用的方法。查看文件C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\SharePointTools\Microsoft.VisualStudio.SharePoint.targets我们可以在第 56 行找到:
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="PackageFiles" />
我知道在哪里可以找到PackageFiles任务/类。
第二步
知道在哪里看后,我反编译了任务。我使用了 Telerik 的 JustDecompile,但我也在 Reflector 中提出了相同的代码。
我可以清楚地看到这条线:
if (PathUtils.HasIllegalDeploymentPathCharacters(str2))
这是错误的。
第三步
我最终决定PathUtils.HasIllegalDeploymentPathCharacters方法只是作为安全检查。我可以在自己的自定义库中重新创建此任务,然后将其插入自定义目标文件中。
这是我想出的课程:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.SharePoint.Tasks;
using Microsoft.Build.Framework;
namespace SharepointTaskLibrary
{
public class PackageFiles : BuildTask
{
[Required]
public ITaskItem LayoutPath
{
get;
set;
}
[Required]
public ITaskItem PackagePath
{
get;
set;
}
public PackageFiles()
{
}
protected override void OnCheckParameters()
{
if (this.LayoutPath == null)
{
throw new InvalidOperationException(Strings.GetString("LayoutPathNotSpecified"));
}
if (this.PackagePath == null)
{
throw new InvalidOperationException(Strings.GetString("PackagePathNotSpecified"));
}
}
protected override void OnExecute()
{
object[] objArray;
object[] objArray2;
object[] objArray3;
string metadata = this.LayoutPath.GetMetadata("FullPath");
string str1 = this.PackagePath.GetMetadata("FullPath");
Assembly sharepointTasksAss = Assembly.Load("Microsoft.VisualStudio.SharePoint.Tasks");
if (sharepointTasksAss != null)
base.Log.LogMessage(MessageImportance.High, "Found Tasks assembly!");
else
{
base.Log.LogError("Couldn't find the tasks assembly");
return;
}
if (!Directory.Exists(metadata))
{
base.Log.LogErrorFromResources("LayoutPathDoesNotExist", new object[] { metadata });
}
else
{
MethodInfo createCabMethod = GetStaticMethod(sharepointTasksAss, "Microsoft.VisualStudio.SharePoint.Tasks.Utilities.CabCreator", "CreateCabinet");
if (createCabMethod == null)
{
base.Log.LogError("the method could not be retrieved on type.");
return;
}
else
base.Log.LogMessage(MessageImportance.High, "Found method: " + createCabMethod.Name);
IEnumerable<string> strs = createCabMethod.Invoke(null, new object[] { metadata, str1 }) as IEnumerable<string>;
/*
* The following code would error in the original task.
*/
//foreach (string str2 in strs)
//{
// if (PathUtils.HasIllegalDeploymentPathCharacters(str2))
// {
// base.Log.LogWarningFromResources("FileNameContainsIllegalDeploymentPathCharacters", new object[] { str2 });
// }
//}
base.Log.LogMessage(MessageImportance.High, Strings.GetString("PackageCreatedSuccessfully"), new object[] { str1 });
}
Type codeMarkersType = null;
try
{
codeMarkersType = sharepointTasksAss.GetType("Microsoft.Internal.Performance.CodeMarkers", true);
}
catch (Exception e)
{
base.Log.LogErrorFromException(e, true);
}
if (codeMarkersType == null)
{
base.Log.LogError("Couldn't get the CodeMarkers class!");
return;
}
else
base.Log.LogMessage(MessageImportance.High, "Found the type: " + codeMarkersType.FullName);
/*
* This has yet to be added back in.
*/
//CodeMarkers.Instance.CodeMarker(CodeMarkerEvent.perfSharePointPackageWspPackageEnd);
}
private MethodInfo GetStaticMethod(Assembly assembly, string typeName, string methodName)
{
Type type = null;
try
{
type = assembly.GetType(typeName, true);
}
catch (Exception e)
{
base.Log.LogErrorFromException(e, true);
}
if (type == null)
{
base.Log.LogError("Couldn't get the type: " + typeName);
return null;
}
else
base.Log.LogMessage(MessageImportance.High, "Found the type: " + type.FullName);
MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.Static);
if (methodInfo == null)
{
MethodInfo[] methods = type.GetMethods().Union(type.GetMethods(BindingFlags.Static)).ToArray();
base.Log.LogWarning(string.Format("Wasn't able to find {0} directly. Searching through the static {1} method(s) on {2}", methodName, methods.Length, type.FullName));
foreach (MethodInfo info in methods)
{
if (info.Name == methodName && methodInfo == null)
methodInfo = info;
}
if (methodInfo == null)
{
MemberInfo[] members =
type.GetMembers().Union(type.GetMembers(BindingFlags.Static | BindingFlags.NonPublic)).Union(type.GetMembers(BindingFlags.NonPublic)).ToArray();
base.Log.LogWarning(string.Format("Wasn't able to find {0}. Searching through the {1} members(s) on {2}", methodName, methods.Length, type.FullName));
MemberInfo createCabMember = null;
foreach (MemberInfo member in members)
{
if (member.Name == methodName)
{
createCabMember = member;
break;
}
else
base.Log.LogMessage(MessageImportance.High, "Found member: " + member.Name);
}
if (createCabMember == null)
base.Log.LogError("Still wasn't able to find " + methodName + " in the members!");
}
}
return methodInfo;
}
}
}
由于大多数类和方法都被标记为内部的,我不得不使用反射来获取实际构建 cab/wsp 文件所需的类型和方法。这是在方法中完成的:GetStaticMethod
第四步
如果您阅读反编译代码和我的自定义版本的类,您会注意到Strings类。它似乎是一个资源访问器类。我决定我也只是反编译该代码并将其用于我的解决方案中,以生成自定义任务,而不是每次我想访问字符串资源时都进行反映。该文件最终不是直接反编译,因为它有一行this.GetType().Assembly用于获取包含资源的当前程序集。这在原始程序集中可以正常工作,但在此自定义程序集中会导致问题。
原行:
internal Strings()
{
this.resources = new ResourceManager("Strings", this.GetType().Assembly);
}
该行必须更改为:
Assembly sharepointTasksAss = Assembly.Load("Microsoft.VisualStudio.SharePoint.Tasks");
this.resources = new ResourceManager("Strings", sharepointTasksAss);
第五步
在我有一个模仿原始的自定义构建任务之后,我现在需要将它放入目标文件中。然后,我备份了原始目标文件并制作了一个自定义文件来替换UsingTask部分,如下所示:
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="CreateSharePointProjectService" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="EnumerateFiles" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="EnumerateFeature" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="EnumeratePackage" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="EnumerateProjectItem" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="LayoutFiles" />
<!-- The next task is a mimic of the one from the other assembly. I decompiled it and recreated it so it wouldn't error. LOL -->
<UsingTask AssemblyFile="C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\SharePointTools\SharepointTaskLibrary.dll" TaskName="PackageFiles" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="ResolveProjectMember" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="SetPackagingProperties" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="ValidatePackage" />
这使得任务指向包含自定义任务的我的 DLL。具体来说,这一行:
<UsingTask AssemblyFile="C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\SharePointTools\SharepointTaskLibrary.dll" TaskName="PackageFiles" />
最后
,我将编译的 DLL 和编辑的目标文件放入C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\SharePointTools目录(再次备份原始目标文件)。
这使我能够通过 TFS 2010 构建自定义输出由 SharePoint 解决方案生成的 wsp 文件!
我将此站点用作资源:http:
//blogs.like10.com/2011/08/04/team-build-2010-customized-output-directories-sharepoint-2010-wsps/
(我可能使用了另外一两个站点作为资源,但我现在可以在浏览器历史记录中找到它们)。
您的里程可能会有所不同,但请让我知道是否有人有类似的问题并且能够以非“黑客”的方式修复它。
更新
这整个问题似乎来自我正在管理的原始 TFS 安装。我最近将我们的团队搬到了一个合适的 TFS 服务器(2012),安装了全新的操作系统和一个新的数据库服务器。一旦我迁移数据库并在 TFS 中运行升级任务,我就能够进行一些小的构建编辑以使我的构建与 2012 一起工作,并且我没有第二次遇到这个问题。我相信,因为最初的 2010 TFS 是在经过转换的开发机器上,所以会导致这个问题。