2009-06-22 4 views
1

Nous sommes encore un peu tôt dans la configuration de notre automatisation de construction et nous utilisons simplement un fichier bat pour Win32 C++ solutions. Nous avons environ 4 solutions et chacun a un couple de fichiers vcproj.Déterminer dynamiquement toutes les solutions, projets et configurations sur un événement build/trigger et les appeler toutes avec MSBuild à partir de la ligne de commande/script

Chaque fois qu'une nouvelle solution ou configuration est ajoutée, je dois mettre à jour le fichier bat pour refléter la nouvelle solution ou configuration à appeler avec MSBuild. Je pensais qu'il serait peut-être plus facile d'écrire un outil qui analyse tous les fichiers sln dans un chemin donné (et son sous-arbre), puis d'analyser tous les fichiers de projet qu'il référence pour les configurations et ensuite appeler toutes les versions.

Y at-il un moyen facile de faire cela?

Vraiment ceci est 2 questions:

  1. Comment puis-je dire MSBuild juste pour construire toutes les solutions à l'intérieur d'un sous-arbre? (Je peux faire une recherche sur eux - c'est un outil simple que je pense écrire)

  2. Comment puis-je dire à MSBuild de construire toutes les configurations d'une solution/vcproj?

Nous utilisons MSBuild, les fichiers de chauve-souris, VC2008, C++

+0

pourquoi dans le monde serait downvote cette question? Au moins fournir une raison pour la downvote ... wow. – Tim

Répondre

0

Le cadre de construction Microsoft est accessible via des API. Mais je ne suis pas sûr si on peut le faire uniquement via un fichier batch. À mon humble avis le plus simple est d'automatiser la construction en écrivant un addin (en utilisant peut-être VB.NET) à l'IDE VS. Cet addin peut être appelé à partir d'un fichier batch. Nous avons automatisé les compilations VC++ en utilisant l'approche addin. Dans notre cas, il n'y avait pas d'autre moyen, car nous devions automatiser le processus moc de Qt également. Je pense que ce sera plus flexible aussi.

Le système de construction (au moins dans VS 2005) sur lequel j'ai travaillé était assez bogué. Reportez-vous à ce link, il vous aidera à comprendre les pièges et fournit également des extraits de code sur la façon de parcourir les fichiers de la solution. Une fois l'addin installé, un petit exe peut être créé pour invoquer la construction via addin. Cet exe peut être appelé à partir d'un fichier batch.

Exemples VS Automator exe qui appelle des méthodes Addin

''' 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 

fichier batch Exemple pour invike VS Automator exe:

@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 

S'il vous plaît rappelez-vous que le code présenté ci-dessus peut ne pas être parfait comme i l'ai référé de mon POC que j'ai fait quand j'ai eu un problème d'automatisation similaire.

1

Être fan PowerShell le one-liner reformaté suivant pourrait être utile:

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])" 
    } 
} 

Ce script peut être enregistré comme un fichier ps1 ou collé en fonction de votre profil. Pour expliquer ce qu'il fait:

  1. trouver tous.fichiers SLN et sous le répertoire courant
  2. analyser le sln extraire la configuration et la plate-forme des valeurs
  3. pour chaque appel combinaison plate-forme de configuration unique msbuild avec la solution

donné de --edit: Mot de Split-cordes est partie de PSCX, il est préférable d'utiliser [chaîne] J'espère que cela aide. Si vous souhaitez toujours utiliser MsBuild, il prend en charge une forme récursive de l'opérateur glob de fichier via la syntaxe ". \ ** \ *. Sln" voir here pour plus de détails. MsBuild fournit également la tâche MsBuild qui peut être utilisée pour créer un ensemble de solutions. Je ne sais pas comment vous pouvez obtenir toutes les configurations «connues» d'une solution facilement dans msbuild. Par conséquent, je choisis la route PowerShell.

+0

super, merci. Je vais essayer. – Tim

Questions connexes