¿Cómo puedo conseguir que TFS 2010 construya cada proyecto en un directorio separado?


En nuestro proyecto, nos gustaría que nuestra compilación TFS pusiera cada proyecto en su propia carpeta debajo de la carpeta desplegable, en lugar de colocar todos los archivos en una estructura plana. Para ilustrar, nos gustaría ver algo como esto:

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

Esta es básicamente la misma pregunta que se hizo aquí, pero ahora que estamos usando compilaciones basadas en flujo de trabajo, esas soluciones no parecen funcionar. La solución que usa la propiedad CustomizableOutDir parecía que funcionaría mejor para nosotros, pero no puedo conseguir que esa propiedad sea reconocida. Personalizé nuestro flujo de trabajo para pasarlo a MSBuild como un argumento de línea de comandos (/p:CustomizableOutDir=true), pero parece que MSBuild simplemente lo ignora y coloca la salida en el OutDir dado por el flujo de trabajo.

Miré los registros de compilación, y puedo ver que las propiedades CustomizableOutDir y OutDir se están estableciendo en la línea de comandos args a MSBuild. Todavía necesito que se pase OutDir para poder copiar mis archivos a TeamBuildOutDir en el final.

¿Alguna idea de por qué mi parámetro CustomizableOutDir no está siendo reconocido, o si hay una mejor manera de lograr esto?

Author: Community, 2010-05-07

9 answers

He descubierto una buena manera de hacerlo. Resulta que dado que puede establecer el OutDir a lo que desee dentro del flujo de trabajo, si lo establece en la cadena vacía, MSBuild usará en su lugar la ruta de salida específica del proyecto. Eso nos permite ser mucho más flexibles. Aquí está mi solución completa (basada en el flujo de trabajo de compilación predeterminado):

En la tarea Ejecutar MSBuild, establezca OutDir en la cadena vacía. En esa misma tarea, establezca sus CommandLineArguments en algo como lo siguiente. Esto le permitirá tenga una referencia al OutDir predeterminado de TFS de su proyecto:

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

En cada proyecto que desee copiar a la carpeta desplegable, establezca la ruta de salida de la siguiente manera:

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

Compruebe todo, y debería tener una compilación en funcionamiento que implemente cada uno de sus proyectos en su propia carpeta debajo de la carpeta desplegable.

 14
Author: Jonathan Schuster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-06 22:35:18

También resolví este problema, y creo que es más limpio que las soluciones existentes en este hilo.

  • Antes de la actividad Run MSBuild for Project, agregué una actividad Assign: projectName = Regex.Replace(New FileInfo(localProject).Name, "\.sln$", "").
  • Luego agregué una actividad Create Directory: outputDirectory + "\" + projectName
  • Finalmente en la actividad de MSBuild cambié OutDir a outputDirectory + "\" + projectName.

La plantilla ya rellena localProject con el nombre de ruta completo del archivo .sln que se está construyendo en el Agente, por ejemplo, c:\build\path\to\MySolution.sln. La actividad de asignación corta la ruta y extensión, colocando la salida en MySolution. Necesitarás crear la variable projectName e importar System.Text.RegularExpressions y System.IO.

La ventaja sobre la solución de OP es que no tiene que editar cada .csproj, esa información se infiere del nombre del archivo de la solución.

 8
Author: Travis Pettijohn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-22 23:56:14

Tuvimos que hacer esto para evitar el problema donde tenemos una biblioteca Silverlight y.Net con el mismo nombre para la serialización CSLA. La biblioteca sería sobrescrita y nuestras pruebas fallarían.

Usé la respuesta de Jonathan y el post de Jim Lamb, pero descubrí que también necesitas establecer OutDir como vacío.

Por lo tanto, debe hacer estos parámetros para las actividades de MSBuild (si utiliza la siguiente macro, también debe establecer los parámetros de actividad para Clean, de lo contrario obtendrá advertencias que OutputPath no está establecido):

  • Establezca CommandLineArguments en String.Format("/p:SkipInvalidConfigurations=true;TeamBuildOutDir=""{0}"" {1}", BinariesDirectory, MSBuildArguments)
  • Establece OutDir a empty (was BinariesDirectory)

