projetos Como propriedades do projeto efetivamente propagar entre vários C ++: Visual Studio Solutions / projeto múltiplo

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

Pergunta

Eu estou trabalhando com uma solução Visual Studio 2005 C ++ que inclui vários projectos (cerca de 30). Com base na minha experiência, que muitas vezes se torna desagradável para manter todas as propriedades dos projectos (ou seja, incluir caminho, caminho lib, libs ligados, opções de geração de código, ...), como muitas vezes você tem que clicar em cada projeto, a fim de modificá-los. A situação torna-se ainda pior quando você tem múltiplas configurações (debug, release versão 64 bits, ...).

exemplos da vida real:

  • Suponha que você deseja usar uma nova biblioteca, e você precisa adicionar o caminho incluem a esta biblioteca para todos os projetos. Como você vai evitar ter que editar as propriedades de cada um cada projeto?
  • Suponha que você quer testar a movimentação uma nova versão da biblioteca (digamos versão 2.1beta), de modo que você precisa para mudar rapidamente a incluir caminhos / caminho da biblioteca / biblioteca ligados por um conjunto de projectos?

Notas:

  • Estou ciente de que é possível selecionar vários projetos ao mesmo tempo, em seguida, fazer um clique direito e selecione "Propriedades". No entanto, este método só funciona para as propriedades que já estavam exatamente idêntico para os diferentes projectos:. Você não pode usá-lo, a fim de adicionar um caminho de inclusão de um conjunto de projectos que estavam usando diferentes incluem caminho
  • Também sei que é possível modificar globalmente as opções de ambiente (Ferramentas / Opções / Projeto e Soluções / Diretórios), no entanto, não é que satisfazer uma vez que não podem ser integrados em um SCM
  • Eu também sei que se pode adicionar "Configurações" a um soluções. Isso não ajuda, uma vez que faz um outro conjunto de propriedades do projeto para manter
  • Eu sei que CodeGear C ++ Builder 2009 oferece uma resposta viável a esta necessidade através chamados "conjuntos de opções" que podem ser herdadas por vários projetos (eu uso tanto Studio e C ++ Builder Visual, e eu ainda pensa rochas C ++ Builder em certa aspectos, em comparação com Visual Studio)
  • Eu espero que alguém vai sugerir uma "autconf", como CMake, no entanto é possível arquivos vcproj importação em tal ferramenta?
Foi útil?

Solução

Eu acho que você precisa investigar arquivos de propriedades, ou seja, *. vsprops (mais antigo) ou * .props (mais recente)

Você do necessidade de adicionar o arquivo de propriedades manualmente para cada projeto, mas uma vez feito isso, você tem vários projetos, mas um. [Vs] adereços arquivo. Se você alterar as propriedades, todos os projetos de herdar as novas configurações.

Outras dicas

Muitas vezes eu preciso fazer algo semelhante desde que eu fazer o link com as bibliotecas de execução estáticos. Eu escrevi um programa para fazer isso por mim. Ele basicamente verifica todos os subdiretórios de qualquer caminho que você dê a ele e IDs de todos os arquivos .vcproj que encontra. Em seguida, um por um, abre-los modifica-los e os salva. Desde que eu só usá-lo raramente, o caminho é codificado-lo, mas eu acho que você vai ser capaz de ajustá-lo como você gosta.

Outra abordagem é perceber que arquivos o Visual Studio projeto são simplesmente arquivos XML e podem ser manipulados com a classe XML favorito. Eu fiz algo usando C # 's XmlDocument para atualizar a incluir diretórios quando havia A LOT de incluir diretórios que não deseja digitar:.)

Estou incluindo ambos os exemplos. Você terá que modificá-los para suas próprias necessidades, mas estes devem começar.

Esta é a C ++ versão:

#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 é a versão C #. Desfrute ...

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 sugerido, você deve olhar para folhas de propriedades (aka .vsprops arquivos).
Eu escrevi uma introdução muito breve a esta característica aqui .

Sim, eu definitivamente sugiro usar CMake . CMake é a melhor ferramenta (eu acho que eu realmente tentei todos eles), que pode gerar arquivos de projeto de estúdio.

Eu também tive o problema de converter arquivos existentes .vcproj em CMakeLists.txt, e eu escrevi um Rubi -script que cuida da maior parte da conversão. O script não lidar com coisas como etapas de pós-construção e tal, então alguns ajustes é necessário, mas vai poupar-lhe o aborrecimento de puxar todos os nomes de arquivo de origem a partir dos arquivos .vcproj.

arquivos vcxproj

*. são arquivos MSBuild. Então você tomar apenas uma propriedade que você não quer em todos os seus arquivos de projeto e excluí-lo. Em seguida, colocá-lo em sua folha de propriedades. Em seguida, certifique-se todos os arquivos de projetos de importação corretamente que a folha de propriedades.

Isso pode ser incrivelmente entediante para centenas de arquivos. Eu escrevi uma ferramenta de fazer esta interativo:

https://github.com/chris1248/MsbuildRefactor

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top