Visual Studio Solutions / Multiple project: jak skutecznie propagować właściwości projektu wśród kilku projektów C++

Pracuję nad rozwiązaniem Visual Studio 2005 C++, które zawiera wiele projektów (około 30). Opierając się na moim doświadczeniu, często denerwujące staje się utrzymywanie wszystkich właściwości projektów (np. ścieżki, ścieżki lib, linked libs, opcje generowania kodu, ...), ponieważ często trzeba kliknąć każdy projekt, aby je zmodyfikować. Sytuacja staje się jeszcze gorsza, gdy masz wiele konfiguracji (Debug, Release, Release 64 bits, ...).

Real life przykłady:

  • Załóżmy, że chcesz użyć nowej biblioteki i musisz dodać ścieżkę dołączania do tej biblioteki do wszystkich projektów. Jak uniknąć konieczności edycji właściwości każdego projektu?
  • Załóżmy, że chcesz przetestować nową wersję biblioteki (powiedzmy wersję 2.1 beta), aby szybko zmienić ścieżki dołączania / ścieżkę biblioteki / bibliotekę połączoną dla zestawu projektów?

Uwagi:

  • zdaję sobie sprawę, że możliwe jest wybranie wielu projekty na raz, a następnie kliknij prawym przyciskiem myszy i wybierz "Właściwości". Jednak ta metoda działa tylko dla właściwości, które były już dokładnie identyczne dla różnych projektów : nie można jej użyć w celu dodania ścieżki include do zestawu projektów, które używały innej ścieżki include.
  • wiem też, że można modyfikować globalnie opcje środowiska (Narzędzia/Opcje/projekt i rozwiązania / katalogi), jednak nie jest to aż tak satysfakcjonujące, ponieważ nie można go zintegrować z SCM
  • Wiem też, że do rozwiązania można dodać "konfiguracje". Nie pomaga, ponieważ sprawia, że inny zestaw właściwości projektu do utrzymania
  • wiem, że codegear C++ Builder 2009 oferuje realną odpowiedź na tę potrzebę poprzez tzw. "zestawy opcji", które mogą być dziedziczone przez kilka projektów (używam zarówno Visual Studio, jak i C++ Builder, i nadal uważam, że C++ Builder rządzi się pewnymi aspektami w porównaniu do Visual Studio) {]}
  • spodziewam się, że ktoś zaproponuje "autconf" taki jak CMake, jednak czy można importować pliki vcproj do takiego narzędzia?
Author: Ian Ringrose, 2008-09-21

5 answers

Myślę, że należy zbadać pliki właściwości, tj. *.vsprops (starszy) lub *.rekwizyty (najnowsze)

Ty czy musisz ręcznie dodać plik właściwości do każdego projektu, ale gdy to zrobisz, masz wiele projektów, ale jeden .[vs]plik rekwizytów. Jeśli zmienisz właściwości, wszystkie projekty odziedziczą nowe ustawienia.

 30
Author: quamrana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-06 08:38:54

Często muszę zrobić coś podobnego, ponieważ łączę się ze statycznymi bibliotekami uruchomieniowymi. Napisałem program, który zrobi to za mnie. Zasadniczo skanuje wszystkie podkatalogi dowolnej ścieżki i identyfikatorów.pliki vcproj znajduje. Następnie jeden po drugim, otwiera je modyfikuje je i zapisuje. Ponieważ używam go tylko rzadko, ścieżka jest ciężko zakodowana, ale myślę, że będziesz w stanie dostosować go jak chcesz.

Innym podejściem jest uświadomienie sobie, że pliki projektu Visual Studio są po prostu Pliki XML i mogą być manipulowane za pomocą ulubionej klasy XML. Zrobiłem coś przy użyciu C#'S XmlDocument do aktualizacji katalogów include, gdy było dużo katalogów include, których nie chciałem wpisywać. :)

Dołączam oba przykłady. Będziesz musiał zmodyfikować je do własnych potrzeb, ale te powinny ci pomóc.

To jest wersja 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());
}

To jest wersja C#. Smacznego...

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;
        }
    }
}
 17
Author: Jere.Jones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-05 19:39:53

Zgodnie z sugestią, należy spojrzeć na arkusze właściwości (aka .pliki vsprops).
Napisałem bardzo krótkie wprowadzenie do tej funkcji tutaj .

 6
Author: Xavier Nodet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-28 13:29:21

Tak, zdecydowanie sugerowałbym użycie CMake . CMake to najlepsze narzędzie (chyba wypróbowałem je wszystkie), które może generować pliki projektów studyjnych.

Miałem też problem konwersji istniejących .pliki vcproj do CMakeLists.txt, a ja napisałem Ruby-script który zajmuje się większością konwersji. Skrypt nie obsługuje takich czynności, jak kroki po kompilacji itp., więc konieczne jest pewne dostosowanie, ale zaoszczędzi ci kłopotów z ciągnięciem wszystkich nazw plików źródłowych z ... pliki vcproj.

 3
Author: JesperE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-01 11:00:10

*.pliki vcxproj są pliki msbuild. Więc po prostu bierzesz właściwość, której nie chcesz we wszystkich plikach projektu i usuwasz ją. Następnie umieść to w swoim arkuszu nieruchomości. Następnie upewnij się, że wszystkie pliki projektów poprawnie zaimportują ten arkusz właściwości.

To może być niezwykle uciążliwe dla setek plików. Napisałem narzędzie make this interactive:

Https://github.com/chris1248/MsbuildRefactor

 0
Author: C Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-19 17:35:10