Soluciones de Visual Studio / Proyecto múltiple:Cómo propagar eficazmente las propiedades del proyecto entre varios proyectos de C++

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

Pregunta

Estoy trabajando con una solución Visual Studio 2005 C++ que incluye varios proyectos (alrededor de 30).Según mi experiencia, a menudo resulta molesto mantener todas las propiedades de los proyectos (es decir, incluir ruta, ruta de biblioteca, bibliotecas vinculadas, opciones de generación de código, ...), ya que a menudo hay que hacer clic en todos y cada uno de los proyectos para poder modificarlos.La situación empeora aún más cuando tienes múltiples configuraciones (Depurar, Liberar, Liberar 64 bits, ...).

Ejemplos de la vida real:

  • Supongamos que desea utilizar una nueva biblioteca y necesita agregar la ruta de inclusión de esta biblioteca a todos los proyectos.¿Cómo evitarás tener que editar las propiedades de cada proyecto?
  • Supongamos que desea probar una nueva versión de la biblioteca (por ejemplo, la versión 2.1beta), por lo que necesita cambiar rápidamente las rutas de inclusión/ruta de la biblioteca/biblioteca vinculada para un conjunto de proyectos.

Notas:

  • Soy consciente de que es posible seleccionar varios proyectos a la vez, luego hacer clic derecho y seleccionar "propiedades".Sin embargo, este método sólo funciona para propiedades que ya eran exactamente idénticas para los diferentes proyectos:no puede usarlo para agregar una ruta de inclusión a un conjunto de proyectos que usaban una ruta de inclusión diferente.
  • También sé que es posible modificar globalmente las opciones del entorno (Herramientas/Opciones/Proyecto Y soluciones/Directorios), sin embargo no es tan satisfactorio ya que no se puede integrar en un SCM.
  • También sé que se pueden agregar "Configuraciones" a las soluciones.No ayuda ya que crea otro conjunto de propiedades del proyecto para mantener.
  • Sé que Codegear C++ Builder 2009 ofrece una respuesta viable a esta necesidad a través de los llamados "conjuntos de opciones" que pueden ser heredados por varios proyectos (yo uso Visual Studio y C++ Builder, y sigo pensando que C++ Builder es excelente en ciertos aspectos en comparación). a Visual Studio)
  • Espero que alguien sugiera una "autconf" como CMake, sin embargo, ¿es posible importar archivos vcproj a dicha herramienta?
¿Fue útil?

Solución

Creo que necesitas investigar los archivos de propiedades, es decir. *.vsprops (mayor) o *.accesorios (el último)

hacer Es necesario agregar el archivo de propiedades manualmente a cada proyecto, pero una vez hecho esto, tendrá varios proyectos, pero un archivo .[vs]props.Si cambia las propiedades, todos los proyectos heredan la nueva configuración.

Otros consejos

A menudo necesito hacer algo similar ya que me vinculo a las bibliotecas de tiempo de ejecución estáticas.Escribí un programa para hacerlo por mí.Básicamente, escanea todos los subdirectorios de cualquier ruta que le proporcione e identifica cualquier archivo .vcproj que encuentre.Luego, uno a uno, los abre, los modifica y los guarda.Como lo uso rara vez, la ruta está codificada, pero creo que podrás ajustarla como quieras.

Otro enfoque es darse cuenta de que los archivos de Visual Studio Project son simplemente archivos XML y pueden manipularse con su clase XML favorita.He hecho algo usando C# XmlDocument para actualizar los directorios de inclusión cuando había MUCHO de incluir directorios en los que no quería escribir.:)

Estoy incluyendo ambos ejemplos.Deberá modificarlos según sus propias necesidades, pero esto le ayudará a empezar.

Esta es la versión 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());
}

Esta es la versión C#.Disfrutar...

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

Como se sugirió, debería consultar las hojas de propiedades (también conocidas como archivos .vsprops).
Escribí una introducción muy breve a esta función. aquí.

Sí, definitivamente sugeriría usar Chacer.CMake es la mejor herramienta (creo que las he probado todas) que puede generar archivos de proyectos de Studio.

También tuve el problema de convertir archivos .vcproj existentes a CMakeLists.txt y escribí un Guión Ruby que se encarga de la mayor parte de la conversión.El script no maneja cosas como pasos posteriores a la compilación y demás, por lo que es necesario realizar algunos ajustes, pero le ahorrará la molestia de extraer todos los nombres de los archivos fuente de los archivos .vcproj.

* Los archivos .vcxproj son archivos msbuild.Así que simplemente toma una propiedad que no desea en todos los archivos de su proyecto y la elimina.Luego ponlo en tu hoja de propiedad.Luego asegúrese de que todos los archivos de proyectos importen correctamente esa hoja de propiedades.

Esto puede resultar increíblemente tedioso para cientos de archivos.Escribí una herramienta para hacer esto interactivo:

https://github.com/chris1248/MsbuildRefactor

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top