حلول الاستوديو المرئي / مشروع متعدد :كيفية نشر خصائص المشروع بشكل فعال بين العديد من مشاريع C++

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

سؤال

أنا أعمل باستخدام حل Visual Studio 2005 C++ الذي يتضمن مشاريع متعددة (حوالي 30).بناءً على تجربتي، غالبًا ما يصبح الحفاظ على جميع خصائص المشاريع أمرًا مزعجًا (على سبيل المثال، تضمين المسار، مسار lib، libs المرتبطة، خيارات إنشاء التعليمات البرمجية، ...)، حيث يتعين عليك غالبًا النقر فوق كل مشروع من أجل تعديلها.يصبح الوضع أسوأ عندما يكون لديك تكوينات متعددة (Debug، Release، Release 64 bits، ...).

أمثلة من الحياة الواقعية:

  • افترض أنك تريد استخدام مكتبة جديدة، وتحتاج إلى إضافة مسار التضمين إلى هذه المكتبة لجميع المشاريع.كيف ستتجنب الاضطرار إلى تعديل خصائص كل مشروع؟
  • افترض أنك تريد اختبار إصدار جديد من المكتبة (على سبيل المثال الإصدار 2.1beta) بحيث تحتاج إلى تغيير مسارات التضمين/مسار المكتبة/المكتبة المرتبطة بسرعة لمجموعة من المشاريع؟

ملحوظات:

  • أدرك أنه من الممكن تحديد مشاريع متعددة في وقت واحد، ثم انقر بزر الماوس الأيمن واختر "خصائص".ومع ذلك، فإن هذه الطريقة تعمل فقط مع الخصائص التي كانت متطابقة تمامًا مع المشاريع المختلفة:لا يمكنك استخدامه لإضافة مسار تضمين إلى مجموعة من المشاريع التي كانت تستخدم مسار تضمين مختلف.
  • أعلم أيضًا أنه من الممكن تعديل خيارات البيئة عالميًا (الأدوات/الخيارات/المشروع والحلول/الدلائل)، إلا أن ذلك ليس مرضيًا لأنه لا يمكن دمجه في SCM
  • أعلم أيضًا أنه يمكن إضافة "التكوينات" إلى الحلول.إنه لا يساعد لأنه يجعل مجموعة أخرى من خصائص المشروع يجب الحفاظ عليها
  • أعلم أن برنامج Codegear C++ Builder 2009 يقدم إجابة قابلة للتطبيق لهذه الحاجة من خلال ما يسمى "مجموعات الخيارات" التي يمكن أن ترثها العديد من المشاريع (أستخدم كلاً من Visual Studio وC++ Builder، وما زلت أعتقد أن C++ Builder يتفوق في جوانب معينة بالمقارنة إلى فيجوال ستوديو)
  • أتوقع أن يقترح شخص ما "autconf" مثل CMake، ولكن هل من الممكن استيراد ملفات vcproj إلى مثل هذه الأداة؟
هل كانت مفيدة؟

المحلول

أعتقد أنك بحاجة إلى التحقق من ملفات الخصائص، أي. *.vsprops (كبار السن) أو *.الدعائم (أحدث)

أنت يفعل تحتاج إلى إضافة ملف الخصائص يدويًا إلى كل مشروع، ولكن بمجرد الانتهاء من ذلك، سيكون لديك مشاريع متعددة، ولكن ملف .[vs]props واحد.إذا قمت بتغيير الخصائص، فسترث كافة المشاريع الإعدادات الجديدة.

نصائح أخرى

غالبًا ما أحتاج إلى القيام بشيء مماثل لأنني أقوم بالارتباط بمكتبات وقت التشغيل الثابتة.لقد كتبت برنامجًا للقيام بذلك من أجلي.فهو يقوم بشكل أساسي بفحص جميع الدلائل الفرعية لأي مسار تقدمه له ويحدد أي ملفات .vcproj يجدها.ثم يفتحها واحدًا تلو الآخر ويعدلها ويحفظها.نظرًا لأنني نادرًا ما أستخدمه، فإن المسار مشفر بشكل صارم، ولكن أعتقد أنك ستتمكن من تعديله بالطريقة التي تريدها.

هناك طريقة أخرى تتمثل في إدراك أن ملفات Visual Studio Project هي ببساطة ملفات 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