Как я могу заставить TFS 2010 строить каждый проект в отдельный каталог?

В нашем проекте мы хотели бы, чтобы наша сборка TFS помещала каждый проект в свою папку под папкой drop, вместо того чтобы отбрасывать все файлы в одну плоскую структуру. Чтобы проиллюстрировать это, мы хотели бы увидеть что-то вроде этого:

DropFolder/
  Foo/
    foo.exe
  Bar/
    bar.dll
  Baz
    baz.dll

Это в основном тот же вопрос, который был задан здесь, но теперь, когда мы используем сборки на основе рабочих процессов, эти решения, похоже, не работают. Решение, использующее свойство CustomizableOutDir, выглядело так, как будто оно будет работать лучше всего для нас, но я не могу распознать это свойство. Я настроил наш рабочий процесс, чтобы передать его в MSBuild в качестве аргумента командной строки (/p: CustomizableOutDir = true), но похоже, что MSBuild просто игнорирует его и помещает вывод в OutDir, заданный рабочим процессом.

Я просмотрел журналы сборки, и я вижу, что свойства CustomizableOutDir и OutDir оба устанавливаются в командной строке args для MSBuild. Мне все еще нужно передать OutDir, чтобы я мог копировать свои файлы в TeamBuildOutDir в конце.

Любая идея, почему мой параметр CustomizableOutDir не распознается, или если есть лучший способ достичь этого?

Ответ 1

Я придумал хороший способ сделать это. Оказывается, так как вы можете установить OutDir в любое рабочее место, если вы установите его в пустую строку, MSBuild вместо этого будет использовать OutputPath для конкретного проекта. Это позволяет нам быть более гибкими. Здесь мое полное решение (основанное на рабочем процессе сборки по умолчанию):

В Запустите задачу MSBuild установите OutDir в пустую строку. В этой же задаче установите для CommandLineArguments следующее: Это позволит вам ссылаться на OutDir по умолчанию для TFS по умолчанию:

String.Format("/p:CommonOutputPath=""{0}\\""", outputDirectory)

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

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <OutputPath Condition=" '$(CommonOutputPath)'=='' ">bin\Release\</OutputPath>
    <OutputPath Condition=" '$(CommonOutputPath)'!='' ">$(CommonOutputPath)YourProjectName\bin\Release\</OutputPath>
</PropertyGroup>

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

Ответ 2

Я тоже решил эту проблему, и я думаю, что она чище, чем существующие решения в этой теме.

  • Перед Run MSBuild for Project активностью я добавил активность Assign: projectName = Regex.Replace(New FileInfo(localProject).Name, "\.sln$", "").
  • Затем я добавил активность Create Directory: outputDirectory + "\" + projectName
  • Наконец, в работе MSBuild я изменил OutDir на outputDirectory + "\" + projectName.

Шаблон уже заполняет localProject полным именем пути файла .sln, созданного на Агенте, например, c:\build\path\to\MySolution.sln. Операция назначения прерывает путь и расширение, помещая вывод в MySolution. Вам нужно будет создать переменную projectName и импортировать System.Text.RegularExpressions и System.IO.

Преимущество над решением OP состоит в том, что вам не нужно редактировать каждый .csproj, эта информация выводится из имени файла решения.

Ответ 3

Мы должны были сделать это, чтобы обойти проблему, когда у нас есть Silverlight и библиотека .Net с тем же именем для сериализации CSLA. Библиотека будет перезаписана, и наши тесты потерпят неудачу.

Я использовал ответ Джонатана и сообщение Джима Лэмба, но я обнаружил, что вам также нужно установить OutDir для пустого.

Итак, вам нужно выполнить эти параметры для действий MSBuild (если вы используете следующий макрос, вам нужно также установить параметры активности для Clean, иначе вы получите предупреждения о том, что OutputPath не установлен):

  • Установить CommandLineArguments для String.Format("/p:SkipInvalidConfigurations=true;TeamBuildOutDir=""{0}"" {1}", BinariesDirectory, MSBuildArguments)
  • Установить OutDir для пустого (был BinariesDirectory)

Я также создал макрос, который можно запустить в visual studio, который удаляет OutputPath из конфигураций, и добавляет PropertyGroup, который содержит OutputPath для всех конфигураций:

<PropertyGroup Label="OutputPathLabel">
  <OutputPath Condition="'$(TeamBuildOutDir)'=='' ">bin\$(Configuration)\</OutputPath>
  <OutputPath Condition="'$(TeamBuildOutDir)'!='' ">$(TeamBuildOutDir)\$(SolutionName)\$(MSBuildProjectName)\$(Configuration)\</OutputPath>
</PropertyGroup>

Здесь макрос:

Public Sub SetTeamBuildOutDir()

    Dim projectObjects = DTE.Solution.Projects

    For Each project In projectObjects

        If project.ProjectItems IsNot Nothing Then
            SetTeamBuildOutDirRecursive(project)
        End If
    Next

End Sub

Sub SetTeamBuildOutDirRecursive(ByVal proj As Project)
    If proj.ConfigurationManager Is Nothing Then
        For Each subProj As ProjectItem In proj.ProjectItems
            If subProj.SubProject IsNot Nothing Then
                SetTeamBuildOutDirRecursive(subProj.SubProject)
            End If
        Next
    Else
        SetTeamBuildOutDir(proj)
    End If

End Sub

