Zwei Projekte in einem CMake zusammenfassen



  • Moin,

    Ich nutze folgende Umgebung:

    • Windows 11
    • Visual Studio 2022
    • CMake
    • Sprache C++

    Ich nutze eine relativ umfangreiche Library:
    https://github.com/mz-automation/libiec61850.git

    Diese lib lässt sich unter MVS mit dem MSV-Compiler und CMake fehlerfrei kompilieren.
    Auch die Einbindung in MVS per CMake -GUI läuft problemlos.

    Die Library ist eine reine Konsolenanwendung, daher möchte ich eine eigene GUI schreiben die Teile der Library nutzt.
    Als GUI habe ich mir das Qt-Framework ausgesucht, da ich dieses von Python her kenne und es einfach zu nutzen ist.
    Auch das kompilieren mit CMake aus MSV heraus läuft problemlos.

    Bei Problem ist jetzt eine simple HelloWorld App mit einem Fenster zu schreiben in der auch die Library eingebunden ist und alles mit einem CMake kompilierbar ist.

    Wer kann mir bei dem zusammenfügen der CmakeLists.txt helfen?
    Vielleicht sogar selber bei sich mal testen. Da beides separat voneinander läuft kann doch der letzte Schritt kein großes Ding mehr sein, so hoffe ich zumindest...

    Leider habe ich keinen Plan von CMake....

    Gruß
    Pf@nne



  • Wo ist genau das Problem? Welche Fehlermeldung? Vlt. mal die CMakeLists.txt sharen?

    @Pf-nne sagte in Zwei Projekte in einem CMake zusammenfassen:

    Leider habe ich keinen Plan von CMake....

    Vlt. zumindest mal ein bisschen basics drauf schaffen? Wenn du wirklich gar kein Plan drauf hast, läuft es ja darauf hinaus, dass man dir ne fertige Lösung hinknallt. Das Forum soll aber Hilfe zur Selbsthilfe sein 🙂

    Zum Beispiel auf die schnell sah: https://hsf-training.github.io/hsf-training-cmake-webpage/04-targets/index.html ganz vernünftig aus.

    Wichtigste ist imo das man rafft was Targets sind.

    Um fremde Libraries einzubinden, hast du zwei Möglichkeiten:

    Der erste downloaded die Library in dein build verzeichnis und buildet sie als Teil von deinem Build Prozess. Beim zweiten musst du die Library auf deinem System schon installiert haben.
    FetchContent tendiert dazu alles einfacher zu machen imo.
    Bei beiden bist du aber etwas drauf angewiesen, dass die Library modernes gutes CMake selber verwendet, sonst kann es schnell ätzend werden.



  • Moin,

    ich habe mir mal ein paar CMake Tutorials reingezogen.
    Ich glaube aber mein Problem setzt früher an...

    Mir fehlt gerade noch das Grundverständnis wie ich diese, für meine Verhältnisse, recht komplexe Library in mein QT-Widget einbinde.

    Ich habe mal mein QT-Widget ins Git hochgeladen:
    https://github.com/Pfannex/IEC61850_Qt.git
    Die benötigte Library liegt hier:
    https://github.com/Pfannex/libiec61850.git

    Im Grunde möchte ich jetzt das "libiec61850\examples\iec61850_client_example2" in mein Qt-Widget einbinden.
    Das example2 ist ein einfacher Client der sich mit einem passenden Server verbinden kann.
    Ich habe diesen Verbindungsteilt mal in mein Widget kopiert:

    mainwindow.cpp

    void MainWindow::on_pushButton_clicked() {
        
        QString txt = "HelloWorld!";
    
        /*
        IedClientError error;
    
        IedConnection con = IedConnection_create();
    
        IedConnection_connect(con, &error, "localhost", 102);
    
        if (error == IED_ERROR_OK) {
            txt = "connected";
            IedConnection_close(con);
        }
        else {
            txt = "failed to connect";
        }
    
        IedConnection_destroy(con);
        */
    
        QMessageBox::about(this, "Title", txt);
    }
    

    Mir ist jetzt aber überhaupt nicht klar wie ich jetzt die Library in mein Widget einbinde.
    Die Library besitzt ja nach dem compilieren *.lib, *.dll und *.exe Files.
    Im iec61850_client_example2 wird lediglich der Header #include "iec61850_client.h" includiert,
    das könnte ich im Widget ja auch machen, nur wie weiter, in iec61850_client.h werden wiederum weitere Header inkludiert.

    Ich hätte jetzt die komplette Library in meinem Widget unter:
    IEC61850_Qt\examples\QtWidgets\Qt6CMake-main\lib\libiec61850
    kopiert, wobei es wahrscheinlich auch reicht nur auf irgend einen Speicherplatz zu zeigen, ggf. sogar das Git-Hub-repo.

    Nur wie binde ich diese lib korrekt ein?
    Mache ich das im MVS oder in CMake?

    Für Unterstützung bin ich dankbar!

    Gruß
    Marco



  • Hi,

    ja du bennenst da schon die richtigen Dinge grundsätzlich. Grundsätzlich können libraries statisch oder dynamisch sein. Im Falle einer statischen Library hast du (auf Windows!) *.lib Dateien. Im Falle einer dynamischen Library hast du sowohl *.lib Dateien (aber mit anderem Inhalt) als auch *.dll Dateien.
    Siehe hierzu: https://stackoverflow.com/a/913744/10764260

    *.exe solltest du nicht haben, das wären executables. Vlt. hat die Library aber Beispielprogramme etc. ... die gehören dann aber nicht zur lib.

    Diese Dateien brauchst du für den Linker. Darüber hinaus hast du ja noch die Header Files. Die müssen lediglich im Include Pfad sein und gefunden werden.

    Wenn du nen Compiler hast wie z.B. g++ oder whatever haben die gewisse Flags um genau das zu spezifizieren.

    Z.B. g++ -I/path/to/my/library/headers
    für den include pfad
    z.B. g++ -lmylibwithoutending
    zum linken einer lib

    Wie die genauen Befehle da jetzt sind, weiß ich jetzt auch nicht so genau. Das macht halt keiner von Hand. Aber ist gut grob zu wissen wie das eig. so funktioniert.

    Warum gibt es jetzt eig. CMake? -> Im wesentlichen um keine 1000 Konsolenbefehle einzugeben zum kompilieren und zu linken.

    CMake hat eine lange Historie. Und Dinge gingen früher anders als heute. Die Erfahrung ist, die meisten können weder CMake von damals noch von heute. Der verbleibende Großteil kann CMake von damals. Ein kleiner Teil kann CMake von heute.
    Und das ist auch der Hauptgrund, warum Libraries einbinden alles andere als trivial ist. Das mag einen vermutlich erstmal überraschen ^^ Aber liegt eben daran, dass viele Autoren von Libraries auch keinen Plan von CMake haben.

    Du solltest modernes CMake nutzen, das bedeutet mindestens (!) CMake 3.x. Und natürlich kommt es wie bei C++ nicht nur auf die Version drauf an, sondern das man es auch richtig nutzt.

    In CMake 3.x gibt es eine zentrale Abstraktion über alles und die nennt sich Target. Vorstellen kann man sich das wie ein Objekt mit Attributen etc.

    Die Hauptbefehle z.B. Erstellen von Targets sind add_executable(MyTarget) und add_library(MyTarget) um eine Executable oder Library zu erstellen. CMake erzeugt dann platformspezisch die entsprechenden Befehle zum kompilieren etc.

    Jetzt gibt es Befehle, um Attribute des Targets entsprechend zu befüllen. Die wichtigsten:

    • target_sources(MyTarget PRIVATE file1.cpp file2.cpp file3.cpp) -> Fügt deinem Target die C++ Dateien hinzu -> Alternativ kann man die sources auch direkt add_executable / add_library hinzufügen
    • target_include_directories(MyTarget PRIVATE path/to/my/headers path/to/other/headers) -> Fügt Verzeichnise hinzu, welche zum Suchen der Header files verwendet werden
    • target_link_libraries(MyTarget PRIVATE mylibrary1 mylibrary2) -> Linkt libraries zu deinem Target

    Pseudocode mäßig könnte dein Code also so aussehen)

    add_executable(MyQtApp) 
    target_sources(MyQtApp PRIVATE file1.cpp file2.cpp file3.cpp) 
    target_include_directories(MyQtApp PRIVATE path/to/header/files/of/library) 
    target_link_libraries(MyQtApp PRIVATE path/to/library/libraryname) 
    

    In der Praxis funktioniert es aber nicht so einfach, weil:

    • Du lauter systemspezifische Pfade hast
    • Es mit static / shared und Windows / Linux Libraries etc. doch ein bisschen komplexer ist

    CMake hat da eine bessere Möglichkeit. Mit target_link_libraries kann man nicht nur Libraries linken, sondern auch Targets! (Und das ist auch genau was man immer tun sollte)

    z.B. target_link_libraries(MyAppTarget PRIVATE MyLinkLibrary)

    Das tut mehrere Dinge:

    1. Das Target MyLinkLibrary wird automatisch gebaut
    2. Die darunter liegende Library wird gelinkt gegen deine App
    3. Alle Attribute des Targets, die mit PUBLIC gekenzeichnet sind werden vererbt

    Beispiel. Deine Library hat folgenden CMake Code

    add_library(LibraryTarget) 
    target_sources(LibraryTarget PRIVATE file1.cpp file2.cpp)
    target_include_directories(LibraryTarget PRIVATE path/to/header/directory)
    target_link_libraries(LibraryTarget PRIVATE libraryDependency1 libraryDependency2)
    // libraryDependency1 etc. sind wiederum wieder Targets
    

    und du versuchst jetzt diese zu linken mit

    add_executable(MyApp) 
    target_sources(MyApp PRIVATE file1.cpp file2.cpp)
    target_link_libraries(MyApp PRIVATE LibraryTarget
    

    dann bekommst du in aller Regel Fehlermeldungen. Nämlich zuerst

    • Kann er die Header von LibraryTarget nicht finden ... die sind nämlich PRIVATE und werden daher nicht in include pfad von MyApp gepackt
    • Kann er die Header von libraryDependency1 / libraryDependency2 nicht finden, welche in den Headern von LibraryTarget refernziert werden. Denn diese library wurden privat gelinkt. Dein Target MyApp erbt also auch nicht die Include Pfade von libraryDependency1 / libraryDependency2
    • Ggf. kriegst du auch Linker Error von libraryDependency1 / libraryDependency2, wenn diese dynamisch gelinkt sind, weil MyApp eben nicht mehr gegen diese linkt, weil sie Privat sind

    Die Faustregel ist daher: Library Include Directories und Dependencies auf Public. Selbes Szenario mit Public:

    add_library(LibraryTarget) 
    target_sources(LibraryTarget PRIVATE file1.cpp file2.cpp)
    target_include_directories(LibraryTarget PUBLIC path/to/header/directory)
    target_link_libraries(LibraryTarget PUBLIC libraryDependency1 libraryDependency2)
    
    add_executable(MyApp) 
    target_sources(MyApp PRIVATE file1.cpp file2.cpp)
    target_link_libraries(MyApp PRIVATE LibraryTarget)
    

    Läuft alles. MyApp erbt transitiv die Include Pfade von LibraryTarget und sogar von libraryDependency1 und libraryDependency2. Es linkt auch gegen alle Libraries, die benötigt werden. Inklusive gegen die dependency der dependency der dependency von deiner Library 😉

    Die Idee hier ist also die folgende: Jeder fütter seine Targets mit seinen Infos. Der Library Autor weiß ja am besten wo die header liegen also soll er auch den include Pfad schreiben.
    Und wenn du ne Dependency brauchst, dann linkst du nur gegen die Targets und bekommst alle benötigten Infos automatisch.

    So viel zur schönen Theorie und wie toll das alles klappen könnte zur Praxis.

    Wie kommst du an die Targets deiner Library? Im einfachsten Fall sieht das ganze so aus:

    Alternativ dazu kannst du FetchContent verwenden ... das macht im wesentlichen Schritt 1 und 2. Download der Library ins Build Verzeichnis und add_subdirectory mit dem Pfad, wo er es hin geschrieben hat.

    Es gibt nur bei vielen Libraries ein Problem. Sie nutzen nicht die neue Syntax mit PUBLIC / PRIVATE. Das siehst du z.B. hier https://github.com/mz-automation/libiec61850/blob/v1.5/src/CMakeLists.txt#L342 ... da steht weder ein PUBLIC noch ein PRIVATE.
    Außerdem sehe ich auf Anhieb auch nicht, ob die include directories richtig gesetzt sind.
    Das ist wie gesagt dann ein Fehler der Library und ist leider bei sehr vielen Library so der Fall.

    In dem Fall musst du dann eben ggf. selbst noch include directories hinzufügen (mit den selben Befehlen). Um es konkret zu sagen, müsste ich es selber testen. Dafür habe ich grade keine Zeit.

    Die Variante mit add_subdirectory ist in der Regel die einfachste. Es gibt auch die Möglichkeit wie du es gemacht hast, dass du die Library kompilisiert und (!) installiert . Dann geht dir der CMake Code mit den Target Definition entsprechend verloren.

    Deswegen gibt es solche Anweisungen https://github.com/mz-automation/libiec61850/blob/v1.5/src/CMakeLists.txt#L373. Die installieren neben den Header, dlls etc. auch CMake Code! GIbt dann meistens nen Order /cmake.

    Der Ablauf ist dann wie Folgt:

    Ob das wirklich so klappt hängt mal wieder davon ab, ob die Library alles richtig gemacht hast. Kann ich dir ohne es auszuprobieren auch nicht sagen.

    Meine Empfehlung wäre:

    Wenn das klappt, entscheide dich erstmal, ob du FetchContent oder FindPackage nutzen willst. Ich nutzte ausschließlich ersteres, aber es gibt Vor -und Nachteile von beidem. Abhängig davon würde ich es den "best-case" testen. Vlt. geht ja alles out of the box. Falls nicht, melde dich nochmal mit deinem konkreten Code, wo du schon mal gegen die Lib linkst etc. und gib uns die konkrete Fehlermeldung.

    Sorry, dass ich hier keinen 3 Zeile schreiben kann wie einfach man das alles zum laufen bringt. Die Realität ist, dass Dependency Management in C++ eine Katastrophe ist. Und man bei CMake im Grunde eine eigene Programmiersprache lernen muss. Mal so eben auf die schnelle ist da selten.
    So meine Einschätzung 😉 Wenn man es aber kann, dann ist CMake so wie C++ auch sehr mächtig 🙂



  • Moin Leon,

    Sorry für die späte Antwort, wir waren im Urlaub und sind gerade wieder eingetrudelt und
    vielen Dank, dass du dir die Mühe machst mir (extrem ausführlich!) unter die Arme zu greifen.

    Das Hauptproblem ist wahrscheinlich mein Basiswissen was die fachgerechte Programmierung und Buildgenerierung angeht. Ich bin eher Hobbyist, der zwar Programme schreiben kann, das jedoch aber eher auch auf einem "Hauptsache läuft Niveau"....
    @Leon0402 sagte in Zwei Projekte in einem CMake zusammenfassen:

    Und das ist auch der Hauptgrund, warum Libraries einbinden alles andere als trivial ist.
    ...
    Die Realität ist, dass Dependency Management in C++ eine Katastrophe ist. Und man bei CMake im Grunde eine eigene Programmiersprache lernen muss. Mal so eben auf die schnelle ist da selten.

    Genau das habe ich befürchtet.... Ich werde jetzt erstmal deine Ausführungen durcharbeiten, auf den ersten Blick sind da noch einige Fragen offen. Mein Vorschlag wäre, dass ich mein Vorgehen step by step hier Poste und "wir" dann schauen wie man es zusammen bekommt.

    Für mein Verständnis:
    Die libiec61850 ist eine Library die nach dem Build folgende Dateitypen bereitstellt:

    • *.h - Header für die Einbindung der Library-Code-Bausteine
    • *.lib - statische Library die beim kompilieren meines Codes mit implementiert wird.
    • *.dll - dynamische Library die zur Laufzeit genutzt wird und auch auf dem ausführenden Zielsystem vorhanden sein muss.
    • *.exe - ausführbare Programme von den beigefügten examples, direkt haben die nix mit der Library zu tun.

    Ich würde mich im ersten Schritt dafür entscheiden die Library in kompilierter Form statisch mit einzubinden.
    Hierzu muss ich herausfinden, welche *.h, *.lib und *.dll ich für meinen Code benötige.

    Die Einbindung der *.h erfolgt in meinem Code mit #include. Hier muss ich dafür sorgen, dass meine Programmierumgebung den Pfad zu allen benötigten Headern bekommt.
    Weiterhin muss ich mir ansehen, wie und auf welche *.lib / *.dll der Library-Beispielcode zugreift.

    Ist das vom Ansatz her erstmal nachvollziehbar?

    Gruß
    Marco



  • Ich habe jetzt in meinem Qt-Widget einen Ordner /lib angelegt und dort die vollständig kompilierte Library abgelegt.
    Im nächsten Schritt habe ich in meine mainwindow.h den Header #include "iec61850_client.h implementiert.
    Dies schmeißt beim kompilieren natürlich diverse Fehlermeldungen da er weder #include "iec61850_client.h" noch die in #include "iec61850_client.h" benötigten Header findet.
    Daher habe ich in den MVS-Debug-Einstellungen sämtliche Heder-Direktoriers mit angegeben:

    -Konfigurationseigenschaften/-VC++-Verzeichnisse/-Includerverzeichnisse

    ..\lib\libiec61850\config;
    ..\lib\libiec61850\src\common\inc;
    ..\lib\libiec61850\src\goose;
    ..\lib\libiec61850\src\sampled_values;
    ..\lib\libiec61850\hal\inc;
    ..\lib\libiec61850\src\hal\inc;
    ..\lib\libiec61850\src\iec61850\inc;
    ..\lib\libiec61850\src\iec61850\inc_private;
    (VC_IncludePath); ..\lib\libiec61850\src\mms\inc; ..\lib\libiec61850\src\mms\inc_private; (WindowsSDK_IncludePath);
    ..\lib\libiec61850\src\mms\iso_mms\asn1c
    ;..\lib\libiec61850\src\logging

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QString>
    #include <QMessageBox>
    
    #include "iec61850_client.h"
    #include <stdlib.h>
    #include <stdio.h>
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private slots:
        void on_pushButton_clicked();
    
    private:
        Ui::MainWindow *ui;
    };
    
    #endif // MAINWINDOW_H
    

    Mit auskommentierten Library-Funktionen in der mainwindow.cpp

    mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::on_pushButton_clicked() {
        
        QString txt = "HelloWorld!";
    
        /*
        IedClientError error;
    
        IedConnection con = IedConnection_create();
        
        IedConnection_connect(con, &error, "localhost", 102);
    
        if (error == IED_ERROR_OK) {
            txt = "connected";
            IedConnection_close(con);
        }
        else {
            txt = "failed to connect";
        }
    
        IedConnection_destroy(con);
        */
    
        QMessageBox::about(this, "Title", txt);
    }
    

    Lässt sich der Code kompilieren.

    will ich den Code nutzen und nehme einen Teil mit in den Code, erhalte ich folgenden Fehler:

    void MainWindow::on_pushButton_clicked() {
        
        QString txt = "HelloWorld!";
    
        IedClientError error;
    
        IedConnection con = IedConnection_create();
    
    ...    
    
    Schweregrad	Code	Beschreibung	Projekt	Datei	Zeile	Unterdrückungszustand
    Fehler	MSB6006	"link.exe" wurde mit dem Code 1120 beendet.	MY_PROJECT	C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.CppCommon.targets	1092	
    Fehler	LNK2019	Verweis auf nicht aufgelöstes externes Symbol "IedConnection_create" in Funktion ""private: void __cdecl MainWindow::on_pushButton_clicked(void)" (?on_pushButton_clicked@MainWindow@@AEAAXXZ)".	MY_PROJECT	C:\TEMP\Code\IEC61850_Qt\examples\QtWidgets\Qt6CMake-main\build\mainwindow.obj	1	
    Fehler	LNK1120	1 nicht aufgelöste Externe	MY_PROJECT	C:\TEMP\Code\IEC61850_Qt\examples\QtWidgets\Qt6CMake-main\build\Release\MY_PROJECT.exe	1	
    
    

    Das liegt jetzt vermutlich daran, das beim Bauen der Bezug zu den .lib/.dll fehlt.
    Jetzt muss ich auch erstmal herausfinden welche .lib/.dll benötigt werden.
    Fürs erste könnte ich auch die fertig kompilierte libiec61850 nach den libs durchsuchen und alle in einen zentralen Ordner kopieren.

    Also versuche ich jetzt die Libs mit ein das CMake zu bekommen?
    Oder müssen die Libs auch in MVS bekannt gemacht werden?

    Ich müsste jetzt an dem Punkt sein, denn du mir versucht hast oben näher zu bringen... denke ich?!



  • Erstmal kurze Rückfrage. Hast du die Library nur gebaut oder auch installiert? Bzw. kannst du hier vlt. mal posten, welche Befehle du da alles so eingetippt hast?

    Normaleweise sollte es grob so aussehen (Cmake 3.15+):

    // CMake Generator Phase -> Liest alles ein, erzeugt Build directory etc.
    $ cmake -B build  
    // Eig. kompilieren der library
    $ cmake --build build 
    // Installieren der Library auf dein System
    $ cmake --install build
    

    Beim letzen Schritt erzeugt er dir normalerweise auf deinem System einen Ordner, wo du alles drin findest ... die header Dateien, die Library Dateien etc.

    Vlt. kannst du das mal machen und ne zip davon teilen oder so, damit wir mal wissen was du da genau alles so hast.

    P.S. Die Befehle oben sind alle mit CMake und Cmake ruft immer das passende Tool unten drunter auf für deine Plattform und Konfiguration. Der präferierte Weg 🙂



  • @Leon0402 sagte in Zwei Projekte in einem CMake zusammenfassen:

    cmake --install build

    😱

    Das fehle mir....
    Ich habe "cmake --install build" durchlaufen lassen und unter "C:\Program Files\libiec61850" das Extrakt der Library erhalten. Ich habe den Inhalt mal mit in das Qt-Widget repo geladen.

    GitHub

    Jetzt muss man doch "nur noch" die richtigen Pfade in meinen Qt-Widget CMakeLists.txt setzen, oder?



  • OK, das einbinden der *.h aus dem Library install funktioniert:

    cmake_minimum_required(VERSION 3.14)
    
    if (WIN32)
        project(MY_PROJECT LANGUAGES CXX)
    elseif(UNIX)
        project(MY_PROJECT)
    endif()
    
    set(CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo" CACHE STRING "" FORCE)
    
    #======================= INCLUSION OF Qt =======================#
    set(CMAKE_INCLUDE_CURRENT_DIR ON)
    set(CMAKE_AUTOMOC ON)
    set(CMAKE_AUTOUIC ON)
    set(CMAKE_PREFIX_PATH $ENV{QTDIR})
    find_package(Qt6Core REQUIRED)
    find_package(Qt6Widgets REQUIRED)
    
    #=================== INCLUSION OF Project Files ====================#
    set(FORMS_DIR "${CMAKE_SOURCE_DIR}/forms")
    set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include")
    set(SOURCE_DIR "${CMAKE_SOURCE_DIR}/src")
    set(IEC61850_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/libiec61850/include/libiec61850")
    #set(LIB_DIR "${CMAKE_SOURCE_DIR}/libiec61850/lib")
    #set(BIN_DIR "${CMAKE_SOURCE_DIR}/libiec61850/bin")
    
    include_directories(${FORMS_DIR})
    include_directories(${INCLUDE_DIR})
    include_directories(${SOURCE_DIR})
    include_directories(${IEC61850_INCLUDE_DIR})
    
    file(GLOB_RECURSE SOURCES
        "${FORMS_DIR}/*.ui"
        "${FORMS_DIR}/*.qrc"
        "${INCLUDE_DIR}/*.h"
        "${SOURCE_DIR}/*.cpp"
        "${IEC61850_INCLUDE_DIR}/*.h"
    )
    
    #=================== SETUP EXECTUABLE ====================#
    # Enable debug logging on RELWITHDEBINFO configuration
    set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS
        $<$<CONFIG:RELWITHDEBINFO>:QT_MESSAGELOGCONTEXT>
    )
    
    # Add the forms directory to the AUTOUIC search paths
    set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_AUTOUIC_SEARCH_PATHS} ${FORMS_DIR})
    
    # Add the executable
    if (WIN32) 
        add_executable(MY_PROJECT WIN32 ${SOURCES})
    elseif(UNIX)
        add_executable(MY_PROJECT ${SOURCES})
    endif()
    
    # Add the target includes for MY_PROJECT 
    target_include_directories(MY_PROJECT PRIVATE ${FORMS_DIR})
    target_include_directories(MY_PROJECT PRIVATE ${INCLUDE_DIR})
    target_include_directories(MY_PROJECT PRIVATE ${SOURCE_DIR})
    target_include_directories(MY_PROJECT PRIVATE ${IEC61850_INCLUDE_DIR})
    
    #===================== LINKING LIBRARIES =======================#
    target_link_libraries(MY_PROJECT Qt6::Widgets)
    #target_link_libraries(MY_PROJECT ${LIB_DIR})
    #target_link_libraries(MY_PROJECT ${BIN_DIR})
    

    Jetz schaue ich mal wie ich die *.lib aus /lib und die *.dll aus /bin der Installation einbinden kann.
    Da hattest du mir ja oben schon viele Hinweise geschrieben.



  • Ja, sieht schon ganz gut aus. Die Zeile

    include_directories(${IEC61850_INCLUDE_DIR})

    brauchst du nicht. Bzw. generell solltest du jegliche include_directories aus deinem CMake verbannen. Das ist quasi sowas wie "globale includes", stattdessen (wie du es auch machst) mit target_include_directories auf Target ebene setzen.

    Jetzt muss du noch die Lib linken. Wie gesagt, es gibt statische und dynamische Libraries.

    • Statische Libraries werden in dein Programm mit reinkompiliert. Deine Executable enthält also die statische Library. Auf Windows haben diese die Endung .lib
    • Dynamische Libraries werden von deinem Programm zur Laufzeit referenziert. Diese bleiben als eigene Datei bestehen. Diese haben eine .dll Endung auf Windows. Wenn du dein Programm aufrufst, muss er die .dll Datei finden können. Entweder indem sie im PATH liegt oder im selben Verzeichnis etc. Während dem kompilieren bzw. linken muss der Linker auch wissen, welche Funktionen etc. von der dynamischen Lib stammen. Dazu gibt es auf Windows eine import library mit ebenfalls einer .lib Endung. Diese musst du also linken. Die dll muss nur zur Runtime dasein.

    Zwei Sachen zum Mitnehmen:

    • Wenn du auf Windows ne .lib Datei hast, weißt du zunächst nicht, ob es eine statische Library ist oder eine Import Library für ne dynamische Library -> Finde das am besten mal raus mit der Kommandozeile (https://stackoverflow.com/a/6403559/10764260)
    • Du musst in jedem Fall die .lib Datei linken 😉

    Meine Vermutung wäre mal, dass die iec61850.lib eine Import Lib ist, weil er dir ja ne passende iec61850.dll auch erstellt hat. Für die hal lib hast du scheinbar beides basierend auf dem Namen (die mit shared Import lib, die andere static).

    Ich würde dir dazu raten erstmal dir den Fehler anzuschauen, der aktuell kommt. Ist es ein Linker Fehler? Welche Funktion / Klasse fehlt? Stammt die aus iec61850?

    Falls ja -> Linken gegen iec61850.lib

    Bekommst du immer noch ein Fehler? Was fehlt jetzt? Stammt es zufällig aus der Hal Library (eine dependency von iec61850 vermute ich)?

    Falls ja -> Linken gegen hal-shared.lib (oder gegen hal.lib, wenn du statisch linken willst)

    Also schrittweise vorgehen, die Fehlermeldungen beobachten. Dann lernste auch wie das genau funktioniert 🙂



  • Moin!

    hal.lib und hal-shared.lib enthalten beide die selben Objekte und sind demnach auch libs und keine import libs, oder?

    PS C:\Program Files\libiec61850\lib> lib /list .\hal.lib
    Microsoft (R) Library Manager Version 14.32.31332.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    hal.dir\Release\socket_win32.obj
    hal.dir\Release\thread_win32.obj
    hal.dir\Release\file_provider_win32.obj
    hal.dir\Release\time.obj
    hal.dir\Release\serial_port_win32.obj
    hal.dir\Release\lib_memory.obj
    PS C:\Program Files\libiec61850\lib> lib /list .\hal-shared.lib
    Microsoft (R) Library Manager Version 14.32.31332.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    hal-shared.dir\Release\socket_win32.obj
    hal-shared.dir\Release\thread_win32.obj
    hal-shared.dir\Release\file_provider_win32.obj
    hal-shared.dir\Release\time.obj
    hal-shared.dir\Release\serial_port_win32.obj
    hal-shared.dir\Release\lib_memory.obj
    

    anders die iec61850.lib, hier kommen keine Objekte zum Vorschein.

    PS C:\Program Files\libiec61850\lib> lib /list .\iec61850.lib
    Microsoft (R) Library Manager Version 14.32.31332.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    iec61850.dll
    ...
    ...
    ...
    

    Ich würde dir dazu raten erstmal dir den Fehler anzuschauen, der aktuell kommt. Ist es ein Linker Fehler? Welche Funktion / Klasse fehlt? Stammt die aus iec61850?

    Schweregrad	Code	Beschreibung	Projekt	Datei	Zeile	Unterdrückungszustand
    Fehler	LNK2019	Verweis auf nicht aufgelöstes externes Symbol "IedConnection_create" in Funktion ""private: void __cdecl MainWindow::on_pushButton_clicked(void)" (?on_pushButton_clicked@MainWindow@@AEAAXXZ)".	MY_PROJECT	C:\TEMP\Code\IEC61850_Qt\examples\QtWidgets\Qt6CMake-main\build\mainwindow.obj	1	
    Fehler	LNK1120	1 nicht aufgelöste Externe	MY_PROJECT	C:\TEMP\Code\IEC61850_Qt\examples\QtWidgets\Qt6CMake-main\build\Release\MY_PROJECT.exe	1	
    

    Der Fehler ist scheinbar in dem Objekt "mainwindow.obj", hier befindet sich der hinzugefügte libiec61850-code:

        IedClientError error;
    
        IedConnection con = IedConnection_create();
    

    das ist Bestandteil der iec61850-library. Ohne diese beiden Zeilen läuft alles problemlos durch.

    Ich muss mir jetzt erstmal ansehen was du mit:

    Falls ja -> Linken gegen iec61850.lib

    meinst, hier habe ich definitiv noch eine mittelschwere Lücke.
    Ich habe ansatzweise verstanden wie ich auf ein include direktory verweise, so dass der Compiler diese auch findet.
    Das schein ja jetzt auch zu funktionieren.

    Was es mit dem linken gegen auf sich hat muss ich jetzt erstmal googeln....



  • OK, nach dem linken der iec61850.lib läuft es jetzt!!!

    cmake_minimum_required(VERSION 3.14)
    
    if (WIN32)
        project(MY_PROJECT LANGUAGES CXX)
    elseif(UNIX)
        project(MY_PROJECT)
    endif()
    
    set(CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo" CACHE STRING "" FORCE)
    
    #======================= INCLUSION OF Qt =======================#
    set(CMAKE_INCLUDE_CURRENT_DIR ON)
    set(CMAKE_AUTOMOC ON)
    set(CMAKE_AUTOUIC ON)
    set(CMAKE_PREFIX_PATH $ENV{QTDIR})
    find_package(Qt6Core REQUIRED)
    find_package(Qt6Widgets REQUIRED)
    
    #=================== INCLUSION OF Project Files ====================#
    set(FORMS_DIR "${CMAKE_SOURCE_DIR}/forms")
    set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include")
    set(SOURCE_DIR "${CMAKE_SOURCE_DIR}/src")
    set(IEC61850_INCLUDE_DIR "C:/Program Files/libiec61850/include/libiec61850")
    set(LIB_DIR "C:/Program Files/libiec61850/lib")
    
    include_directories(${FORMS_DIR})
    include_directories(${INCLUDE_DIR})
    include_directories(${SOURCE_DIR})
    
    file(GLOB_RECURSE SOURCES
        "${FORMS_DIR}/*.ui"
        "${FORMS_DIR}/*.qrc"
        "${INCLUDE_DIR}/*.h"
        "${SOURCE_DIR}/*.cpp"
        "${IEC61850_INCLUDE_DIR}/*.h"
    )
    
    #=================== SETUP EXECTUABLE ====================#
    # Enable debug logging on RELWITHDEBINFO configuration
    set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS
        $<$<CONFIG:RELWITHDEBINFO>:QT_MESSAGELOGCONTEXT>
    )
    
    # Add the forms directory to the AUTOUIC search paths
    set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_AUTOUIC_SEARCH_PATHS} ${FORMS_DIR})
    
    # Add the executable
    if (WIN32) 
        add_executable(MY_PROJECT WIN32 ${SOURCES})
    elseif(UNIX)
        add_executable(MY_PROJECT ${SOURCES})
    endif()
    
    # Add the target includes for MY_PROJECT 
    target_include_directories(MY_PROJECT PRIVATE ${FORMS_DIR})
    target_include_directories(MY_PROJECT PRIVATE ${INCLUDE_DIR})
    target_include_directories(MY_PROJECT PRIVATE ${SOURCE_DIR})
    target_include_directories(MY_PROJECT PRIVATE ${IEC61850_INCLUDE_DIR})
    
    #===================== LINKING LIBRARIES =======================#
    target_link_libraries(MY_PROJECT ${LIB_DIR}/iec61850.lib )
    target_link_libraries(MY_PROJECT ${LIB_DIR}/hal.lib )
    #target_link_libraries(MY_PROJECT ${LIB_DIR}/hal-shared.lib )
    target_link_libraries(MY_PROJECT Qt6::Widgets)
    

    Ist das jetzt hingefummelt oder eine mehr oder weniger saubere Lösung?

    Zumindest kann ich jetzt eigene Qt-Widgets schreiben, die auf die zentral abgelegte UND kompilierte lib zugreifen.
    Die iec61850.lib schien ja eine import lib zu sein, daher nutze ich wohl die iec61850.dll. Ich habe den /bin-Ordner mal umbenannt, es lässt sich aber immer noch alles sauber kompilieren, ich hätte jetzt erwartet, dass er mecker, weil die *.dll nicht mehr da ist.....



  • @Pf-nne sagte in Zwei Projekte in einem CMake zusammenfassen:

    Ist das jetzt hingefummelt oder eine mehr oder weniger saubere Lösung?

    hingefummelt 😉 Schon alleine deswegen, weil sie nur auf Windows läuft und bestimmte Pfade und Ordnerstrukturen vorraussetzt. Aber auch weil deine Library z.B. gegen HAL direkt linkt, obwohl du ja gar nicht HAL nutzt, sondern nur indirekt.

    Das kriegen wir sicher dann noch ein bisschen schöner zumindestens hin. Aber erstmal alles lauffähig etc. bekommen

    @Pf-nne sagte in Zwei Projekte in einem CMake zusammenfassen:

    Der Fehler ist scheinbar in dem Objekt "mainwindow.obj", hier befindet sich der hinzugefügte libiec61850-code:

    Ja richtig, wobei die entscheidene stelle in deiner Fehlermeldung ist nicht aufgelöstes externes Symbol "IedConnection_create". Der Rest gibt dir einen Hinweis wo du es findest. Mit wenig Fantasie sieht man da relativ leicht, dass das fehlende Symbol die IedConnection_create Methode ist. Also sprich eine Methode der Library wie du selbst schon erkannt hast. Soweit alles klar und erwartbar würde ich sagen.

    @Pf-nne sagte in Zwei Projekte in einem CMake zusammenfassen:

    Die iec61850.lib schien ja eine import lib zu sein, daher nutze ich wohl die iec61850.dll. Ich habe den /bin-Ordner mal umbenannt, es lässt sich aber immer noch alles sauber kompilieren, ich hätte jetzt erwartet, dass er mecker, weil die *.dll nicht mehr da ist.....

    Die .dll benötigt man wie gesagt zur Runtime. Daher, wenn du deine Applikation dann ausführst. Hast du das mal gemacht? Deine .exe gestartet?



  • Scheinbar nutz er die:

    MY_PROJECT.exe" (Win32): "C:\TEMP\Code\libiec61850\build\src\Release\iec61850.dll" geladen. Das Modul wurde ohne Symbole erstellt.
    

    Aber warum nicht die Version die im "install-ordner" von cmake --install abgelegte?
    Auf jeden Fall nutzt er die *.dll

    Dann zeige ich in der CMakeLists.txt zwar auf die iec61850.lib (import-lib), diese aber dann auch die iec61850.dll im build-ordner zeigt?
    Wozu legt er dann aber im Installationsordner unter /bin auch eine ab?



  • @Leon0402 sagte in Zwei Projekte in einem CMake zusammenfassen:

    Aber erstmal alles lauffähig etc. bekommen

    Ich weiß gerade nicht, was jetzt aktuell die eigentliche Aufgabe ist.... 🤣
    Ich hab irgendwie den Faden verloren....🙁

    Ich habe den Eindruck, dass je mehr ich mir zu CMake anlese, desto verwirrender wird es.
    Vielleicht sollte ich wirklich nochmal bei Adam und Eva mit einem Miniprojekt anfangen.



  • @Pf-nne sagte in Zwei Projekte in einem CMake zusammenfassen:

    Dann zeige ich in der CMakeLists.txt zwar auf die iec61850.lib (import-lib), diese aber dann auch die iec61850.dll im build-ordner zeigt?
    Wozu legt er dann aber im Installationsordner unter /bin auch eine ab?

    Ich bin kein Windows Library Experte, daher folgende Aussagen mit etwas Vorsicht genießen. Meines Wissens nach enthält die Import Library gar keine Referenz zu der DLL.
    Die Import Library enthält stubs ("dummy Implementierungen") für alle Funktionen in der DLL. Und wird genutzt zum Linken, damit halt der Linker weiß: Ah ja, die Funktion stammt aus der Library. Die Import Library hat keinen Plan, ob es ne dll gibt oder wo die liegen könnte. Nur wie sie heißt 🙂

    Dein Programm weiß also: Ich brauche eine DLL mit dem Namen XXX und dann sucht sie nach der DLL mit diesem Namen. Klassische Suchpfade sind der PATH und das aktuelle Verzeichnis in dem die .exe liegt. Wenn er die nicht findet, kommt ne Fehlermeldung. Bestimmt schon mal bei dem ein oder anderne programm gesehen, wenn vergessen wurde DLLs mitzuliefern.
    Gefühlt immer (meine Erfahrung, keine umfassende Recherche durchgeführt :-)) liegen die DLLs, sofern nicht irgendwelche Windows System Sachen, einfach im selben Verzeichnis wie die exe.

    Also der wichtige Teil ist hier einfach: Es wird auf gar nix gezeigt, er sucht danach.

    Für den Rest fehlen mir ein paar Infos. Aber meine Vermutung: Deine exe liegt zufällig in C:\TEMP\Code\libiec61850\build\src\Release? Und entsprechend nimmt er die DLL eben aus dem selben Verzeichnis.
    Zweite Frage: Wie kommt die DLL da hin? Meine Vermutung auch hier ... du hast die Library ja weiter oben gebaut und vlt. hat er in dem Zuge dort die DLL abgelegt? Ich glaube jedenfalls nicht, dass er die automatisch kopiert hat, weil du gegen die .lib linkst.

    Einfacher Test wäre im zweifelsfall mal dein Build Directory zu löschen und eben nur deine Qt App zu bauen. Dann wird da vermutlich keine DLL mehr liegen.

    Aber naja alles Spekulation hier jetzt. Da musst du schon ein paar mehr Infos über deine Verzeichnisstruktur etc. geben 😉



  • Moin,

    macht wie immer Sinn was du schreibst und passt zu meinen Beobachtungen!
    Ist aber an sich nix wo wir uns mit aufschießen sollten... 😁

    "Wir" sollten uns wieder auf CMake fokussieren und da "hingefummelte" in was vernünftiges verwandeln.

    @Pf-nne sagte in Zwei Projekte in einem CMake zusammenfassen:

    Ich weiß gerade nicht, was jetzt aktuell die eigentliche Aufgabe ist....
    Ich hab irgendwie den Faden verloren....
    Ich habe den Eindruck, dass je mehr ich mir zu CMake anlese, desto verwirrender wird es.
    Vielleicht sollte ich wirklich nochmal bei Adam und Eva mit einem Miniprojekt anfangen.

    Wie würden "wir" denn jetzt weiter vorgehen?

    Gruß



  • @Pf-nne sagte in Zwei Projekte in einem CMake zusammenfassen:

    macht wie immer Sinn was du schreibst und passt zu meinen Beobachtungen!
    Ist aber an sich nix wo wir uns mit aufschießen sollten...

    Verständnis davon wie Libraries etc. funktionieren ist wichtig, um auch cmake zu schnallen. Letzendes baut es ja darauf auf.

    @Pf-nne sagte in Zwei Projekte in einem CMake zusammenfassen:

    Wie würden "wir" denn jetzt weiter vorgehen?

    Zunächst solltest du deine Library in ein ordentliches CMake Target verwandeln, was alle Informationen über deine Library besitzt. Sprich die Header files, dependencies etc.
    Das Ziel sollte sein, dass du dann das Target nur linken musst.

    target_link_libraries(MY_PROJECT PRIVATE Qt6::Widgets iec6185)
    

    (Falls nicht bekannt: Man kann mehrere Sachen auf einmal in diesen Befehlen angeben)

    Wie definierst du jetzt also dein Target iec6185? Das macht man mit einem Import Target:
    https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html#importing-targets

    Das soll für den Anfang erstmal genügen. Als kleinen Ausblick aber, so soll es am Ende aussehen:

    find_package(iec6185) 
    
    add_executable(MY_PROJECT ${SOURCES})
    target_link_libraries(MY_PROJECT Qt6::Widgets iec6185)
    

    Das find_package sucht also die Library für dich und erstellt dir ein Import Target mit allen benötigten Informationen, was du einfach linkst. Das ganze funktioniert dann Cross Platform für Windows, Linux, Mac OS etc.
    Wie das geht, erfährst du dann nach der aktuellen Aufgabe 😃



  • echt cool, das du dir mit mir so viel Mühe gibt, das ist nicht selbstverständlich!!!

    Noch eine Verständnisfrage, wir haben ja das Problem, dass der libiec61850-install-Ordner (bin; include; lib) kein CMakeLists.txt mitliefert, also nicht als target funkgieren kann.
    Das Stammverzeichnis in dem die Library (der GitHub-Download) kompiliert abliegt hat doch aber im root ein CMakeLists.txt.

    cmake_minimum_required(VERSION 3.5.1)
    
    # automagically detect if we should cross-compile
    if(DEFINED ENV{TOOLCHAIN})
        set(CMAKE_C_COMPILER	$ENV{TOOLCHAIN}gcc)
        set(CMAKE_CXX_COMPILER	$ENV{TOOLCHAIN}g++)
        set(CMAKE_AR	"$ENV{TOOLCHAIN}ar" CACHE FILEPATH "CW archiver" FORCE)
    endif()
    
    project(libiec61850)
    ENABLE_TESTING()
    
    set(LIB_VERSION_MAJOR "1")
    set(LIB_VERSION_MINOR "5")
    set(LIB_VERSION_PATCH "1")
    set(LIB_VERSION "${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}")
    
    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/third_party/cmake/modules/")
    
    # feature checks
    include(CheckLibraryExists)
    check_library_exists(rt clock_gettime "time.h" CONFIG_SYSTEM_HAS_CLOCK_GETTIME)
    
    # check if we are on a little or a big endian
    include (TestBigEndian)
    test_big_endian(PLATFORM_IS_BIGENDIAN)
    
    set(CONFIG_MMS_MAXIMUM_PDU_SIZE "65000" CACHE STRING "Configure the maximum size of an MMS PDU (default 65000)"   )
    set(CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS 5 CACHE STRING "Configure the maximum number of clients allowed to connect to the server")
    
    option(BUILD_EXAMPLES "Build the examples" ON)
    option(BUILD_PYTHON_BINDINGS "Build Python bindings" OFF)
    
    option(CONFIG_MMS_SINGLE_THREADED "Compile for single threaded version" ON)
    option(CONFIG_MMS_THREADLESS_STACK "Optimize stack for threadless operation (warning: single- or multi-threaded server will not work!)" OFF)
    option(CONFIG_ACTIVATE_TCP_KEEPALIVE "Activate TCP keepalive" ON)
    option(CONFIG_INCLUDE_GOOSE_SUPPORT "Build with GOOSE support" ON)
    
    option(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB "Build with pre-compiled mbedtls dynamic library" OFF)
    
    set(CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/library" CACHE STRING "Path to search for the mbedtls dynamic libraries" )
    set(CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/include" CACHE STRING "Path to search for the mbedtls include files" )
    
    # choose the library features which shall be included
    option(CONFIG_IEC61850_CONTROL_SERVICE "Build with support for IEC 61850 control features" ON)
    option(CONFIG_IEC61850_REPORT_SERVICE "Build with support for IEC 61850 reporting services" ON)
    option(CONFIG_IEC61850_LOG_SERVICE "Build with support for IEC 61850 logging services" ON)
    option(CONFIG_IEC61850_SERVICE_TRACKING "Build with support for IEC 61850 service tracking" ON)
    option(CONFIG_IEC61850_SETTING_GROUPS "Build with support for IEC 61850 setting group services" ON)
    option(CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL "Allow user provided callback to control read access" ON)
    option(CONFIG_IEC61850_RCB_ALLOW_ONLY_PRECONFIGURED_CLIENT "allow only configured clients (when pre-configured by ClientLN)" OFF)
    
    set(CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE "65536" CACHE STRING "Default buffer size for buffered reports in byte" )
    
    # advanced options
    option(DEBUG "Enable debugging mode (include assertions)" OFF)
    option(DEBUG_SOCKET "Enable printf debugging for socket layer" ${DEBUG})
    option(DEBUG_COTP "Enable COTP printf debugging" ${DEBUG})
    option(DEBUG_ISO_SERVER "Enable ISO SERVER printf debugging" ${DEBUG})
    option(DEBUG_ISO_CLIENT "Enable ISO CLIENT printf debugging" ${DEBUG})
    option(DEBUG_IED_SERVER "Enable IED SERVER printf debugging" ${DEBUG})
    option(DEBUG_IED_CLIENT "Enable IED CLIENT printf debugging" ${DEBUG})
    option(DEBUG_MMS_SERVER "Enable MMS SERVER printf debugging" ${DEBUG})
    option(DEBUG_MMS_CLIENT "Enable MMS CLIENT printf debugging" ${DEBUG})
    option(DEBUG_GOOSE_SUBSCRIBER "Enable GOOSE subscriber printf debugging" ${DEBUG})
    option(DEBUG_GOOSE_PUBLISHER "Enable GOOSE publisher printf debugging" ${DEBUG})
    option(DEBUG_SV_SUBSCRIBER "Enable Sampled Values subscriber debugging" ${DEBUG})
    option(DEBUG_SV_PUBLISHER "Enable Sampled Values publisher debugging" ${DEBUG})
    option(DEBUG_HAL_ETHERNET "Enable Ethernet HAL printf debugging" ${DEBUG})
    
    include_directories(
        ${CMAKE_CURRENT_BINARY_DIR}/config
        ${CMAKE_CURRENT_LIST_DIR}/src/common/inc
        ${CMAKE_CURRENT_LIST_DIR}/src/goose
        ${CMAKE_CURRENT_LIST_DIR}/src/sampled_values
        ${CMAKE_CURRENT_LIST_DIR}/src/hal/inc
        ${CMAKE_CURRENT_LIST_DIR}/src/iec61850/inc
        ${CMAKE_CURRENT_LIST_DIR}/src/iec61850/inc_private
        ${CMAKE_CURRENT_LIST_DIR}/src/mms/inc
        ${CMAKE_CURRENT_LIST_DIR}/src/mms/inc_private
        ${CMAKE_CURRENT_LIST_DIR}/src/mms/iso_mms/asn1c
        ${CMAKE_CURRENT_LIST_DIR}/src/logging
    )
    
    set(API_HEADERS
        hal/inc/hal_base.h
        hal/inc/hal_time.h
        hal/inc/hal_thread.h
        hal/inc/hal_filesystem.h
        hal/inc/hal_ethernet.h
        hal/inc/hal_socket.h
        hal/inc/tls_config.h
        src/common/inc/libiec61850_common_api.h
        src/common/inc/linked_list.h
        src/iec61850/inc/iec61850_client.h
        src/iec61850/inc/iec61850_common.h
        src/iec61850/inc/iec61850_server.h
        src/iec61850/inc/iec61850_model.h
        src/iec61850/inc/iec61850_cdc.h
        src/iec61850/inc/iec61850_dynamic_model.h
        src/iec61850/inc/iec61850_config_file_parser.h
        src/mms/inc/mms_value.h
        src/mms/inc/mms_common.h
        src/mms/inc/mms_types.h
        src/mms/inc/mms_type_spec.h
        src/mms/inc/mms_client_connection.h
        src/mms/inc/mms_server.h
        src/mms/inc/iso_connection_parameters.h
        src/goose/goose_subscriber.h
        src/goose/goose_receiver.h
        src/goose/goose_publisher.h
        src/sampled_values/sv_subscriber.h
        src/sampled_values/sv_publisher.h
        src/logging/logging_api.h
    )
    
    if(MSVC AND MSVC_VERSION LESS 1800)
        include_directories(
            ${CMAKE_CURRENT_LIST_DIR}/src/vs
        )
    endif(MSVC AND MSVC_VERSION LESS 1800)
    
    if(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
    set(WITH_MBEDTLS 1)
    set(USE_PREBUILD_MBEDTLS 1)
    set(MBEDTLS_INCLUDE_DIR ${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH})
    endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
    
    if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16)
    set(WITH_MBEDTLS 1)
    set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/include")
    endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16)
    
    if(WITH_MBEDTLS)
    
    add_definitions(-DCONFIG_MMS_SUPPORT_TLS=1)
    
    endif(WITH_MBEDTLS)
    
    # write the detected stuff to this file
    configure_file(
        ${CMAKE_CURRENT_LIST_DIR}/config/stack_config.h.cmake
        ${CMAKE_CURRENT_BINARY_DIR}/config/stack_config.h
    )
    
    include_directories(
    	${CMAKE_CURRENT_LIST_DIR}/hal/inc
    )
    
    add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/hal")
    
    if(DEBUG)
    	set(CMAKE_BUILD_TYPE Debug)
    endif(DEBUG)
    
    if(BUILD_EXAMPLES)
        add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/examples)
    endif(BUILD_EXAMPLES)
    
    add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/src)
    
    install(FILES ${API_HEADERS} DESTINATION include/libiec61850 COMPONENT Development)
    
    if(BUILD_PYTHON_BINDINGS)
        add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/pyiec61850)
    endif(BUILD_PYTHON_BINDINGS)
    
    set(CPACK_PACKAGE_DESCRIPTION "IEC 61850 MMS/GOOSE client and server library")
    set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "IEC 61850 MMS/GOOSE client and server library")
    set(CPACK_PACKAGE_VENDOR "MZ Automation GmbH")
    set(CPACK_PACKAGE_CONTACT "info@libiec61850.com")
    set(CPACK_PACKAGE_VERSION_MAJOR "${LIB_VERSION_MAJOR}")
    set(CPACK_PACKAGE_VERSION_MINOR "${LIB_VERSION_MINOR}")
    set(CPACK_PACKAGE_VERSION_PATCH "${LIB_VERSION_PATCH}")
    set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}_${CMAKE_SYSTEM_PROCESSOR}")
    set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
    
    set(CPACK_COMPONENTS_ALL Libraries Development Applications)
    #set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_NAME}")
    
    if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
        include(InstallRequiredSystemLibraries)
    
        include(CPack)
    endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
    

    Wäre das nicht unser passendes target?
    Allerdings, so mein bisheriges Verständnis, würden wir die library dann nicht mehr statisch nutzen, sondern die *.lib und *.dll files beim bauen meines Widgets kompilieren.

    Oder stehe ich jetzt wieder auf dem Schlauch?



  • Das lässt sich nicht so ganz einfach beantworten, aber ich versuche es mal.

    Es gibt zwei wesentliche Modi wie du eine Library verwenden kannst:

    • Die Library kompilieren & installieren auf dein System (Das ist was du bisher gemacht hast)
    • Die Library auf einem von vielen Wegen mit in dein Projekt einbinden (FetchContent, Git Submodule, ...) und als Teil deines Projektes mitkompilieren

    Der zweite Weg ist aus meiner Sicht der beste für Bibliotheken mit ein paar wenigen Ausnahmen (Ausnahmen: Große Libs wie Boost, Qt etc., Non CMake Projekte bei denen das schwierig geht).

    Die Grundidee vom zweiten Weg ist eig. relativ einfach. Du machst einfach ein add_subdirectory(pfad/zu/der/cmake/library). Und du kannst einfach gegen die Targets linken direkt, weil die Lib definiert die ja.

    Es hat nur einen einzigen Haken manchmal. Der CMake Code von vielen Libraries ist einfach Schrott. Ich hatte dir oben ein Beispiel gegeben.
    Sagen wir die Library definiert

    add_library(libiec61850 ...)
    target_include_directories(libiec61850 PRIVATE path/to/library/include/folder)
    

    da das Include Directory hier auf PRIVATE gesetzt ist, wird es nicht an dein Target vererbt, wenn du linkst mit target_include_directories(MY_PROJECT libiec61850).
    Das bedeutet du musst eben auch nochmal zusätzlich das include directory mit target_include_directories(MY_PROJECT path/to/library/include/folder) setzen.
    Oder noch besser: Einen Pull Request einreichen und das CMake in der Lib fixen 😉

    Von solchen Fallstriken gibt es einige und nicht selten muss man in der Lage sein den CMake Code des Projektes halbwegs zu verstehen, um die Probleme zu fixen. Das kann eine Herausforderung sein, weil so CMake Code einer größeren Lib ist selten trivial 😉
    Aber es wird besser ... ich kann dir einige Libs nennen, die mittlerweile out of the box funktionieren.

    Soviel zu diesem Weg. Der andere sieht wie gesagt vor, dass man die Library zuerst baut und installiert. Das ganze läuft dann immer so wie oben beschrieben. Man macht:

    find_package(Iec6185) 
    
    add_executable(MY_PROJECT ${SOURCES})
    target_link_libraries(MY_PROJECT Qt6::Widgets iec6185)
    

    find_package hat widerum 2 Modi. Einen alten legacy modi und einen neuen.

    Der legacy modi:

    • Er sucht nach einer Datei FindIec6185.cmake. Diese hat die Aufgabe die Teile der Library in einer Cross Plattform Art zu suchen und daraus so ein oben beschriebenes Import Target zu erzeugen, was du dann nutzt. Dieses Skript wird von dir geschrieben (oder aus nem anderen Projekt geklaut). Für wenige populäre Libs stellt CMake so ein Skript auch bereit. Hier eine Lib, die lauter so Skripte für verschiedene DB Libs hat: https://github.com/SOCI/soci/tree/master/cmake/modules

    Der moderne Weg:

    • Er Sucht nach einer Datei Iec6185Config.cmake. Diese wird von der Library, so wie die dlls etc. mitinstalliert. Diese enthält dann die Definition für Target Informationen. Jede gute moderne Lib sollte das machen. Das kann man so mehr oder wenige semi automatisch generieren lassen mit dem install(TARGETS ...) Befehl.

    Es lässt sich auch zusammenfassen zu: Dependencies einbinden ist einfach, wenn die Lib es dir einfach macht 🙂 Wenn möglich erstellt man einen Pull Requests, um es einfach zu machen, wenn es das noch nicht ist. Aber dafür braucht man natürlich etwas CMake Ahnung.


Anmelden zum Antworten