Решение TFS 2010 Building Sharepoint 2010 с настраиваемыми выводами

У меня очень похожий вопрос на это сообщение SO: TFS Build 2010 - Пользовательское двоичное расположение и 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. Нить)'.

Я просмотрел эту строку (411) в файле целей:

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

Целевой объект PackageFiles определен:

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

Я проверил GAC и не увидел его там, поэтому добавил. На машине сборки TFS 2010 установлены 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. Однако я могу найти его только в 64-разрядной версии командной строки Visual Studio. Когда запускаю нормальную, то сборки обратно не получаю. Я предполагаю, что это потому, что сборка Sharepoint 64-битная. Насколько я знаю, TFS настроен на 64-битный. Это будет моей проблемой?


person Mike G    schedule 25.05.2012    source источник


Ответы (3)


Метод PathUtils.HasIllegalDeploymentPathCharacters присутствует в версии 10.0.40219.1 Microsoft.VisualStudio.SharePoint.Designers.Models.dll, а не в версии 10.0.30319.1 (где я видел эту ошибку).

person Rob L    schedule 14.01.2013
comment
Недавно я перенес наш экземпляр TFS 2010 с одного компьютера на экземпляр TFS 2012 на другом компьютере и больше не видел, чтобы эта проблема возникала. Я считаю, что причина, по которой это произошло, заключается в том, что ящик TFS, из которого возникла эта проблема, был псевдо-блоком разработчика, преобразованным в хост TFS. Теперь, когда у меня есть правильный сервер TFS, проблем нет. - person Mike G; 16.01.2013
comment
Это было точным решением и причиной моей ошибки. В моем случае на TFS 2015 миграция старых версий рабочих процессов. - person Brandon Harris; 05.12.2017

Вам не хватает сборки "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

С уважением,

Уэс Макдональд

person Wes MacDonald    schedule 30.05.2012
comment
Привет, Уэс, я собирался ответить на твое письмо, когда увидел, что у меня есть последние новости о SO! Я использовал ту ссылку, которую вы разместили, в моем первом раунде попыток исправления. В нем упоминается установка Sharepoint в системе сборки или запуск сценария PowerShell на машине разработчика, чтобы получить необходимые файлы для добавления их в GAC машины сборки. У нас уже был Sharepoint (и Sharepoint SDK), установленный на сервере сборки. Я также проверил, что все эти библиотеки DLL находятся в GAC. Вот почему эта проблема меня так расстроила! - person Mike G; 31.05.2012

Я нашел решение этой проблемы. Я не думаю, что кто-либо когда-либо сталкивался с этим, поэтому я сомневаюсь, что будет «правильное» решение. Я опубликую здесь то, что я сделал, чтобы мои файлы .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.

Шаг второй. Зная, где искать, я декомпилировал задачу. Я использовал JustDecompile Telerik, но я также придумал тот же код в 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 с настраиваемыми выводами файлы wsp, созданные решениями SharePoint!

Я использовал этот сайт как ресурс: http://blogs.like10.com/2011/08/04/team-build-2010-customized-output-directories-sharepoint-2010-wsps/

(Возможно, я использовал еще один или два сайта в качестве ресурса, но на данный момент я могу найти их в истории браузера).

Ваш пробег может отличаться, но, пожалуйста, дайте мне знать, есть ли у кого-нибудь подобная проблема и сможет ли ее исправить "взломанным" способом.

ОБНОВЛЕНИЕ
Похоже, вся эта проблема возникла из-за исходной установки TFS, которую я администрировал. Недавно я перевел нашу команду на правильный сервер TFS (2012 г.) с полностью новой установкой ОС и новым сервером базы данных. После того, как я перенес базы данных и выполнил задачи обновления в TFS, я смог внести некоторые небольшие изменения в сборку, чтобы моя сборка работала с 2012 годом, и я не сталкивался с этой проблемой во второй раз. Я считаю, что эта проблема возникла из-за того, что исходная TFS 2010 была на переделанной машине dev.

person Mike G    schedule 29.05.2012