40

複数のプロジェクト (約 30) を含む Visual Studio 2005 C++ ソリューションを使用しています。私の経験に基づくと、プロジェクトのすべてのプロパティ (つまり、インクルード パス、ライブラリ パス、リンクされたライブラリ、コード生成オプションなど) を維持するのは煩わしいことがよくあります。それらを変更します。複数の構成 (デバッグ、リリース、リリース 64 ビットなど) がある場合、状況はさらに悪化します。

実際の例:

  • 新しいライブラリを使用する必要があり、このライブラリへのインクルード パスをすべてのプロジェクトに追加する必要があるとします。各プロジェクトのプロパティを編集する必要がないようにするにはどうすればよいでしょうか?
  • プロジェクト セットのインクルード パス / ライブラリ パス / リンクされたライブラリをすばやく変更する必要があるため、新しいバージョンのライブラリ (バージョン 2.1beta など) をテストしたいとしますか?

ノート:

  • 一度に複数のプロジェクトを選択し、右クリックして「プロパティ」を選択できることは承知しています。ただし、この方法は、異なるプロジェクトですでに完全に同一であるプロパティに対してのみ機能します。異なるインクルード パスを使用していた一連のプロジェクトにインクルード パスを追加するために使用することはできません。
  • また、環境オプション (ツール/オプション/プロジェクトとソリューション/ディレクトリ) をグローバルに変更できることも知っていますが、SCM に統合できないため、満足できるものではありません。
  • また、ソリューションに「構成」を追加できることも知っています。維持する別のプロジェクト プロパティのセットを作成するため、役に立ちません。
  • 私は、codegear C++ Builder 2009 が、いくつかのプロジェクトに継承できる、いわゆる「オプション セット」を通じて、このニーズに対する実行可能な答えを提供することを知っています (私は Visual Studio と C++ Builder の両方を使用していますが、C++ Builder は特定の側面で優れていると考えています)。 Visual Studio へ)
  • 誰かが CMake などの「autconf」を提案することを期待していますが、vcproj ファイルをそのようなツールにインポートすることは可能ですか?
4

5 に答える 5

32

プロパティ ファイル、つまり*.vsprops (古い) または*.props (最新)を調査する必要があると思います。

プロパティ ファイルを各プロジェクトに手動で追加する必要がありますが、それが完了すると、複数のプロジェクトが作成されますが、.[vs]props ファイルは 1 つになります。プロパティを変更すると、すべてのプロジェクトが新しい設定を継承します。

于 2008-09-21T18:32:40.373 に答える
17

静的ランタイム ライブラリにリンクしているので、よく似たようなことをする必要があります。私はそれを行うためのプログラムを書きました。基本的に、指定したパスのすべてのサブディレクトリをスキャンし、見つかった .vcproj ファイルを識別します。次に、それらを1つずつ開き、変更して保存します。めったに使わないのでパスはハードコーディングしていますが、お好みで調整していただけると思います。

もう 1 つのアプローチは、Visual Studio プロジェクト ファイルが単純な XML ファイルであり、好みの XML クラスで操作できることを理解することです。入力したくないインクルードディレクトリがたくさんXmlDocumentあるときに、インクルードディレクトリを更新するためにC#を使用して何かをしました。:)

両方の例を含めています。独自のニーズに合わせて変更する必要がありますが、これらを使用して開始する必要があります。

これは 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;
        }
    }
}
于 2008-09-21T18:41:56.747 に答える
6

提案されているように、プロパティ シート (別名 .vsprops ファイル) を確認する必要があります。この機能の簡単な紹介をここ
に書きました。

于 2009-08-28T13:29:21.423 に答える
3

はい、 CMakeを使用することをお勧めします。CMake は、Studio プロジェクト ファイルを生成できる最高のツールです (実際にすべて試したことがあると思います)。

また、既存の .vcproj ファイルを CMakeLists.txt に変換するという問題もあり、ほとんどの変換を処理するRuby スクリプトを作成しました。スクリプトはビルド後の手順などを処理しないため、微調整が必​​要ですが、.vcproj ファイルからすべてのソース ファイル名を取得する手間が省けます。

于 2008-09-21T18:35:28.430 に答える