Можете ли вы использовать несколько рабочих папок с TFS?

В проектах, в которых рабочая область имеет только одну рабочую папку, мои сценарии сборки работают отлично. Теперь, когда я работаю с новым проектом, для которого требуются 2 рабочие папки, все команды извлечения и регистрации из моего предыдущего сценария не выполняются, файлы не найдены.

Очевидно, я не понимаю критическую часть реализации рабочего пространства здесь... У меня есть проект, который зависит от других проектов, вторая рабочая папка - это, в основном, сторонняя папка со ссылками на различные опубликованные библиотеки DLL и заголовки. файлы, необходимые для компиляции моего проекта. Есть 2 активные папки и локальные папки:

$(SourceDir)\TEAM-MAIN\ Финализатор адресов
$(SourceDir)\TEAM-MAIN\ Проект HH-CAHPS \MAINLINE\ Третья сторона

Встроенный код работает нормально, но пользовательский AfterGet завершается ошибкой для следующей записи:

<!-- Check out all of the assemblyInfo files -->
<Exec Command="$(TfCommand) checkout AssemblyInfo.cs /recursive"
      WorkingDirectory="$(MSBuildProjectDirectory)\..\sources"
      ContinueOnError="false"/>

Проект, конечно, будет работать, если у меня будет одна рабочая папка, и я переместлю исходный код на достаточно высокую точку, чтобы получить все необходимые файлы, но я не хочу перебирать 43 других проекта, чтобы делать то, что я хочу, не говоря уже о гадании с их файлы сборки...

Я также попробовал:

<!-- Check out all of the assemblyInfo files -->
<Exec Command="$(TfCommand) checkout AssemblyInfo.cs /recursive"
      WorkingDirectory="$(SolutionRoot)"
      ContinueOnError="false"/>

Та же проблема, не удается найти файлы сборки... Я проверил журнал сборки, и я определенно вижу, как файлы сборки извлекаются на этапе сборки...

Задача "Получить"
  Get TeamFoundationServerUrl="http://pgpd-team01:8080/" BuildUri="vstfs:///Build/Build/1430" Force=True Overwrite=False PopulateOutput=False Preview=False Recursive=True Version="C7564" Рабочая область ="SBN01P-TFS03_61"
<Вырезано>
  Получение C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\ Финализатор адресов \ Источники \ Финализатор адресов \ Финализатор адресов \ Свойства \AssemblyInfo.cs;C7525.

Если у кого-нибудь есть какие-либо идеи или вы можете указать мне статью, чтобы лучше объяснить, как работают разные рабочие папки, я был бы признателен за это.

Значения для некоторых переменных сборки:

MSBuildProjectDirectory: C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\ Address Finalizer \ BuildType

Корень решения: C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\ Финализатор адресов \ Источники

Чтобы предоставить больше информации, я добавил следующую команду:

    
    

Результат был:

Задача "Exec"
  Команда:
  Рабочий стол "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\..\tf.exe"
  ================================================== =============================
  Рабочая область: SBN01P-TFS03_61 (tfsservice)
  Сервер: http://pgpd-team01:8080/
   $/InfoTurn/TEAM-MAIN/ Финализатор адресов: C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\ Финализатор адресов \ Источники \ TEAM-MAIN \ Финализатор адресов
   $ / InfoTurn / TEAM-MAIN / Проект HH-CAHPS /MAINLINE/ Сторонние разработчики: C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\ Финализатор адресов \ Источники \ TEAM-MAIN \ Проект HH-CAHPS \MAINLINE\3rd Вечеринка

Я обнаружил, что следующий рабочий каталог будет работать:

WorkingDirectory = "$ (SolutionRoot) \ TEAM-MAIN \ Финализатор адресов"

Но что следующие два не делают, обратите внимание, что 2nd - это моя вторая рабочая папка:

WorkingDirectory = "$ (SolutionRoot)"
WorkingDirectory = "$ (SolutionRoot) \ TEAM-MAIN \ HH-CAHPS Project \MAINLINE\ Third Party"

Ошибка, которую я получаю для задания метки, является наиболее полезной:

Использование задачи "Метка" из сборки "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.Build.Tasks.VersionControl.dll".
Задача "Ярлык"
  Метка TeamFoundationServerUrl="http://pgpd-team01:8080/" BuildUri="vstfs:///Build/Build/1507" Name="Address Finalizer 2.0.1 Build 039" Recursive=True Comments="Автоматическая сборка: Адрес Finalizer 2.0.1 Build 039" Version="W" Child="replace" Files="C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources"
C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\BuildType\TFSBuild.proj(310,5,310,5): ошибка: ошибка: невозможно определить рабочее пространство.

Фактическая ошибка из проверки, которая не является полезной, является:

Задача "Exec"
  Команда:
  Извлечь сборку "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\..\tf.exe" AssemblyInfo.cs /recursive
  Не найдено подходящих элементов в C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\Sources\AssemblyInfo.cs в вашей рабочей области.
