Решения Visual Studio / Несколько проектов: как эффективно распространять свойства проекта среди нескольких проектов C++

Я работаю с решением Visual Studio 2005 C++, которое включает несколько проектов (около 30). Исходя из моего опыта, часто становится утомительно поддерживать все свойства проектов (то есть включать путь, путь к библиотеке, связанные библиотеки, параметры генерации кода и т. д.), Поскольку вам часто приходится щелкать каждый проект, чтобы изменить их. Ситуация становится еще хуже, когда у вас есть несколько конфигураций (Debug, Release, Release 64 bit, ...).

Примеры из реальной жизни:

  • Предположим, вы хотите использовать новую библиотеку, и вам нужно добавить путь включения к этой библиотеке во все проекты. Как вы избежите необходимости редактировать свойства каждого проекта?
  • Предположим, вы хотите протестировать новую версию библиотеки (скажем, версию 2.1beta), так что вам нужно быстро изменить пути включения / путь к библиотеке / связанную библиотеку для набора проектов?

Примечания:

  • Я знаю, что можно выбрать несколько проектов одновременно, затем щелкнуть правой кнопкой мыши и выбрать «Свойства». Однако этот метод работает только для свойств, которые уже были точно идентичны для разных проектов: вы не можете использовать его для добавления пути включения к набору проектов, которые использовали другой путь включения.
  • Я также знаю, что можно глобально изменить параметры среды (Инструменты / Параметры / Проект и решения / Каталоги), однако это не так хорошо, поскольку его нельзя интегрировать в SCM.
  • Я также знаю, что к решениям можно добавить «Конфигурации». Это не помогает, поскольку заставляет другой набор свойств проекта поддерживать
  • Я знаю, что codegear C++ Builder 2009 предлагает жизнеспособный ответ на эту потребность с помощью так называемых «наборов параметров», которые могут быть унаследованы несколькими проектами (я использую как Visual Studio, так и C++ Builder, и я все еще думаю, что C++ Builder по некоторым аспектам по сравнению в Visual Studio)
  • Я ожидаю, что кто-то предложит "autconf", такой как CMake, однако можно ли импортировать файлы vcproj в такой инструмент?
blog.gockelhut.com/2009/11/…
danijar 19.11.2013 17:46
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
40
1
19 442
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Я думаю, вам нужно исследовать файлы свойств, то есть * .vsprops (более ранний) или *.реквизит (последний)

Вам делать нужно добавить файл свойств вручную в каждый проект, но как только это будет сделано, у вас будет несколько проектов, но один файл. [Vs] props. Если вы измените свойства, все проекты наследуют новые настройки.

Да, я определенно предлагаю использовать CMake. CMake - лучший инструмент (я думаю, я действительно пробовал их все), который может генерировать файлы проектов Studio.

У меня также была проблема с преобразованием существующих файлов .vcproj в CMakeLists.txt, и я написал Ruby-скрипт, который выполняет большую часть преобразования. Сценарий не обрабатывает такие вещи, как шаги после сборки и тому подобное, поэтому необходимы некоторые настройки, но он избавит вас от хлопот по извлечению всех имен исходных файлов из файлов .vcproj.

Мне часто нужно делать что-то подобное, поскольку я связываюсь со статическими библиотеками времени выполнения. Я написал программу, которая сделает это за меня. Он в основном сканирует все подкаталоги любого пути, который вы ему указываете, и идентифицирует все найденные файлы .vcproj. Затем один за другим он их открывает, изменяет и сохраняет. Поскольку я использую его редко, путь к нему жестко запрограммирован, но я думаю, вы сможете настроить его так, как вам нравится.

Другой подход - понять, что файлы проекта 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;
        }
    }
}

Как было предложено, вам следует изучить таблицы свойств (также известные как файлы .vsprops) .
Я написал очень короткое введение в эту функцию здесь.

Файлы * .vcxproj - это файлы msbuild. Итак, вы просто берете свойство, которое вам не нужно, во всех файлах вашего проекта и удаляете его. Затем поместите его в свой лист свойств. Затем убедитесь, что все файлы проектов правильно импортируют эту страницу свойств.

Для сотен файлов это может быть невероятно утомительно. Я написал инструмент, чтобы сделать это интерактивным:

https://github.com/chris1248/MsbuildRefactor

Другие вопросы по теме