Visual Studio Solutions / Progetto multiplo: come propagare efficacemente le proprietà del progetto tra diversi progetti C ++

StackOverflow https://stackoverflow.com/questions/111631

Domanda

Sto lavorando con una soluzione C ++ di Visual Studio 2005 che include più progetti (circa 30). In base alla mia esperienza, spesso diventa fastidioso mantenere tutte le proprietà dei progetti (ad esempio percorso, percorso lib, librerie collegate, opzioni di generazione del codice, ...), poiché spesso è necessario fare clic su ogni progetto per modificali. La situazione peggiora ancora quando hai più configurazioni (Debug, Release, Release 64 bit, ...).

Esempi di vita reale:

  • Supponi di voler utilizzare una nuova libreria e devi aggiungere il percorso di inclusione a questa libreria a tutti i progetti. Come eviterai di dover modificare le proprietà di ognuno di ogni progetto?
  • Supponi di voler testare una nuova versione della libreria (ad esempio la versione 2.1beta) in modo da dover cambiare rapidamente i percorsi include / percorso libreria / libreria collegata per una serie di progetti?

Note:

  • Sono consapevole che è possibile selezionare più progetti alla volta, quindi fare clic con il tasto destro e selezionare " proprietà " ;. Tuttavia, questo metodo funziona solo per proprietà che erano già esattamente identiche per i diversi progetti: non è possibile utilizzarlo per aggiungere un percorso di inclusione a un insieme di progetti che utilizzavano un percorso di inclusione diverso.
  • So anche che è possibile modificare globalmente le opzioni ambientali (Strumenti / Opzioni / Progetto e soluzioni / Directory), tuttavia non è così soddisfacente poiché non può essere integrato in un SCM
  • So anche che è possibile aggiungere " Configurazioni " a una soluzione. Non aiuta poiché crea un altro set di proprietà del progetto da mantenere
  • So che codegear C ++ Builder 2009 offre una risposta praticabile a questa esigenza attraverso i cosiddetti "set di opzioni". che può essere ereditato da diversi progetti (utilizzo sia Visual Studio che C ++ Builder, e penso ancora che C ++ Builder sia in qualche modo diverso rispetto a Visual Studio)
  • Mi aspetto che qualcuno suggerirà un " autconf " come CMake, tuttavia è possibile importare file vcproj in un tale strumento?
È stato utile?

Soluzione

Penso che sia necessario esaminare i file delle proprietà, ovvero *. vsprops (precedente) o * .props (più recente)

Devi fare aggiungere manualmente il file delle proprietà a ciascun progetto, ma una volta fatto, hai più progetti, ma uno. [vs] file di oggetti di scena. Se si modificano le proprietà, tutti i progetti ereditano le nuove impostazioni.

Altri suggerimenti

Spesso devo fare qualcosa di simile poiché mi collego alle librerie di runtime statiche. Ho scritto un programma per farlo per me. Fondamentalmente scansiona tutte le sottodirectory di qualunque percorso tu le dia e identifica tutti i file .vcproj che trova. Quindi uno per uno, li apre, li modifica e li salva. Dal momento che lo uso solo raramente, il percorso è fortemente codificato, ma penso che sarai in grado di regolarlo come preferisci.

Un altro approccio è rendersi conto che i file di Visual Studio Project sono semplicemente file XML e possono essere manipolati con la tua classe XML preferita. Ho fatto qualcosa usando XmlDocument di C # per aggiornare le directory include quando c'erano MOLTO di directory include che non volevo digitare.:)

Includo entrambi gli esempi. Dovrai modificarli in base alle tue esigenze, ma questi dovrebbero iniziare.

Questa è la versione C ++:

 
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <boost/filesystem/convenience.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/regex.hpp>
#include <boost/timer.hpp>

using boost::regex;
using boost::filesystem::path;
using namespace std;

vector<path> GetFileList(path dir, bool recursive, regex matchExp);
void FixProjectFile(path file);
string ReadFile( path &file );
void ReplaceRuntimeLibraries( string& contents );
void WriteFile(path file, string contents);

