【问题标题】:TFS 2010 Building Sharepoint 2010 Solution With Custom OutputsTFS 2010 构建具有自定义输出的 Sharepoint 2010 解决方案
【发布时间】:2012-06-01 02:33:34
【问题描述】:

我对这个 SO 帖子有一个非常相似的问题:TFS Build 2010 - Custom Binary Location and SharePoint WSP。没有明显的答案,但提供的唯一答案似乎是要走的路。

我正在构建多个解决方案,并且需要将解决方案和项目放入它们自己的文件夹中。这导致生成输出更改为我正在使用的模板中的 MSBuild 调用。我已经使用了一段时间了,没有任何问题。

最近一位开发人员抱怨我们的日常构建中没有生成 .wsp 文件。我对此进行了调查,并遇到了前面提到的 SO 帖子。

我按照说明进行操作,现在出现了一个新错误:

C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\SharePointTools\Microsoft.VisualStudio.SharePoint.targets (411):找不到方法:'Boolean Microsoft.VisualStudio.SharePoint.PathUtils.HasIllegalDeploymentPathCharacters (System.String)'。

我查看了目标文件中的这一行 (411):

<PackageFiles LayoutPath="$(LayoutPath)%(EnumeratedFiles.Package)\" PackagePath="$(BasePackagePath)%(EnumeratedFiles.Package).$(PackageExtension)" />

PackageFiles 目标已定义:

<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="PackageFiles" />

我检查了 GAC 并没有看到它,所以我添加了它。 TFS 2010 Build 机器上安装了 Visual Studio 2010 和 Sharepoint 2010。除了更改此任务之外,我认为我不需要做任何事情:

<CreateSharePointProjectService Configuration="$(Configuration)"
                                Platform="$(Platform)"
                                ProjectFile="$(MSBuildProjectFile)"
                                ProjectReferences="@(SharePointProjectReference)"
                                OutDir="$(TargetDir)">
  <Output PropertyName="ProjectService" TaskParameter="ProjectService" />
</CreateSharePointProjectService>

这样 OutDir 指向 $(TargetDir)

我是否遗漏了一些关于为什么在现在找不到方法的情况下出现此错误的信息?这个错误非常令人恼火,因为无论使用 Google Fu,网络上都没有任何信息!

更新 我已经拆开了构建服务器上的 Microsoft.VisualStudio.SharePoint.dll。没有 PathUtils 类或命名空间。我可能有这个文件的错误版本吗?我怎样才能检测到这个?我应该在构建服务器上安装 Sharepoint SDK。它已经安装了 Sharepoint 2010。

更新 2
我检查了 GAC。 Microsoft.VisualStudio.Sharepoint 程序集出现。但是,我只能在运行 x64 版本的 Visual Studio 命令提示符时找到它。当我运行正常时,我没有得到任何组装。我假设这是因为 Sharepoint 程序集是 64 位的。据我所知,TFS 设置为 64 位。这会是我的问题吗?

【问题讨论】:

    标签: sharepoint sharepoint-2010 msbuild-4.0 wsp


    【解决方案1】:

    PathUtils.HasIllegalDeploymentPathCharacters 方法存在于 Microsoft.VisualStudio.SharePoint.Designers.Models.dll 版本 10.0.40219.1 中,而不是版本 10.0.30319.1 中(我看到此错误)。

    【讨论】:

    • 我最近将我们的 2010 TFS 实例从一台机器迁移到另一台机器上的 TFS 2012 实例,我没有看到这个问题再次出现。我相信出现这个问题的原因是,这个问题起源的 TFS 盒子是一个转换为主机 TFS 的伪开发盒子。现在我有了一个合适的 TFS 服务器,没有问题。
    • 这是我的错误的确切解决方案和原因。以我的 TFS 2015 迁移旧版本工作流为例。
    【解决方案2】:

    您缺少程序集“Microsoft.VisualStudio.SharePoint.Designers.Models.dll”

    以下程序集必须复制到构建系统的 GAC:

    Microsoft.VisualStudio.SharePoint.Designers.Models.dll Microsoft.VisualStudio.SharePoint.Designers.Models.Features.dll Microsoft.VisualStudio.SharePoint.Designers.Models.Packages.dll Microsoft.VisualStudio.SharePoint.dll

    有关所需程序集的更多信息,请参阅以下文章:

    http://msdn.microsoft.com/en-us/ff622991.aspx

    问候,

    韦斯·麦克唐纳

    【讨论】:

    • Hey Wes,我正要回复你的邮件时看到我有关于 SO 的更新!我使用了您在第一轮修复尝试中发布的链接。它提到在构建系统上安装 Sharepoint 或在开发机器上运行 powershell 脚本以获取必要的文件以将它们添加到构建机器的 GAC。我们已经在构建服务器上安装了 Sharepoint(和 Sharepoint SDK)。我还验证了所有这些 DLL 都在 GAC 中。这就是为什么这个问题让我如此沮丧!
    【解决方案3】:

    我找到了解决此问题的方法。我认为没有人遇到过这种情况,所以我怀疑是否会有“正确”的解决方案。我将在这里发布我所做的以允许我的 .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;
        }
      }
    }
    

    由于大多数类和方法都标记为 internal,我不得不使用反射来获取实际构建 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 是在转换后的 dev 机器上,所以导致了这个问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-13
      • 1970-01-01
      相关资源
      最近更新 更多