Sub SetTeamBuildOutDir(ByVal project As Project)
    'Do not handle .vdproj
    If project.FullName.ToLower().EndsWith(".vdproj") Then
        Exit Sub
    End If

    Dim needToSave = False
    Dim msproject = ProjectRootElement.Open(project.FullName)
    Dim outputPathGroupExists = False
    Dim outputPropertyGroup As ProjectPropertyGroupElement = Nothing
    Dim lastConfigPropertyGroup As ProjectPropertyGroupElement = Nothing

    For Each propertyGroup In msproject.PropertyGroups

        If propertyGroup.Label = "OutputPathLabel" Then
            outputPathGroupExists = True
            outputPropertyGroup = propertyGroup
        End If

        If Not String.IsNullOrEmpty(propertyGroup.Condition) AndAlso _
            propertyGroup.Condition.TrimStart().StartsWith("'$(Configuration)") Then

            lastConfigPropertyGroup = propertyGroup
        End If

        'Remove the OutputPath from the configurations
        Dim outputPathElement As ProjectPropertyElement = Nothing
        For Each element As ProjectPropertyElement In propertyGroup.Children
            If element.Name = "OutputPath" Then
                outputPathElement = element
            End If
        Next
        If outputPathElement IsNot Nothing Then
            propertyGroup.RemoveChild(outputPathElement)
            needToSave = True
        End If
    Next

    'If we want to always remove the group and add it back (in case of modifications to the group)
    'If outputPathGroupExists Then
    '    msproject.RemoveChild(outputPropertyGroup)
    '    outputPathGroupExists = False
    'End If

    If Not outputPathGroupExists Then
        Dim propertyGroup = msproject.CreatePropertyGroupElement()
        propertyGroup.Label = "OutputPathLabel"
        'Need to insert the PropertyGroup before the CSharp targets are included
        msproject.InsertAfterChild(propertyGroup, lastConfigPropertyGroup)

        Dim isDbProject = project.FullName.ToLower().EndsWith(".dbproj")

        Dim outputEmpty = propertyGroup.AddProperty("OutputPath", IIf(Not isDbProject, "bin\$(Configuration)\", "sql\$(Configuration)\"))
        outputEmpty.Condition = "'$(TeamBuildOutDir)'=='' "

        Dim outputTeamBuild = propertyGroup.AddProperty("OutputPath", "$(TeamBuildOutDir)\$(SolutionName)\$(MSBuildProjectName)\$(Configuration)\")
        outputTeamBuild.Condition = "'$(TeamBuildOutDir)'!='' "

        needToSave = True
    End If

    If needToSave Then
        'checkout the project file with tfs
        Shell("C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\tf.exe checkout " & project.FullName, , True)

        'Save the project file
        msproject.Save()
    End If
End Sub

Надеюсь, это поможет!!!

Ответ 5

Здесь очень простое решение, которое не требует изменения исходных файлов или файлов проекта. При настройке процесса сборки → Проекты для сборки вместо указания вашего .sln файла в проектах для сборки добавьте каждый проект (.csproj или .vbproj). Теперь в шаге "Запуск MSBuild для проекта" измените свойство OutDir на следующее:

outputDirectory + "/" + serverBuildProjectItem.Substring(serverBuildProjectItem.LastIndexOf("/"), serverBuildProjectItem.Length - serverBuildProjectItem.LastIndexOf("/")).Replace(".csproj", String.Empty)

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

Ответ 6

Ive установил пакет PublishedApplications из Nuget для каждого исполняемого файла в моем решении и создал подпапки в папке _PublishedApplications для каждого проекта во время сборки.

Ответ 7

Я не играл с получением TFS/MSBuild для размещения выходных файлов в отдельных папках, поэтому не могу дать прямой ответ. Однако, вот несколько предложений, которые я не заметил в вашей ссылке:

  • Вы можете добавить шаги после сборки к проектам, которые копируют необходимые файлы в структуру "развертывания". (Это, конечно, также будет работать на dev машинах, что может быть болью). Мы используем этот подход для наших библиотек, которые создаются, а затем копируются в папку общих библиотек (двоичных файлов) для других проектов, чтобы ссылаться на них.

  • Вы можете добавить цель MSBuild, чтобы скопировать нужные файлы там, где они вам нужны. Мы переопределили цели по умолчанию "копировать в папку", чтобы скопировать файлы в другую папку, обфускать их, подписать их цифровым способом, создать их в установщике, подписать под цифровой подписью установщик и затем скопировать его (и другие полезные материалы, такие как obfuscation map files) и список изменений с момента последней сборки в папке с отбрасыванием. В конечном итоге добавление вашей собственной цели после сборки дает вам максимальный контроль над тем, что получает где-то. (На минусовой стороне вам, возможно, придется вручную добавлять любые новые dll или exes в цель копирования после сборки, что может вызвать раздражение)

Ответ 8

Вот еще одно очень простое решение, которое не требует модификации исходных файлов или файлов проекта. При настройке процесса сборки → Проекты для сборки вместо указания вашего .sln файла в проектах для сборки добавьте каждый проект (.csproj или .vbproj). Теперь в шаге "Запуск MSBuild для проекта" измените свойство OutDir на следующее:

OutputDirectory + "\" + System.IO.Path.GetFileNameWithoutExtension(serverBuildProjectItem)

Ответ 9

Не уверен, что вы все равно можете получить команду build 2010 для использования последней версии msbuild, но там будет добавлено новое свойство /p: GenerateProjectSpecificOutputFolder = true, если указано, будет бить биты в $(OutDir)\$(ProjectName)\for каждый проект.