También he creado una macro que puede ejecutar en visual studio que quita el OutputPath de las configuraciones, y añade un PropertyGroup que contiene el OutputPath para todas las configs así :

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

Aquí está la Macro:

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

Espero que esto ayude!!!

 4
Author: Christian Lavallee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-29 19:32:54

Los pasos aquí no requieren una modificación del archivo de proyecto - http://lajak.wordpress.com/2011/05/07/customize-binaries-folder-in-tfs-team-build /

 2
Author: ak7,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-30 17:41:35

Aquí hay una solución muy simple que no requiere ninguna modificación en los archivos de origen o archivos de proyecto. Al configurar su definición de compilación Proceso -> Proyectos a construir, en lugar de especificar su .archivo sln en los proyectos a construir, agregue cada proyecto (.csproj or .vbproj). Ahora, dentro del paso "Ejecutar MSBuild para Project" de su flujo de trabajo, cambie la propiedad OutDir a lo siguiente:

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

Esto colocará la salida de compilación de cada proyecto en un subdirectorio con el nombre del proyecto.

 1
Author: Andy Morris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-31 20:30:57

He instalado el paquete PublishedApplications de Nuget para cada Ejecutable en Mi Solución y creó subcarpetas en la carpeta _PublishedApplications para cada proyecto durante la compilación.

 1
Author: Michael Freidgeim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-11 11:25:52

No estoy seguro de si todavía puede conseguir que team build 2010 use la última versión de msbuild, pero hay una nueva propiedad /p:GenerateProjectSpecificOutputFolder=true cuando se especifique, soltará los bits en $(OutDir) \Name(ProjectName)\para cada proyecto.

 1
Author: Richard Spence,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-07 02:41:52

No he jugado con conseguir que TFS/MSBuild ponga los archivos de salida en carpetas separadas, por lo que no puedo dar una respuesta directa. Sin embargo, aquí hay un par de sugerencias que no vi en tu enlace:

  • Puede agregar pasos posteriores a la compilación a los proyectos que copian los archivos requeridos en una estructura de "implementación". (Esto, por supuesto, también se ejecutaría en máquinas de desarrollo, lo que podría ser un dolor). Utilizamos este enfoque para nuestras bibliotecas, que se construyen y luego se copian en una biblioteca compartida carpeta (binarios) para otros proyectos desde los que hacer referencia.

  • Puede agregar un destino de MSBuild para copiar los archivos necesarios donde los desee. Hemos anulado los objetivos predeterminados de "copiar a la carpeta desplegable" para copiar los archivos a otra carpeta, ofuscarlos, firmarlos digitalmente, construirlos en un instalador, firmar digitalmente el instalador y luego copiarlo (y otras cosas útiles como los archivos de mapa de ofuscación) y una lista de cambios desde la última compilación a la carpeta desplegable. En última instancia, agregar su propio objetivo posterior a la compilación le da el máximo control sobre exactamente qué se pone dónde. (En el lado negativo, es posible que tenga que agregar manualmente cualquier dll o EX al destino de copia posterior a la compilación, lo que podría ser una irritación)

 0
Author: Jason Williams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-06 21:14:56

Aquí hay otra solución muy simple que no requiere ninguna modificación a los archivos de origen o archivos de proyecto. Al configurar su definición de compilación Proceso -> Proyectos a construir, en lugar de especificar su .archivo sln en los proyectos a construir, agregue cada proyecto (.csproj or .vbproj). Ahora, dentro del paso "Ejecutar MSBuild para Project" de su flujo de trabajo, cambie la propiedad OutDir a lo siguiente:

OutputDirectory + "\" + System.IO.Path.GetFileNameWithoutExtension(serverBuildProjectItem)
 0
Author: Rajani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-01 16:15:16