40 votes

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

Je travaille avec Visual Studio C++ 2005 solution qui comprend de multiples projets (environ 30). Basée sur mon expérience, c'est souvent gênant pour conserver toutes les propriétés de projets (j'.e chemin include, lib chemin, lié libs, options de génération de code, ...), que vous avez souvent à cliquer sur chaque projet afin de les modifier. La situation devient encore pire lorsque vous avez plusieurs configurations (Debug, Release, Version 64 bits, ...).

Exemples concrets:

  • Supposons que vous souhaitez utiliser une nouvelle bibliothèque, et vous devez ajouter le chemin d'inclusion de cette bibliothèque pour tous les projets. Comment allez-vous éviter d'avoir à modifier les propriétés de chaque une de chaque projet?
  • Supposons que vous voulez faire l'essai d'une nouvelle version de bibliothèque (dire la version 2.1 beta), de sorte que vous devez changer rapidement les chemins d'inclusion / chemin de la bibliothèque / bibliothèque de liens pour un ensemble de projets?

Notes:

  • Je suis conscient qu'il est possible de sélectionner plusieurs projets à la fois, puis faire un clic droit et sélectionnez "propriétés". Toutefois, cette méthode ne fonctionne que pour les propriétés qui ont été déjà exactement identiques pour les différents projets : vous ne pouvez pas l'utiliser pour ajouter un chemin d'inclusion d'un ensemble de projets qui ont été à l'aide de différents inclure le chemin d'accès.
  • Je sais aussi qu'il est possible de modifier globalement les options de l'environnement (Outils/Options/Projet Et des solutions de sous-Répertoires), mais il n'est pas satisfaisant car il ne peut pas être intégré dans une SCM
  • Je sais aussi qu'on peut ajouter des "Configurations" à l'une des solutions. Il n'a pas d'aide car cela fait un autre ensemble de propriétés du projet, à maintenir
  • Je sais que codegear C++ Builder 2009 offre une réponse viable à ce besoin par le soi-disant "Option définit" ce qui peut être hérité par plusieurs projets (j'utilise Visual Studio et C++ Builder, et je pense toujours que C++ Builder rochers sur certains aspects par rapport à Visual Studio)
  • J'espère que quelqu'un va proposer un "autconf" comme CMake, cependant est-il possible d'importer vcproj fichiers dans un tel outil?

32voto

quamrana Points 6411

Je pense que vous devez examiner les fichiers de propriétés, par exemple * .vsprops

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, mais un fichier .vsprops. Si vous modifiez les propriétés, tous les projets héritent des nouveaux paramètres.

17voto

Jere.Jones Points 5394

J'ai souvent besoin de faire quelque chose de semblable depuis que j'ai le lien de la statique des bibliothèques d'exécution. J'ai écrit un programme pour le faire pour moi. Essentiellement, il analyse tous les sous-répertoires de quelle que soit la voie que vous donnez et les id de tout .vcproj les fichiers qu'il trouve. Puis, un par un, il les ouvre les modifie et les enregistre. Depuis je ne l'utilise que rarement, le chemin est codé en dur, mais je pense que vous serez en mesure de l'ajuster comme vous le souhaitez.

Une autre approche est de réaliser que Visual Studio fichiers de Projet sont simplement des fichiers XML et peut être manipulé avec votre favori de la classe XML. J'ai fait quelque chose à l'aide de C#XmlDocument pour la mise à jour de l'inclure des répertoires quand il y avait BEAUCOUP de répertoires include que je n'ai pas envie de taper dans. :)

Je suis, y compris les deux exemples. Vous aurez besoin de les modifier pour vos besoins propres, mais celles-ci devraient vous aider à démarrer.

C'est 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());
}

C'est 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;
        }
    }
}

6voto

Xavier Nodet Points 2498

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

3voto

JesperE Points 34356

Oui, je serais certainement vous suggérons d'utiliser CMake. CMake est le meilleur outil (je pense en fait, j'ai tout essayé), ce qui peut générer Studio fichiers de projet.

J'ai également eu le problème de la conversion de l'existant .vcproj fichiers dans CMakeLists.txt j'ai écrit un Rubis script qui prend soin de la plupart de la conversion. Le script ne gère pas les choses comme post-étapes de génération et de ces, de sorte que certains ajustements sont nécessaires, mais il vous fera économiser les tracas de tirer tous les noms de fichier source à partir de l' .vcproj fichiers.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X