Visual Studio 솔루션/다중 프로젝트:여러 C++ 프로젝트 간에 프로젝트 속성을 효과적으로 전파하는 방법

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

문제

저는 여러 프로젝트(약 30개)가 포함된 Visual Studio 2005 C++ 솔루션을 사용하여 작업하고 있습니다.내 경험에 따르면, 프로젝트의 모든 속성(예: 경로, lib 경로, 연결된 libs, 코드 생성 옵션 등 포함)을 유지하는 것이 짜증나는 경우가 많습니다. 수정하세요.여러 구성(디버그, 릴리스, 릴리스 64비트 등)이 있는 경우 상황은 더욱 악화됩니다.

실제 사례:

  • 새 라이브러리를 사용하려고 하며 모든 프로젝트에 이 라이브러리에 대한 포함 경로를 추가해야 한다고 가정합니다.모든 프로젝트의 속성을 편집하지 않으려면 어떻게 해야 합니까?
  • 일련의 프로젝트에 대한 포함 경로/라이브러리 경로/링크된 라이브러리를 신속하게 변경해야 하기 위해 새 버전의 라이브러리(예: 버전 2.1베타)를 테스트하고 싶다고 가정해 보겠습니다.

노트:

  • 한 번에 여러 프로젝트를 선택한 다음 마우스 오른쪽 버튼을 클릭하고 "속성"을 선택하는 것이 가능하다는 것을 알고 있습니다.그러나 이 방법은 다른 프로젝트에서 이미 정확히 동일한 속성에 대해서만 작동합니다.다른 포함 경로를 사용하고 있던 프로젝트 집합에 포함 경로를 추가하기 위해 이를 사용할 수 없습니다.
  • 또한 환경 옵션(도구/옵션/프로젝트 및 솔루션/디렉터리)을 전역적으로 수정할 수 있다는 것도 알고 있지만 SCM에 통합할 수 없기 때문에 만족스럽지 않습니다.
  • 또한 솔루션에 "구성"을 추가할 수 있다는 것도 알고 있습니다.유지 관리할 또 다른 프로젝트 속성 세트를 만들기 때문에 도움이 되지 않습니다.
  • 나는 codegear C++ Builder 2009가 여러 프로젝트에서 상속될 수 있는 소위 "옵션 세트"를 통해 이러한 요구에 대한 실행 가능한 대답을 제공한다는 것을 알고 있습니다. Visual Studio로)
  • 누군가가 CMake와 같은 "autconf"를 제안할 것으로 예상합니다. 그러나 vcproj 파일을 그러한 도구로 가져올 수 있습니까?
도움이 되었습니까?

해결책

속성 파일을 조사해야 한다고 생각합니다. *.vsprops (이전) 또는 *.소품 (최신)

하다 각 프로젝트에 속성 파일을 수동으로 추가해야 하지만, 완료되면 프로젝트는 여러 개 있지만 .[vs]props 파일은 하나입니다.속성을 변경하면 모든 프로젝트가 새 설정을 상속합니다.

다른 팁

정적 런타임 라이브러리에 연결하기 때문에 비슷한 작업을 수행해야 하는 경우가 많습니다.나는 나를 위해 프로그램을 작성했습니다.기본적으로 사용자가 제공한 경로의 모든 하위 디렉터리를 검색하고 찾은 모든 .vcproj 파일의 ID를 지정합니다.그런 다음 하나씩 열어서 수정하고 저장합니다.거의 사용하지 않기 때문에 경로가 하드코딩되어 있지만 원하는대로 조정하시면 될 것 같습니다.

또 다른 접근 방식은 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;
        }
    }
}

제안된 대로 속성 시트(일명 .vsprops 파일)를 살펴봐야 합니다.
나는 이 기능에 대해 매우 짧은 소개를 썼습니다. 여기.

예, 꼭 사용하는 것이 좋습니다. CMake.CMake는 Studio 프로젝트 파일을 생성할 수 있는 최고의 도구입니다(실제로 모두 시도해 본 것 같습니다).

또한 기존 .vcproj 파일을 CMakeLists.txt로 변환하는 문제가 있었고 다음을 작성했습니다. 루비 스크립트 대부분의 변환을 처리합니다.스크립트는 빌드 후 단계 등을 처리하지 않으므로 약간의 조정이 필요하지만 .vcproj 파일에서 모든 소스 파일 이름을 가져오는 번거로움을 덜 수 있습니다.

*.vcxproj 파일은 msbuild 파일입니다.따라서 모든 프로젝트 파일에서 원하지 않는 속성을 선택하여 삭제하면 됩니다.그런 다음 속성 시트에 입력합니다.그런 다음 모든 프로젝트 파일이 해당 속성 시트를 올바르게 가져왔는지 확인하십시오.

수백 개의 파일에 대해 이는 엄청나게 지루할 수 있습니다.나는 이것을 대화형으로 만드는 도구를 작성했습니다.

https://github.com/chris1248/MsbuildRefactor

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top