Динамически определять все решения, проекты и конфигурации для события сборки / запуска и вызывать их все с помощью MSBuild из командной строки / скрипта - PullRequest
1 голос
/ 22 июня 2009

Мы все еще немного рано настраиваем автоматизацию сборки и пока просто используем bat-файл для решений Win32 C ++. У нас есть около 4 решений, и у каждого есть пара файлов vcproj.

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

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

Есть ли простой способ сделать это?

На самом деле это 2 вопроса:

  1. Как я могу сказать MSBuild просто построить все решения внутри поддерева? (Я могу сделать поиск по ним - это простой инструмент, который я думаю написать)

  2. Как я могу сказать MSBuild построить все конфигурации решения / vcproj?

Мы используем MSBuild, bat-файлы, vc2008, c ++

Ответы [ 2 ]

1 голос
/ 22 июня 2009

Будучи поклонником PowerShell, может помочь следующий переформатированный однострочный:

Get-ChildItem -Recurse -Include *.sln | 
ForEach-Object {  
    $Solution = $_.FullName
    Get-Content $_ | 
    ForEach-Object { 
        if($_ -match '\{[^\}]+[^=]+= ([^\{\s]*)$') { 
            $matches[1] 
        }
    } | Sort-Object -Unique | 
    ForEach-Object { 
        $config =  ([string]$_).Split([char]'|')
        & "$env:windir\Microsoft.NET\Framework\v3.5\msbuild.exe" $Solution /p:Configuration="$($config[0])" /p:Platform="$($config[1])" 
    }
}

Этот скрипт может быть сохранен в виде файла ps1 или вставлен как функция в ваш профиль. Чтобы объяснить, что он делает:

  1. найти все файлы .sln в текущем каталоге и ниже
  2. парсинг sln, извлекающий значения конфигурации и платформы
  3. для каждой уникальной комбинации платформы конфигурации вызовите msbuild с данным решением

- редактировать: Забыли Split-String является частью PSCX , лучше использовать [string] Надеюсь, это поможет. Если вы все еще хотите использовать MsBuild, он поддерживает рекурсивную форму оператора файлового глобуса через синтаксис ". \ ** \ *. Sln", подробности см. здесь . MsBuild также предоставляет задачу MsBuild, которую можно использовать для создания набора решений. Я не знаю, как можно легко получить все «известные» конфигурации из решения в msbuild. Поэтому я выбираю маршрут PowerShell.

0 голосов
/ 22 июня 2009

Доступ к инфраструктуре сборки Microsoft можно получить через API. Но я не уверен, что можно сделать это только через командный файл. ИМХО самый простой способ - это автоматизировать сборку, написав надстройку (возможно, с использованием VB.NET) в VS IDE. Это дополнение может быть вызвано из командного файла. Мы автоматизировали сборки VC ++, как это, используя подход addin. В нашем случае другого пути не было, потому что нам также пришлось автоматизировать процесс moc в Qt. Я думаю, что это будет более гибким.

Система сборки (по крайней мере в VS 2005), над которой я работал, была довольно глючной. Обратитесь к этой ссылке , она поможет вам понять подводные камни, а также предоставит фрагменты кода о том, как пройти файлы решения. После установки надстройки можно создать небольшой исполняемый файл, который вызывает сборку через надстройку. Этот exe-файл можно вызвать из командного файла.

Образец исполняемой программы VS Automator, вызывающей методы надстройки

