Решения Visual Studio / Несколько проектов :Как эффективно распространять свойства проекта среди нескольких проектов на C ++
-
02-07-2019 - |
Вопрос
Я работаю с решением Visual Studio 2005 C ++, которое включает в себя несколько проектов (около 30).Исходя из моего опыта, часто становится раздражающим поддерживать все свойства проектов (т. Е. включать путь, путь к библиотеке, связанные библиотеки, параметры генерации кода, ...), поскольку вам часто приходится нажимать на каждый проект, чтобы изменить их.Ситуация становится еще хуже, когда у вас есть несколько конфигураций (Debug, Release, 64-разрядный релиз, ...).
Примеры из реальной жизни:
- Предположим, вы хотите использовать новую библиотеку, и вам нужно добавить путь включения к этой библиотеке во все проекты.Как вы избежите необходимости редактировать свойства каждого проекта?
- Предположим, вы хотите протестировать новую версию library (скажем, бета-версию 2.1), чтобы вам нужно было быстро изменить пути включения / путь к библиотеке / связанную библиотеку для набора проектов?
Примечания:
- Я знаю, что можно выбрать несколько проектов одновременно, затем щелкнуть правой кнопкой мыши и выбрать "свойства".Однако этот метод работает только для свойств, которые уже были точно идентичны для разных проектов :вы не можете использовать его для добавления включаемого пути к набору проектов, которые использовали другой включаемый путь.
- Я также знаю, что можно глобально изменять параметры среды (Инструменты / Параметры / Проект и решения / Каталоги), однако это не настолько удовлетворительно, поскольку оно не может быть интегрировано в SCM
- Я также знаю, что к решениям можно добавить "Конфигурации".Это не помогает, так как создает другой набор свойств проекта для поддержания
- Я знаю, что codegear C ++ Builder 2009 предлагает жизнеспособный ответ на эту потребность с помощью так называемых "наборов опций", которые могут наследоваться несколькими проектами (я использую как Visual Studio, так и C ++ Builder, и я все еще думаю, что C ++ Builder превосходит некоторые аспекты по сравнению с Visual Studio)
- Я ожидаю, что кто-нибудь предложит "autconf", такой как CMake, однако возможно ли импортировать файлы vcproj в такой инструмент?
Решение
Я думаю, вам нужно изучить файлы свойств, т.е. *.вспросы (старше) или *.реквизит (последняя версия)
Ты делай необходимо добавить файл свойств вручную к каждому проекту, но как только это будет сделано, у вас останется несколько проектов, кроме одного .[vs] файл props.Если вы измените свойства, все проекты унаследуют новые настройки.
Другие советы
Мне часто нужно сделать что-то подобное, поскольку я ссылаюсь на статические библиотеки времени выполнения.Я написал программу, которая сделает это за меня.Он в основном сканирует все подкаталоги любого пути, который вы ему укажете, и идентифицирует любые найденные файлы .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;
}
}
}
Как было предложено, вам следует просмотреть таблицы свойств (они же файлы .vsprops).
Я написал очень краткое введение к этой функции здесь.
Да, я бы определенно предложил использовать СМейк.CMake - лучший инструмент (думаю, я действительно перепробовал их все), который может генерировать файлы проекта Studio.
У меня также возникла проблема с преобразованием существующих файлов .vcproj в CMakeLists.txt, и я написал Ruby-скрипт который берет на себя большую часть преобразования.Скрипт не обрабатывает такие вещи, как шаги после сборки и тому подобное, поэтому необходима некоторая настройка, но это избавит вас от необходимости извлекать все имена исходных файлов из файлов .vcproj.
* файлы .vcxproj - это файлы msbuild.Таким образом, вы просто берете свойство, которое вам не нужно во всех файлах вашего проекта, и удаляете его.Затем внесите это в свой список свойств.Затем убедитесь, что все файлы проектов правильно импортируют эту таблицу свойств.
Это может быть невероятно утомительно для сотен файлов.Я написал инструмент, делающий это интерактивным: