Frage zu Makefile für MINGW/GCC linker



  • Hi !

    Ich versuche mich gerade an Makefiles....
    Mein Problem: Ich habe mir eine dll gebastelt, will die dazu passende lib(*.a)
    über nen makefile mit meinem Projekt linken.

    Scheinbar mache ich da etwas mit der Pfadangabe Falsch (vlt ist es auch was anderes)

    Also mit nem shellscript funktioniert das:

    g++ main.cpp dll.h -o main.exe -lm ../../../tmpWorkspace/makefiles/dllStatischLinkenObjekte/libdllBsp1.a
    

    In meinem Makefile:

    CC = gcc 										                                           #c compiler
    CPP = g++										                                           #c++ compiler
    LD = $(CPP) 										                                       #linker
    CFLAGS = -Wall -g									                                     #compiler flags
    
    LDDIR = -lm  ../../../tmpWorkspace/makefiles/dllStatischLinkenObjekte/libdllBsp1.a
    LDFLAGS = $(LDDIR)          		 						                           #linker flags 
    RM = rm -f										                                          #clean (löschen)
    
    OBJS = main.o 
    SRCS = main.cpp 
    PROG = main.exe
    
    all: $(OBJS)
    	$(LD) $(LDFLAGS) $(OBJS) -o $(PROG)
    
    # *.o aus *.c erstellen
    %.o: %.c
    	$(CPP) $(CFLAGS) -c $<
    
    #abhängigkeiten bilden
    depend:
    	$(CPP) $(CFLAGS) -MM $(SRCS) > depend
    
    run: all
    	$(PROG)
    
    include depend
    
    clean:
    	$(RM) $(PROG)
    	$(RM) *.o
    	$(RM) depend
    

    funktioniert das leider nicht, was mache ich hier falsch ?
    Ich habe auch schon versucht den Pfad direkt anzugeben(leider kein Erfolg)

    Die Ausgabe in der Konsole(cygwin) lässt mich darauf schließen das die lib nicht richtig verlinkt wurde...

    $ make
    g++                                                                                                                                                                                   -lm  ../../../tmpWorkspace/makefiles/dllStatischLinkenObjekte/libdllBsp1.a                                                         main.o  -o main.exe
    main.o:main.cpp:(.text+0x174): undefined reference to `_imp___ZN8DllClassC1Ev'
    main.o:main.cpp:(.text+0x1c2): undefined reference to `_imp___ZN8DllClass5HelloEv'
    collect2: ld returned 1 exit status
    make: *** [all] Error 1
    

    Ich würde mich über jedliche Hilfe sehr freuen und binn sehr gespannt was ich hier falsch mache !

    Mfg McMorf



  • 1. CXX ist die Variable für den C++-Compiler. CPP ist der Präprozessor! CXXFLAGS sind die Flags für den C++-Compiler. CFLAGS sind die Flags für den C Compiler.
    2. In Zeile 19 die Regel brauchst du vermutlich nicht. Du hast ja eh keine C-Dateien. Außerdem hat Make implizite Regeln für cpp und c Dateien. (Daher auch 1. beachten!)
    3. Sollten all, run, clean als PHONY markiert werden, da sie ja keinen Dateien entsprechen.
    4. all sollte vielleicht eine Abhängigkeit auf depend haben.
    5. Du hast leider in deiner Konsolenausgabe ausgelassen, wie der g++ genau ausgerufen wird.

    Siehe auch: http://www.c-plusplus.net/forum/88418



  • Ich hatte mal ein "dll"-Beispiel aus dem Buch "Windows Programmierung" von Charles Petzold mit mingw gebaut und folgendes Makefile geschrieben:

    all:
    	mingw32-gcc -c -DBUILD_DLL EdrLib.c
    	mingw32-gcc -shared -o EdrLib.dll -Wl,--out-implib,libEdrLib.a EdrLib.o -mwindows
    	mingw32-gcc EdrTest.c -o EdrTest.exe -pedantic -Wall -mwindows -ggdb -L./ -lEdrLib
    

    Vielleicht hilft es weiter.

    (PS: man kann das Makefile natürlich beliebig kompliziert machen)



  • Erst mal Danke für die Antworten !

    wo genau jetzt mein fehler liegt wurde leider nicht vollständig geklährt...
    ich werde mich wohl noch ein bischen weiter damit auseinander setzen müssen...

    Trotzdem danke, da hab ich erst mal ein paar infos wo ich vlt. drauf aufbauen kan...

    Mfg McMorf



  • hmmm... irgendwie hilft mir keine dieser antworten wirklich weiter...
    dann muss ich mir wohl shell scripte dafür bauen...

    trotzdem danke für den versuch !



  • Bei deinem Shell\1:

    g++ main.cpp [b]dll.h[/b] -o main.exe -lm ...
    

    Die dll.h kommt mir ungewöhnlich vor 😕

    Erwarte da, wenn es C++ werden soll, nur Dateinamen mit der Endung '.cpp' oder ähnliche, aber keine '.h'.

    Wenn deine dll.h nicht zu lang ist, zeig die mal.

    MfG f.-th.



  • dll.h

    #ifndef _DLL_H_
    #define _DLL_H_
    
    #if BUILDING_DLL
    # define DLLIMPORT __declspec (dllexport)
    #else /* Not BUILDING_DLL */
    # define DLLIMPORT __declspec (dllimport)
    #endif /* Not BUILDING_DLL */
    
    class DLLIMPORT DllClass
    {
      public:
        DllClass();
        virtual ~DllClass(void);
    
        void Hello(void);
    
      private:
    
    };
    
    #endif /* _DLL_H_ */
    

    dll.cpp

    /* Replace "dll.h" with the name of your header */
    #include "dll.h"
    #include <windows.h>
    
    DLLIMPORT DllClass::DllClass()
    {
    
    }
    
    DLLIMPORT DllClass::~DllClass ()
    {
    
    }
    
    DLLIMPORT void DllClass::Hello(void)
    {
        MessageBox(NULL, "Hallo Welt", "MsgBox", MB_OK | MB_ICONINFORMATION);
    }
    
    BOOL APIENTRY DllMain (HINSTANCE hInst     /* Library instance handle. */ ,
                           DWORD reason        /* Reason this function is being called. */ ,
                           LPVOID reserved     /* Not used. */ )
    {
        switch (reason)
        {
          case DLL_PROCESS_ATTACH:
            break;
    
          case DLL_PROCESS_DETACH:
            break;
    
          case DLL_THREAD_ATTACH:
            break;
    
          case DLL_THREAD_DETACH:
            break;
        }
    
        /* Returns TRUE on success, FALSE on failure */
        return TRUE;
    }
    

    main.cpp:

    #include <cstdlib>
    #include <iostream>
    #include <windows.h>
    #include "dll.h"
    
    using namespace std;
    
    int main(int argc, char *argv[])
    {
        DllClass *dll = new DllClass();
        dll->Hello();
        delete dll;
    
        system("PAUSE");
        return 0;
    }
    

    und das shellScript was ich mir jetzt gebaut habe:

    #Under WINDOWS(CYGWIN) dont forget 2 run dos2unix [scriptname]
    COMPILER=g++
    SOURCES=main.cpp
    HEADERS=dll.h
    OUTPUT=main.exe
    LIBS=/tmpWorkspace/makefiles/dllStatischLinkenObjekte/libdllBsp1.a
    
    echo "$COMPILER $SOURCES $HEADERS -o $OUTPUT -lm $LIBS"
    $COMPILER $SOURCES $HEADERS -o $OUTPUT -lm $LIBS
    

    und mir geht es eigendlich garnicht um die dll sondern generell um libs also sowohl unter windoof wie auch unter linux....

    Ich frage mich auch welchen vorteil eine makefile gegenüber einem shellscript hat ?!?

    naja wie gesagt finde den fehler in meinem makefile nicht, denke aber das das irgendwas mit der ordnerangabe zu tun haben muss... notfalls würde mir aber wirklich auch ein shellscript reichen, hauptsache es funktioniert... und ich bleibe platformunabhängig (was sich natürlich mit ner dll beist) es geht mir ja nur um das algemeine verständniss

    Mfg McMorf



  • Die Lösung hab ich jetzt nicht, aber:
    Weder im Makefile noch sonst wo hat da 'dll.h' etwas zu suchen ⚠
    Da musst du etwas mit deiner 'dll.cpp' schreiben ⚠
    Im Quelltext deiner 'dll.cpp' ist der Header 'dll.h' ja schon eingebunden.
    Ebenso in 'main.cpp'. Das reicht!

    Da du spezielle Windows-Funktionen im Quelltext nutzt musst du dies dem Linker bekannt geben ⚠
    Also muss da irgend so was im Makefile stehen, wie abc.w schon schrieb: -mwindows
    Eventuell gibt es da noch einen weiteren Weg.

    Ursprünglich wurden DLL's für Windows mit einer C-konformen Schnittstelle konzipiert. Abweichungen davon sind heute möglich, bringen dem Programmierer oft viel Spass 😉

    Da dein Quelltext sehr Windows-lastig ist wirst du den ohne deutliche Modifizierungen kaum unter Linux zum Laufen bekommen.

    Wenn du das vorhast, solltest du dich in eine Bibliothek einarbeiten die beide Betriebssysteme unterstützt.

    f.-th.



  • Hier noch etwas zu deiner DLL:
    http://msdn.microsoft.com/de-de/library/3y1sfaz2.aspx

    Auf deutsch 'DLLIMPORT' gibt es nur bei Microsoft.
    Und ob du da die Gross- und Kleinschreibung beliebig nutzen kannst 😕
    Das gibt unter C und C++ meist Ärger.

    MfG f.-th.



  • Zu dllimport und gcc gibt es schon einen Beitrag:
    http://www.c-plusplus.net/forum/245780
    und hier auch noch:
    http://gcc.gnu.org/wiki/Visibility

    MfG f.-th.



  • f.-th. schrieb:

    Auf deutsch 'DLLIMPORT' gibt es nur bei Microsoft.
    Und ob du da die Gross- und Kleinschreibung beliebig nutzen kannst 😕
    Das gibt unter C und C++ meist Ärger.

    Das ist Blödsinn. Siehe Links. 😡

    Aber trotzdem in deiner Microsoft-dll.h nachsehen:
    export <=> import 😕

    MfG f.-th.



  • -mwindows führt bei einem konsolen prg nur dazu das keine konsole gebildet wird...
    DLLIMPORT ist eine Konstante da ist Groß/Klein schreibung geschmackssache...
    noch dazu habe ich diese dll ganz schnell mit dev-cpp erstellt und das war erst mal vorgabe, was man auch an den komentaren sehen kann...

    gut... in meinem shell script brauch ich die dll.h wirklich nicht angeben ich dachte immer das macht man so 🤡 naja jut 1 schritt weiter...

    das problem ist aber nicht in meiner dll zu suchen, sondern irgendwo in meiner makefile 😕

    Natürlich will ich keine dll auf linux verwenden die dll hab ich nur zum testen benutzt mir geht es eher generell ums verlinken was ist z.B wenn ich unter M$ die libole32.a linken will oder sonst irgend eine lib ?!?



  • McMorf! schrieb:

    das problem ist aber nicht in meiner dll zu suchen, sondern irgendwo in meiner makefile 😕

    Ich verstehe das Problem nicht. Schau Dir mein Makefile an, es wird von oben nach unten, von links nach rechts abgearbeitet. Es werden eine dll , eine .a und schließlich eine exe erstellt. Beim Erstellen der exe bekommt der Compiler die Parameter " -L./ -lEdrLib " und sie sagen dem Compiler, in welchem Verzeichnis die EdrLib zu suchen ist (wie man sieht, im aktuellen Verzeichnis ./). Keine Hexerei also. Du müsstest irgendwo im Makefile " -L/tmpWorkspace/makefiles/dllStatischLinkenObjekte/ " an gcc übergeben, damit gcc weiss, in welchem Verzeichnis die Bibliothek zu suchen ist (absolute Pfade sind aber Scheiße...). Das war's.

    Schau noch mal mein Makefile und Dein Makefile an. Meins ist einfach, deins ist kompliziert. Muss es so kompliziert sein 😕



  • Also abc.w anich währe deine lösung für mich bei größeren Projekten einfach zu viel schreibarbeit auserdem finde ich wird es dann unübersichtlich...
    vlt einfach nur geschmackssache.....

    Aber ne generelle Frage habe ich trotzdem...
    Also jetzt mal kurz vom makefile weck...:

    Habe jetzt erst mal nen ShellScript (welches ich recht übersichtlich finde) gebastelt...

    #(CYGWIN) run dos2unix [scriptname]
    COMPILER="g++"
    SOURCES="main.cpp"
    HEADERS=""
    CFLAGS="-mwindows -Wall"
    OUTPUT="main.exe"
    LDIR=""
    LIBS=""
    OWNLDIR="/tmpWorkspace/makefiles/dllStatischLinkenObjekte/"
    OWNLIBS="libdllBsp1.a"
    
    #Absolute Pfandangabe zusammen setzen:
    LIBS=" $LIBS"
    LDIR=" $LDIR"
    LIBS="${LIBS// /$LDIR}"
    OWNLIBS=" $OWNLIBS"
    OWNLDIR=" $OWNLDIR"
    OWNLIBS="${OWNLIBS// /$OWNLDIR}"
    
    #Ausgabe an Kompiler:
    echo "$COMPILER $SOURCES -o $OUTPUT -lm $LIBS $OWNLIBS $CFLAGS"
    $COMPILER $SOURCES -o $OUTPUT -lm $LIBS $OWNLIBS $CFLAGS
    

    Was währe der Nachteil eines solchen Scripts gegenüber einem Makefile ???

    Mfg McMorf



  • McMorf! schrieb:

    Also abc.w anich währe deine lösung für mich bei größeren Projekten einfach zu viel schreibarbeit auserdem finde ich wird es dann unübersichtlich...

    Ja, natürlich, bei größeren Projekten muss man die Struktur des Makefiles überdenken. Mein Makefile baut ja die Dateien jedes Mal neu, ob sie sich geändert haben oder nicht, kostet Zeit und Zeit ist Geld.

    McMorf! schrieb:

    Was währe der Nachteil eines solchen Scripts gegenüber einem Makefile ???

    Mit dem Skript versuchst Du die Funktionalität des Tools make nachzubauen, also ein Tool, was schon da ist und von Profis speziell für diese Zwecke entwickelt wurde und sich in der Praxis bewährt hatte. make bringt ja noch Automatismen mit: Prüfst Du im Skript den Rückgabewert des Compilers? Nein (oder ich sehe es nicht, weil müde, will schlafen oder sonstiges). make würde bei einem aufgetretenen Fehler die Abarbeitung des Makefiles sofort abbrechen. Nutzt Dein Skript mehrere Kerne aus? Nein, ein Skript wird von oben nach unten abgearbeitet. Mit make könnte man die Aufgabe auf mehrere CPU-Kerne verteilen, d.h. man könnte ein Projekt deutlich schneller bauen, mit dem Skript nicht.



  • Hier noch mal wie das auf Kommandozeile laut MinGW aufgerufen wird:
    http://www.mingw.org/wiki/sampleDLL
    Passende Umgebung (Path) setze ich mal voraus.

    CYGWIN - nicht jeder hantiert in der Form mit dem MinGW.
    Also läuft dein Script nicht überall. Hab die Möglichkeit bezüglich der Scripte unter den neuen Windows, Stichwort "Powershell", noch nicht in der Hinsicht getestet.

    MfG f.-th.



  • Super Herzlichen Dank für die Antworten !

    Mit den letzten beiden Antworten (abc.w f.-th.) konnte ich richtig viel anfangen, das ist schon mal ne perfekte erklährung warum makefiles sinniger sind...

    http://www.mingw.org/wiki/sampleDLL

    Wird mir hoffentlich weiter helfen, sieht auf jeden fall so aus als könnte ich damit weiter kommen...

    Den ansatz shell script werde ich dann mal vernachlässigen und mich weiter mit Makefiles beschäftigen... irgendwie muss ich das ja mal zum laufen bekommen ^^

    Ihr habt mir auf jeden fall deutlich weiter geholfen !

    Mfg McMorf



  • So, hab das jetzt mal total minimalistisch gemacht...
    und ja, es funktioniert !

    CC = gcc                                                                                    
    CPP = g++                                                                                  
    LD = $(CPP)                                                                                
    CFLAGS = -Wall -g                                                                         
    
    LDDIR = -L./ -ldllBsp1
    LDFLAGS = $(LDDIR)                                                                      
    RM = rm -f                                                                                  
    
    OBJS = main.o
    SRCS = main.cpp
    PROG = main.exe
    
    all: $(OBJS)
    	$(LD) $(SRCS) -o $(PROG) $(LDFLAGS)
    

    jetzt hab ich ne frage die mich beschäftigt....
    was hat das mit den *.o dateien genau auf sich ?
    also bei meinem shell script z.b habe ich diese ja nicht gebildet....

    und wofür brächte man das ?:

    depend:
        $(CPP) $(CFLAGS) -MM $(SRCS) > depend
    

    das sind ja alles sachen die z.B von DEV-CPP auch mit gebildet würden, ich verstehe nur nicht wozu....

    Aber trotzdem erst mal Danke für die tolle hilfe hier, so komme ich doch immer n stückchen weiter !!! Will halt so langsam mal verstehen ^^



  • also *.o hab ich schon mal verstanden...

    hier speichert der compiler zwischen und deswegen muss nicht bei jeder Änderung das gesamte projekt neu kompiliert werden (denke nicht das meine progs so groß werden das ich das zwingend benötige)

    bleibt nur noch die frage wozu depend ?

    Mfg McMorf



  • Zum Thema mehrere Kerne...

    Also klar... nen shellScript bekomme ich ohne CYGWIN(o.ä.) nicht unter Windows zum Laufen... das verstehe ich....

    Prüfst Du im Skript den Rückgabewert des Compilers?

    bei einem Shellscript bricht MinGW bei error ab...
    bei nem Makefile auch...
    wozu den Rückgabewert überprüfen ? ich sehe es doch dann in der Shell ?!?

    Was mir jetzt nicht ganz klar ist....:

    Nutzt Dein Skript mehrere Kerne aus? Nein, ein Skript wird von oben nach unten abgearbeitet. Mit make könnte man die Aufgabe auf mehrere CPU-Kerne verteilen, d.h. man könnte ein Projekt deutlich schneller bauen, mit dem Skript nicht.

    also mal auf mein beispiel bezogen:

    COMPILER=	G++                                                                              
    SOURCE= 	main.cpp
    OUTPUT= 	main.exe
    CFLAGS= 	-Wall -g -mwindows                                                                        
    LDIR= 		-L./
    LIBS=		-lm libdllBsp1.a		
    
    RM = 		rm -f                                                                                  
    OBJS= 		main.o
    
    LIBS1 = $(LDIR) $(LIBS)
    
    all:
    	$(COMPILER) -c $(SOURCE) -o $(OBJS)
    	$(COMPILER) $(SOURCE) -o $(OUTPUT) $(LIBS1) $(CFLAGS)
    
    clean:
    	$(RM) $(PROG)
    	$(RM) *.o
    	$(RM) depend
    

    wie würde ich das jetzt auf mehrere kerne aufteilen ?
    und warum sollte das mit einem shellscript nicht gehen ? wird nicht letzten endes die aufgabe eh an MinGw übergeben ?

    Fragen über fragen... sehe schon das thema wird mich noch länger beschäftigen 😉

    Mfg McMorf


Log in to reply