Zwei Projekte in einem CMake zusammenfassen



  • 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.



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

    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.

    Ich glaube auch, dass das der richtige Weg ist. Aber warum sind es "nur" definitions und nicht gleich ein fertiges target?



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

    Aber warum sind es "nur" definitions und nicht gleich ein fertiges target?

    Ist nen fertiges target, hab mich nur unklar ausgedrückt.



  • Moin, ich bin noch dran und nicht eingeschlafen...
    Ist nur aktuell echt Sommer draußen, daher eher Strand im Trend!



  • Ich hab jetzt nochmal versucht mich weiter einzulesen....
    Ich muss aber sagen, dass, je mehr ich lese, desto schlimmer wird es... gerade der Vermischung zwischen klassischem und modernem CMake ist echt verwirrend.

    Ich glaube verstanden zu haben, dass bei meine Library zwar ein CMake --install unterstütz und alle Header, *.lib und *.dll in einen Ordner kopiert, aber keine CMakeLists.txt bereit stellt.
    Diese CmakeLists.txt würde mir dann in meinem Projekt als Target dienen und ich bräuchte mich um den Ort der Library Files keine Sorgen mehr zu machen.

    Ich denke nicht, dass ich hierzu, mangels wissen und Verständnis, geistreiche Anregungen geben kann.
    Mit anderen Worten, ich bin voll und ganz auf die Unterstützung und das Wohlwollen von sehenden angewiesen.

    Vielleicht hast du ja Lust hier noch weiter Energie reinzustecken.
    Ich könnte es dann als PullRequest einstellen.
    Ich habe mir auch vorgenommen ein kleines HowTo in meinem Fork bereitzustellen.

    Wer diese Library benötigt wird wahrscheinlich an ähnlichen Stellen wie ich hängen bleiben.
    Das Thema der Library "IEC61850" ist definitiv schon komplex genug, da braucht man nicht auch noch Ärger mit CMake!!

    In jedem Fall erstmal danke für deine bisherige Unterstützung!!!

    Sonnigen Gruß
    Marco


Anmelden zum Antworten