Solutions Visual Studio / Projets multiples: comment propager efficacement les propriétés d'un projet à plusieurs projets C ++

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

Question

Je travaille avec une solution Visual Studio 2005 C ++ qui comprend plusieurs projets (environ 30). Sur la base de mon expérience, il devient souvent gênant de conserver toutes les propriétés des projets (par exemple, chemin d’inclusion, chemin de la bibliothèque, bibliothèques liées, options de génération de code, ...), car vous devez souvent cliquer sur chaque projet pour modifiez-les. La situation devient encore pire lorsque vous avez plusieurs configurations (Debug, Release, Release 64 bits, ...).

Exemples concrets:

  • Supposons que vous souhaitiez utiliser une nouvelle bibliothèque et que vous deviez ajouter le chemin d'inclusion de cette bibliothèque à tous les projets. Comment allez-vous éviter de devoir modifier les propriétés de chaque projet?
  • Supposons que vous souhaitiez tester une nouvelle version de la bibliothèque (par exemple la version 2.1beta) de sorte que vous deviez modifier rapidement les chemins d'inclusion / chemin de bibliothèque / bibliothèque liée pour un ensemble de projets?

Notes:

  • Je suis conscient qu'il est possible de sélectionner plusieurs projets à la fois, puis de cliquer avec le bouton droit de la souris et de sélectionner "propriétés". Toutefois, cette méthode ne fonctionne que pour les propriétés déjà identiques pour les différents projets: vous ne pouvez pas l’utiliser pour ajouter un chemin d’inclusion à un ensemble de projets utilisant un chemin d’inclusion différent.
  • Je sais aussi qu'il est possible de modifier globalement les options d'environnement (Outils / Options / Projet et solutions / Répertoires), mais ce n'est pas très satisfaisant car il ne peut pas être intégré dans un GDS
  • Je sais aussi que l'on peut ajouter " Configurations " à des solutions. Cela n’aide en rien car cela crée un autre ensemble de propriétés de projet
  • Je sais que codegear C ++ Builder 2009 offre une réponse viable à ce besoin par le biais de ce que l'on appelle "ensembles d'options". qui peut être hérité de plusieurs projets (j'utilise à la fois Visual Studio et C ++ Builder, et je pense toujours que C ++ Builder joue un rôle clé par rapport à Visual Studio)
  • Je pense que quelqu'un vous proposera un "quot automatique". comme CMake, est-il possible d'importer des fichiers vcproj dans un tel outil?
Était-ce utile?

La solution

Je pense que vous devez examiner les fichiers de propriétés, c'est-à-dire *. vsprops (ancien) ou * .props (dernière)

Vous avez besoin d'ajouter le fichier de propriétés manuellement à chaque projet, mais une fois que cela est fait, vous avez plusieurs projets, à l'exception d'un fichier. [vs]. Si vous modifiez les propriétés, tous les projets hériteront des nouveaux paramètres.

Autres conseils

J'ai souvent besoin de faire quelque chose de similaire depuis que je fais un lien vers les bibliothèques d'exécution statiques. J'ai écrit un programme pour le faire pour moi. En gros, il analyse tous les sous-répertoires du chemin que vous lui donnez et identifie tous les fichiers .vcproj qu'il trouve. Ensuite, un par un, il les ouvre les modifie et les sauvegarde. Comme je ne l'utilise que rarement, le chemin est codé en dur, mais je pense que vous pourrez l'ajuster à votre guise.

Une autre approche consiste à réaliser que les fichiers Visual Studio Project sont simplement des fichiers XML et peuvent être manipulés avec votre classe XML préférée. J'ai utilisé quelque chose en utilisant XmlDocument de C # pour mettre à jour les répertoires d'inclusion lorsqu'il y avait BEAUCOUP de répertoires d'inclusion que je ne voulais pas taper.:)

J'inclus les deux exemples. Vous devrez les adapter à vos propres besoins, mais ceux-ci devraient vous aider à démarrer.

Il s'agit de la version 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());
}

Il s'agit de la version C #. Profitez de ...

 
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;
        }
    }
}

Comme suggéré, vous devriez consulter les feuilles de propriétés (fichiers .vsprops).
J'ai écrit une très courte introduction à cette fonctionnalité ici .

Oui, je recommanderais certainement d'utiliser CMake . CMake est le meilleur outil (je pense les avoir tous essayés) qui peut générer des fichiers de projet Studio.

J'ai également eu le problème de convertir les fichiers .vcproj existants en CMakeLists.txt et j'ai écrit un Ruby -script qui s’occupe de la majeure partie de la conversion. Le script ne gère pas des tâches telles que les étapes de post-génération, etc. Il est donc nécessaire de peaufiner certains réglages, mais il vous évite d'avoir à extraire tous les noms de fichiers source des fichiers .vcproj.

Les fichiers

*. vcxproj sont des fichiers msbuild. Vous devez donc simplement supprimer une propriété que vous ne voulez pas dans tous vos fichiers de projet. Ensuite, mettez-le dans votre fiche de propriété. Assurez-vous ensuite que tous les fichiers de projet importent correctement cette feuille de propriétés.

Cela peut être extrêmement fastidieux pour des centaines de fichiers. J'ai écrit un outil pour rendre cette interactive:

https://github.com/chris1248/MsbuildRefactor

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top