C:\Users\tfsservice\AppData\Local\Temp\InfoTurn\Address Finalizer\BuildType\TFSBuild.proj(280,5): ошибка MSB3073: команда ""C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\..\tf.exe" checkout AssemblyInfo.cs /recursive" завершен с кодом 1.

2 ответа

Решение

Можно использовать непересекающиеся отображения рабочей области + рекурсию одновременно. Вы просто должны быть осторожны.

Это получает все файлы C# в Foo & Bar:

$/project/dir1/foo -> c:\code\foo
$/project/dir2/bar -> c:\code\bar

tf get c:\code\*.cs -recursive

Обычно это получает все в Foo & Bar, но завершится ошибкой, если рабочий каталог не может быть однозначно преобразован в рабочее пространство:

$/project/foo -> c:\code1\foo
$/project/bar -> c:\code2\bar

tf get $/project/* -recursive

Это может сработать (то же самое, что и выше), но, вероятно, не то, что вы ожидаете!

$/project/foo -> c:\code1\foo
$/project/bar -> c:\code2\bar

tf get c:\code* -recursive

Есть больше вариаций; Поведение зависит от того, как определено ваше рабочее пространство, а также от наличия других рабочих пространств на том же компьютере с аналогичными локальными путями. Можете ли вы точно изложить свое определение сборки? И можете ли вы добавить несколько операторов отладки, чтобы точно определить, чему равны $(SolutionRoot) и другие свойства MSBuild при выполнении вашей цели?

/ РЕДАКТИРОВАТЬ /

Как я уже сказал, дизъюнктные отображения + рекурсия очень сложно понять правильно. Оказывается, что поведение зависит не только от тонких деталей вашей файловой спецификации от определения рабочего пространства, но и от команд! В отличие от моего первого примера, это терпит неудачу:

$/project/dir1/foo -> c:\code\foo
$/project/dir2/bar -> c:\code\bar

tf checkout c:\code\*.cs -recursive

Смущены еще? Я работаю в команде TFS несколько лет!

Позвольте мне обобщить мои рекомендации из различных тем комментариев:

  • Будьте супер осторожны. Прежде чем что-то вводить в скрипты msbuild, войдите на сборочную машину и протестируйте ее! При интерактивном тестировании вы получите более приятные сообщения об ошибках и гораздо более быстрые отзывы. Что важно, потому что то, что вы пытаетесь сделать, довольно хрупко. Принимая ваши сценарии за чистую монету, вот несколько исправлений, с которых можно начать:
    • Задача Checkout должна использовать путь к серверу для достижения наилучших результатов. После исправления вы можете запустить его из $(SolutionRoot)\TEAM-MAIN\Address Finalizer или из $(SolutionRoot)\TEAM-MAIN\HH-CAHPS Project\MAINLINE\ Third Party, без разницы. [не просто старый $(SolutionRoot), к сожалению - как показано выше, Checkout не такой умный, как Get] Причина, по которой он работал в Address Finalizer при использовании локальной спецификации элементов, заключается в том, что у вас есть несколько подходящих файлов в этом локальном каталоге., Предположительно, у вас нет файлов AssemblyInfo.cs в третьей стороне. В отличие от этого, использование пути к серверу, такого как $/InfoTurn/TEAM-MAIN/AssemblyInfo.cs, расширит область видимости, так что TFS выполнит поиск по всему TEAM-MAIN соответствующих файлов для извлечения. Вам все еще нужно запустить его из рабочего каталога, который однозначно определяет рабочее пространство - или указать оба параметра /server и /workspace - чтобы TFS могла ограничить запрос элементами, фактически находящимися в рабочем пространстве, и знать, кому их проверять,
    • Точно так же для написанной вами задачи Label необходимо использовать путь к серверу + запуск из папки с прямым отображением. Или вы можете использовать полную спецификацию версии (Wwsname; домен \wsowner) вместо сокращения (W).
  • А еще лучше, во-первых, не использовать непересекающееся рабочее пространство. Посмотрите на все отображаемые локальные пути: их общий предок сам должен быть сопоставленной папкой. В противном случае (а) команды, запускаемые оттуда, будут неоднозначными, требующими дополнительных параметров и / или вообще не работать (б) команды, запускаемые из индивидуально сопоставленных подпапок, не будут запрашивать всю рабочую область [если вы не используете пути к серверу]. Чтобы избежать всего этого, выберите одно отображение как "корень", а затем убедитесь, что все последующие отображения указывают где-то под ним. После того, как вы установили единый унифицированный корень, все станет намного проще. Команды tf, запускаемые из любой точки, будут работать более предсказуемо (как новичкам, так и экспертам!). Есть несколько способов настроить единое рабочее пространство, которое соответствует вашим целям:

    • Вернитесь к одному большому сопоставлению, а затем закройте папки, которые вам не нужны. Обратите внимание, что вы можете создавать позитивные отображения под плащами (например, проект плаща $ / InfoTurn / TEAM-MAIN / HH-CAHPS, в то же время отображая $/InfoTurn/TEAM-MAIN/HH-CAHPS Project/MAINLINE/ 3rd Party). Это оставляет вас с хорошим чистым рабочим пространством и без лишних побочных эффектов. Недостатком является объем работы. Если существует множество папок, вы можете использовать сценарий для создания плащей, но даже при этом возникает проблема их обновления по мере добавления новых папок или переименования существующих.
    • Найдите папку, которая является родительской для ваших текущих сопоставлений, и создайте для нее одноуровневое сопоставление. Например, вы можете создать одноуровневое отображение $ / InfoTurn / TEAM-MAIN / * -> $(SolutionRoot) \ TEAM-MAIN. Это приведет к удалению всех непосредственных потомков TEAM-MAIN, но не углубит их, за исключением двух папок, которые вы уже добавили с полной рекурсией. Недостатком является наличие нескольких дополнительных файлов для загрузки и маркировки.
    • Измените ваши текущие сопоставления так, чтобы одна папка отображалась под другой. Например, вы можете отобразить $ / InfoTurn / TEAM-MAIN / Финализатор адресов -> $(SolutionRoot) и $/InfoTurn/TEAM-MAIN/HH-CAHPS Project/MAINLINE/ Third Party -> $(SolutionRoot) \ Third Party. Теперь $(SolutionRoot) - это единый корень для всего кода, который вам нужен. Проблема с этим решением заключается в том, что он может сломать любые make-файлы, которые полагаются на относительные пути, оставаясь неизменными между вашей конфигурацией сборки и другими, которые также пытаются построить Address Finalizer без использования нестандартных отображений.
    • Переместите вашу стороннюю папку в более естественное место в структуре управления источниками. В конце концов, вы, вероятно, не единственный человек, работающий в TEAM-MAIN, которому необходимо ссылаться на сторонние компоненты. Если вы все согласились хранить общие ресурсы в корне и соответствующим образом создавать свои make-файлы, то большинство проблем, связанных с отображением глубоких путей в рабочее пространство, исчезнет.
  • Более того, не извлекайте и не проверяйте файлы во время процесса сборки. Шутки в сторону. Обычные ситуации, с которыми людям легко справиться (например, блокировки, конфликты версий), являются кошмаром для автоматизации, учитывая все возможные крайние случаи. И да поможет вам Бог, если вы когда-нибудь попробуете использовать непрерывную интеграцию... Между тем все ваши важные сборки ломаются, пока вы отлаживаете функциональность, для которой я действительно не вижу никакой выгоды. Вместо:

    • Консолидируйте всю системную информацию о сборке (включая нумерацию сборки) в один файл AssemblyInfo.cs.
    • Ссылайтесь на этот файл C# из общего файла MSBuild *.targets, который импортирует каждый проект. (Хорошая возможность для рефакторинга задач на уровне проекта, если вы еще этого не сделали).
    • Попросите Team Build изменить этот файл C# по мере необходимости. Например, вы можете ввести номер сборки в любое время до выполнения задачи CoreCompile. Обратите внимание, что мы просто перезаписываем данные на диске, а не касаемся контроля версий.
    • Если вы хотите, чтобы сборки вашего рабочего стола также имели инкрементную нумерацию (лично я не вижу в этом смысла), добавьте аналогичную задачу в ваш общий файл *.targets с условием, исключающим его из Team Builds.

Мой скрипт сейчас работает, было несколько небольших проблем, которые вызывали проблему. Одна из них заключалась в том, что для многих команд необходимо указывать точный рабочий каталог (локальную папку), чтобы команды работали. Другая проблема заключалась в том, что настройки свойств необходимо было перенести в пользовательскую задачу, чтобы они могли правильно использоваться еще одной задачей. Так что, надеюсь, мой полный сценарий сборки, приведенный ниже, поможет кому-то еще. Вставка этого в любом редакторе облегчит чтение.

     $(SolutionRoot)\TEAM-MAIN\ Финализатор адресов \ Финализатор адресов \versionNumber.txt   "$(TeamBuildRefPath)\..\tf.exe"   $(SolutionRoot)\TEAM-MAIN\ Финализатор адресов   $(SolutionRoot)\TEAM-MAIN\HH-CAHPS Проект \MAINLINE\ Third Party   Address_Finalizer.dll  <Опубликованная папка> $(SolutionRoot)\TEAM-MAIN\HH-CAHPS Проект \MAINLINE\ Сторонняя организация \bin\PG File Import   unknown      <Имя цели =" AfterGet "> <Текст сообщения =" Файл версии FOOBAR: $ (VersioningFile) "/>                $(DropLocation) \ $ (BuildNumber) \ Release \ $(DllName)  <Опубликовано DLL> $ (Опубликованный Folder) \ $ (Имя DllName)                       $ (Revision)   0 $ (Revision)  < PaddedRevision Condition = "$ (Revision) <10"> 00$(Revision)          <Текст сообщения =" Новая версия: $ (ProjectVersion) "/>          <Текст сообщения = "FOOBAR Solution Root: $ (SolutionRoot)" />                   <Текст сообщения = "Версия FOOBAR: $ (ProjectVersion)" /> <Текст сообщения = "Комментарий FOOBAR: $ (VersionComment)" /> <Текст сообщения = "Путь FOOBAR: $ (LabelPath) "/>  
Другие вопросы по тегам