''' Summary: Console App to automate MSVS build with special regard to Addin
Imports System
Imports Extensibility
Imports EnvDTE
Imports EnvDTE80
Imports System.Reflection
Imports System.Windows.Forms
Imports System.IO
'''<summary>Module class that automates launching MSVS in silent mode. It disables all user actions. A hidden window is used for MSVS automation. This should work for VS 2008 as well.</summary>
'''<remarks>An STA Thread is used for this console app. Refer http://msmvps.com/blogs/carlosq/archive/2007/10/11/frustrations-with-command-line-add-ins-for-visual-studio.aspx for details </remarks>
Module VSAutomaton
    Private Enum VisualStudioSolVersion
        Unknown = 0
        VSNET2002 = 1
        VSNET2003 = 2
        VS2005 = 3
        VS2008 = 4
    End Enum
    <STAThread()> _
    Sub Main(ByVal args() As String)
        Const ADDIN_PROGID As String = "AddIn-Name.Connect"
        Const ADDIN_METHOD As String = "SyncSolutionBatch" ' name of your method in addin code
        Dim dte As EnvDTE.DTE = Nothing
        Dim dteType As Type
        Dim commandLineAddIn As AddIn-Name.Connect = Nothing
        Dim solutionFullFileName As String
        Dim solutionFolder As String
        Dim solutionName As String
        Dim logFullFileName As String
        Dim buildLogFile As String = Nothing
        Dim buildConfig As String = Nothing
        Dim connectObject As Object = Nothing
        Dim connectObjectType As Type
        Dim version As VisualStudioSolVersion
        Dim progID As String
        Dim executableName As String
        Dim addIn As EnvDTE.AddIn
        Dim msgFilter As MessageFilter.MessageFilter = Nothing

        Try
            msgFilter = New MessageFilter.MessageFilter
            If args.Length = 0 Then
                executableName = IO.Path.GetFileName(System.Reflection.Assembly.GetExecutingAssembly.Location)
                ReportError("Usage: " & executableName & " solution_file_name.sln")
            Else
                solutionFullFileName = args(0) ' project solution file
                If Not IO.File.Exists(solutionFullFileName) Then
                    ReportError("Solution file '" & solutionFullFileName & "' does not exist.")
                Else
                    solutionFolder = IO.Path.GetDirectoryName(solutionFullFileName)
                    solutionName = IO.Path.GetFileNameWithoutExtension(solutionFullFileName)
                    logFullFileName = IO.Path.Combine(solutionFolder, solutionName & ".log")
                    If IO.File.Exists(logFullFileName) Then
                        IO.File.Delete(logFullFileName)
                    End If
                    version = GetSolutionVersion(solutionFullFileName)
                    If version = VisualStudioSolVersion.Unknown Then
                        ReportError("The format version of the solution file is not supported.")
                    Else
                        progID = GetVisualStudioProgID(version)
                        dteType = System.Type.GetTypeFromProgID(progID)
                        If dteType Is Nothing Then
                            ReportError("Could not find the ActiveX Server for ProgID '" & progID & "'. Likely the proper version of Visual Studio is not installed.")
                        Else
                            dte = DirectCast(System.Activator.CreateInstance(dteType), EnvDTE.DTE)
                            dte.SuppressUI = True
                            dte.UserControl = False
                            addIn = GetAddInByProgID(dte, ADDIN_PROGID)
                            If addIn Is Nothing Then
                                ReportError("The Add-in " & ADDIN_PROGID & " was not found in Visual Studio.")
                            Else
                                addIn.Connected = True
                                connectObject = addIn.Object
                                connectObjectType = connectObject.GetType
                                ' So a copy of the same DLL is necessary in the same dir as this app. exe
                                connectObjectType.InvokeMember(ADDIN_METHOD, Reflection.BindingFlags.InvokeMethod Or Reflection.BindingFlags.Static Or Reflection.BindingFlags.Public, Nothing, connectObject, New String() {solutionFullFileName})
                            End If
                        End If
                    End If
                End If
            End If

        Catch ex As Exception
            ReportError(ex.ToString)
        Finally
            If Not (dte Is Nothing) Then
                Try
                    dte.Quit() 
                Catch ex As Exception
                End Try
            End If
            If Not (msgFilter Is Nothing) Then
                ' this is a tricky aspect. We do not want leaks but .NET can sometimes be extra smart
                msgFilter.Dispose() 'If the GC decides to re-collect the garbage from this app, then a crash may result
                ' but this is the drawback of indeterministic destruction semantics
            End If
        End Try
    End Sub

    Private Sub ReportError(ByVal msg As String)
#If DEBUG Then
        MsgBox(msg)
#End If
        Console.WriteLine(msg)
    End Sub
    Private Function GetAddInByProgID(ByVal dte As EnvDTE.DTE, ByVal addinProgID As String) As EnvDTE.AddIn
        Dim addinResult As EnvDTE.AddIn = Nothing
        Dim addin As EnvDTE.AddIn
        For Each addin In dte.AddIns
            If addin.ProgID = addinProgID Then
                addinResult = addin
                Exit For
            End If
        Next
        Return addinResult
    End Function
    Private Function GetSolutionVersion(ByVal solutionFullFileName As String) As VisualStudioSolVersion
        Dim version As VisualStudioSolVersion = VisualStudioSolVersion.Unknown
        Dim solutionStreamReader As IO.StreamReader = Nothing
        Dim firstLine As String = Nothing
        Dim format As String
            Try
                solutionStreamReader = New IO.StreamReader(solutionFullFileName)
                firstLine = solutionStreamReader.ReadLine()
                format = firstLine.Substring(firstLine.LastIndexOf(" ")).Trim
                Select Case format
                    Case "7.00"
                        version = VisualStudioSolVersion.VSNET2002
                    Case "8.00"
                        version = VisualStudioSolVersion.VSNET2003
                    Case "9.00"
                        version = VisualStudioSolVersion.VS2005
                    Case "10.00"
                        version = VisualStudioSolVersion.VS2008
                End Select
            Finally
                If Not (solutionStreamReader Is Nothing) Then
                    solutionStreamReader.Close()
                End If
            End Try
        Return version
    End Function
    Private Function GetVisualStudioProgID(ByVal version As VisualStudioSolVersion) As String
        Dim progID As String = ""
        Select Case version
            Case VisualStudioSolVersion.VSNET2002
                progID = "VisualStudio.DTE.7"
            Case VisualStudioSolVersion.VSNET2003
                progID = "VisualStudio.DTE.7.1"
            Case VisualStudioSolVersion.VS2005
                progID = "VisualStudio.DTE.8.0"
            Case VisualStudioSolVersion.VS2008
                progID = "VisualStudio.DTE.9.0"
        End Select
        Return progID
    End Function

End Module

Пример командного файла для запуска exe автомата VS:

@echo off
:: --Usage:       $>BatchFileName.bat "<project_name(.sln)>" {Release | Debug} [ Make ]
::                Please remember the "double-quotes". 


REM -- check for blank input
if x%1%x == xx goto InputError
if x%2%x == xx goto InputError

echo Automating MSVS-Build for %1% ...
echo .

set arg1=%1%
REM -- remove quotes
for /f "useback tokens=*" %%a in ('%arg1%') do set match=%%~a
set slnFile=%match:~-4%
if %slnFile% == .sln goto lbl_FileOK 

:lbl_FileOK

REM build configuration and output file
set SOLFILE=%1%
set BUILDCONFIG=%2%
set CLEANWSOBJS=%3%


REM -- Read necessary registry entries
REM --- Read MSVS installation dir
regedit /e A$B$C$.bxt "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Setup\VS"
find "VS7CommonDir" <A$B$C$.bxt>A$B$C$.bat
goto :1st


:1st
for /F "tokens=1* delims==" %%A in ('TYPE A$B$C$.bat ^| find "VS7CommonDir"') do set vscomdir=%%B
set vscomdir=%vscomdir:"=%
REM -- Initialize the MSVS environment
set VSENV="%vscomdir%Tools\vsvars32.bat"
call %VSENV% > nul

REM -- remove quotes
for /f "useback tokens=*" %%a in ('%SOLFILE%') do set str=%%~a
set LastFolder=%str%
REM -- Extract the project name
if "%LastFolder:~-1%"=="\" set LastFolder=%LastFolder:~0,-1%
for %%a in ("%LastFolder%") do set LastFolder=%%~nxa
set flname=%LastFolder:.shared=.sln%
set tmpfile=%solPath%\%flname%

REM --- Check if the target '.sln' already exists, if yes delete
if EXIST %NEWSOLFILE% DEL /Q %NEWSOLFILE%
     REM -- use the addin functionality 
    VSAutomator.exe %SOLFILE%

    REM --- create log file as projectname_buildconfig.log
    set tmplog=%NEWSOLFILE:.sln=%
    set OUTLOGFILE=%tmplog%_%BUILDCONFIG%.log

    REM -- Now build the newly ready .sln file
    echo .
    echo Building Solution file - %NEWSOLFILE%, Output log file - %OUTLOGFILE%
    echo .

    if x%CLEANWSOBJS%x == xMakex goto JustBuild1
    devenv.com /useenv %NEWSOLFILE% /CLEAN %BUILDCONFIG% /OUT %OUTLOGFILE% > nul

:JustBuild1
    devenv.com /useenv %NEWSOLFILE% /BUILD %BUILDCONFIG% /OUT %OUTLOGFILE% > nul

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...