【问题标题】:Visual Studio Solutions / Multiple project : How to effectively propagate project properties amongst several C++ projectsVisual Studio 解决方案/多个项目:如何在多个 C++ 项目之间有效传播项目属性
【发布时间】:2008-09-21 18:28:21
【问题描述】:

我正在使用包含多个项目(大约 30 个)的 Visual Studio 2005 C++ 解决方案。 根据我的经验,维护项目的所有属性(即包括路径、库路径、链接库、代码生成选项...)通常会变得很烦人,因为您经常必须单击每个项目才能修改它们。 当您有多种配置(Debug、Release、Release 64 bits,...)时,情况会变得更糟。

现实生活中的例子:

  • 假设您要使用一个新库,并且您需要将该库的包含路径添加到所有项目中。 您将如何避免必须编辑每个项目的属性?
  • 假设您想试驾新版本的库(比如 2.1beta 版),因此您需要快速更改一组项目的包含路径/库路径/链接库?

注意事项:

  • 我知道可以一次选择多个项目,然后右键单击并选择“属性”。但是,此方法仅适用于对于不同项目已经完全相同的属性:您不能使用它来向使用不同包含路径的一组项目添加包含路径。
  • 我也知道可以全局修改环境选项(工具/选项/项目和解决方案/目录),但是由于它不能集成到 SCM 中,所以并不令人满意
  • 我也知道可以将“配置”添加到解决方案中。它没有帮助,因为它需要维护另一组项目属性
  • 我知道 codegear C++ Builder 2009 通过所谓的“选项集”为这个需求提供了一个可行的答案,它可以被多个项目继承(我同时使用 Visual Studio 和 C++ Builder,我仍然认为 C++ Builder 在某些方面很出色方面与 Visual Studio 相比)
  • 我希望有人会建议使用“autconf”,例如 CMake,但是是否可以将 vcproj 文件导入到这样的工具中?

【问题讨论】:

标签: visual-studio projects-and-solutions


【解决方案1】:

我认为您需要调查属性文件,即*.vsprops(旧)或*.props(最新)

确实需要手动将属性文件添加到每个项目,但一旦完成,您将拥有多个项目,但只有一个 .[vs]props 文件。如果您更改属性,所有项目都会继承新设置。

【讨论】:

    【解决方案2】:

    我经常需要做类似的事情,因为我链接到静态运行时库。我写了一个程序来为我做这件事。它基本上扫描你给它的任何路径的所有子目录,并识别它找到的任何 .vcproj 文件。然后一一打开,修改它们并保存它们。由于我很少使用它,因此路径是硬编码的,但我认为您可以根据需要调整它。

    另一种方法是认识到 Visual Studio 项目文件只是 XML 文件,并且可以使用您最喜欢的 XML 类进行操作。当有 很多 我不想输入的包含目录时,我使用 C# 的 XmlDocument 更新了包含目录。:)

    我将两个示例都包括在内。您需要根据自己的需要修改它们,但这些应该可以帮助您入门。

    这是 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# 版本。享受...

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

    【讨论】:

      【解决方案3】:

      按照建议,您应该查看属性表(又名 .vsprops 文件)。
      我写了一篇关于这个功能的简短介绍here

      【讨论】:

        【解决方案4】:

        是的,我绝对建议使用CMake。 CMake 是可以生成 Studio 项目文件的最佳工具(我想我实际上已经尝试过所有这些工具)。

        我还遇到了将现有 .vcproj 文件转换为 CMakeLists.txt 的问题,我写了一个 Ruby-script 来处理大部分转换。该脚本不处理诸如后期构建步骤之类的事情,因此需要进行一些调整,但它会为您省去从 .vcproj 文件中提取所有源文件名的麻烦。

        【讨论】:

          【解决方案5】:

          *.vcxproj 文件是 msbuild 文件。因此,您只需在所有项目文件中获取不需要的属性并将其删除。然后把它放在你的属性表中。然后确保所有项目文件正确导入该属性表。

          这对于数百个文件来说可能非常乏味。我写了一个工具让这个交互:

          https://github.com/chris1248/MsbuildRefactor

          【讨论】:

            猜你喜欢
            • 2012-10-25
            • 1970-01-01
            • 1970-01-01
            • 2011-04-11
            • 2023-03-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-03
            相关资源
            最近更新 更多