int _tmain(int argc, _TCHAR* argv[])
{
    boost::timer stopwatch;
    boost::filesystem::path::default_name_check(boost::filesystem::native);
    regex projFileRegex("(.*)\\.vcproj");
    path rootPath("D:\\Programming\\Projects\\IPP_Decoder");

    vector<path> targetFiles = GetFileList(rootPath, true, projFileRegex);
    double listTimeTaken = stopwatch.elapsed();

    std::for_each(targetFiles.begin(), targetFiles.end(), FixProjectFile);

    double totalTimeTaken = stopwatch.elapsed();
    return 0;
}

void FixProjectFile(path file) {
    string contents = ReadFile(file);
    ReplaceRuntimeLibraries(contents);
    WriteFile(file, contents);
}

vector<path> GetFileList(path dir, bool recursive, regex matchExp) {
    vector<path> paths;
    try {
        boost::filesystem::directory_iterator di(dir);
        boost::filesystem::directory_iterator end_iter;
        while (di != end_iter) {
            try {
                if (is_directory(*di)) {
                    if (recursive) {
                        vector<path> tempPaths = GetFileList(*di, recursive, matchExp);
                        paths.insert(paths.end(), tempPaths.begin(), tempPaths.end());
                    }
                } else {
                    if (regex_match(di->string(), matchExp)) {
                        paths.push_back(*di);
                    }
                }
            }
            catch (std::exception& e) {
                string str = e.what();
                cout << str << endl;
                int breakpoint = 0;
            }
            ++di;
        }
    }
    catch (std::exception& e) {
        string str = e.what();
        cout << str << endl;
        int breakpoint = 0;
    }
    return paths;
}

string ReadFile( path &file ) {
//  cout << "Reading file: " << file.native_file_string() << "\n";
    ifstream infile (file.native_file_string().c_str(), ios::in | ios::ate);
    assert (infile.is_open());

    streampos sz = infile.tellg();
    infile.seekg(0, ios::beg);

    vector<char> v(sz);
    infile.read(&v[0], sz);

    string str (v.empty() ? string() : string (v.begin(), v.end()).c_str());

    return str;
}

void ReplaceRuntimeLibraries( string& contents ) {
    regex releaseRegex("RuntimeLibrary=\"2\"");
    regex debugRegex("RuntimeLibrary=\"3\"");
    string releaseReplacement("RuntimeLibrary=\"0\"");
    string debugReplacement("RuntimeLibrary=\"1\"");
    contents = boost::regex_replace(contents, releaseRegex, releaseReplacement);
    contents = boost::regex_replace(contents, debugRegex, debugReplacement);
}

void WriteFile(path file, string contents) {
    ofstream out(file.native_file_string().c_str() ,ios::out|ios::binary|ios::trunc); 
    out.write(contents.c_str(), contents.length());
}

Questa è la versione C #. Godetevi ...

 
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;

namespace ProjectUpdater
{
    class Program
    {
        static public String rootPath = "D:\\dev\\src\\co\\UMC6\\";
        static void Main(string[] args)
        {
            String path = "D:/dev/src/co/UMC6/UMC.vcproj";
            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            XmlDocument xmldoc = new XmlDocument();
            xmldoc.Load(fs);
            XmlNodeList oldFiles = xmldoc.GetElementsByTagName("Files");
            XmlNode rootNode = oldFiles[0].ParentNode;
            rootNode.RemoveChild(oldFiles[0]);

            XmlNodeList priorNode = xmldoc.GetElementsByTagName("References");
            XmlElement filesNode = xmldoc.CreateElement("Files");
            rootNode.InsertAfter(filesNode, priorNode[0]);

            DirectoryInfo di = new DirectoryInfo(rootPath);
            foreach (DirectoryInfo thisDir in di.GetDirectories())
            {
                AddAllFiles(xmldoc, filesNode, thisDir.FullName);
            }


            List<String> allDirectories = GetAllDirectories(rootPath);
            for (int i = 0; i < allDirectories.Count; ++i)
            {
                allDirectories[i] = allDirectories[i].Replace(rootPath, "$(ProjectDir)");
            }
            String includeDirectories = "\"D:\\dev\\lib\\inc\\ipp\\\"";
            foreach (String dir in allDirectories) 
            {
                includeDirectories += ";\"" + dir + "\"";
            }

            XmlNodeList toolNodes = xmldoc.GetElementsByTagName("Tool");
            foreach (XmlNode node in toolNodes)
            {
                if (node.Attributes["Name"].Value == "VCCLCompilerTool") {
                    try
                    {
                        node.Attributes["AdditionalIncludeDirectories"].Value = includeDirectories;
                    }
                    catch (System.Exception e)
                    {
                        XmlAttribute newAttr = xmldoc.CreateAttribute("AdditionalIncludeDirectories");
                        newAttr.Value = includeDirectories;
                        node.Attributes.InsertBefore(newAttr, node.Attributes["PreprocessorDefinitions"]);
                    }

                }
            }
            String pathOut = "D:/dev/src/co/UMC6/UMC.xml";
            FileStream fsOut = new FileStream(pathOut, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
            xmldoc.Save(fsOut);

        }
        static void AddAllFiles(XmlDocument doc, XmlElement parent, String path) {
            DirectoryInfo di = new DirectoryInfo(path);
            XmlElement thisElement = doc.CreateElement("Filter");
            thisElement.SetAttribute("Name", di.Name);
            foreach (FileInfo fi in di.GetFiles())
            {
                XmlElement thisFile = doc.CreateElement("File");
                String relPath = fi.FullName.Replace(rootPath, ".\\");
                thisFile.SetAttribute("RelativePath", relPath);
                thisElement.AppendChild(thisFile);
            }
            foreach (DirectoryInfo thisDir in di.GetDirectories())
            {
                AddAllFiles(doc, thisElement, thisDir.FullName);
            }
            parent.AppendChild(thisElement);
        }
        static List<String> GetAllDirectories(String dir)
        {
            DirectoryInfo di = new DirectoryInfo(dir);
            Console.WriteLine(dir);

            List<String> files = new List<String>();
            foreach (DirectoryInfo subDir in di.GetDirectories())
            {
                List<String> newList = GetAllDirectories(subDir.FullName);
                files.Add(subDir.FullName);
                files.AddRange(newList);
            }
            return files;
        }
        static List<String> GetAllFiles(String dir)
        {
            DirectoryInfo di = new DirectoryInfo(dir);
            Console.WriteLine(dir);

            List<String> files = new List<String>();
            foreach (DirectoryInfo subDir in di.GetDirectories())
            {
                List<String> newList = GetAllFiles(subDir.FullName);
                files.AddRange(newList);
            }
            foreach (FileInfo fi in di.GetFiles())
            {
                files.Add(fi.FullName);
            }
            return files;
        }
    }
}

Come suggerito, dovresti dare un'occhiata ai fogli di proprietà (alias file .vsprops).
Ho scritto una breve introduzione a questa funzione qui .

Sì, suggerirei sicuramente di utilizzare CMake . CMake è lo strumento migliore (penso di averli effettivamente provati tutti) in grado di generare file di progetto di Studio.

Ho anche avuto il problema di convertire i file .vcproj esistenti in CMakeLists.txt e ho scritto un Ruby -script che si occupa della maggior parte della conversione. Lo script non gestisce cose come i passaggi post-build e simili, quindi sono necessarie alcune modifiche, ma ti risparmierà il fastidio di estrarre tutti i nomi dei file di origine dai file .vcproj.

* I file .vcxproj sono file msbuild. Quindi prendi semplicemente una proprietà che non vuoi in tutti i tuoi file di progetto ed eliminala. Quindi inseriscilo nella tua finestra delle proprietà. Quindi assicurati che tutti i file dei progetti importino correttamente quella finestra delle proprietà.

Questo può essere incredibilmente noioso per centinaia di file. Ho scritto uno strumento per renderlo interattivo:

https://github.com/chris1248/MsbuildRefactor

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top