Utilisation de make pour la compilation multiplateforme

Je développe actuellement un projet C sous Linux et Win32. Le ‘livrable’ est une bibliothèque partagée et tout le développement est effectué sous Linux avec la chaîne d’outils GNU. J’utilise un Makefile pour comstackr la bibliothèque partagée.

De temps en temps, je dois construire un fichier .dll sous Win32 à partir du même src.

J’ai installé MinGW sur le boîtier Win32 pour pouvoir utiliser et obtenir beaucoup moins de plaintes du compilateur (par rapport à MSVC). Je suis à un stade où le code src est compilé sur les deux plates-formes

Mais le Makefile Linux et le Makefile Win32 sont différents. Je suis curieux de savoir comment gérer au mieux cela – devrais-je:

  1. avoir 2 makefiles, par exemple Makefile pour linux et Makefile.WIN32, puis lancer make -f Makefile.WIN32 sous Windows

  2. Devrais-je créer une cible différente dans un seul Makefile et faire quelque chose comme make WIN32 sous Windows?

  3. Dois-je abandonner make et utiliser CMake (le jus vaut-il la peine d’être pressé pour un projet aussi simple, c’est-à-dire 1 bibliothèque partagée)

Utilisez un fichier Make unique et mettez les spécificités de la plate-forme dans des conditions , par exemple

 ifeq ($(OS),Windows_NT) DLLEXT := .dll else DLLEXT := .so endif DLL := libfoo$(DLLEXT) lib : $(DLL) 

J’utilise UNAME := $(shell uname) dans mon Makefile pour détecter la plate-forme (Linux ou MS-Windows).

Je fournis ci-dessous un exemple complet basé sur make et gcc pour construire une bibliothèque partagée: *.so ou *.dll selon la plate-forme.

L’exemple est basique / simple / stupide pour être plus compréhensible 🙂

Pour utiliser make et gcc sur MS-Windows, Cygwin ou MinGW peuvent être installés.

L’exemple utilise cinq fichiers:

  ├── app │ └── Makefile │ └── main.c └── lib └── Makefile └── hello.h └── hello.c 

Les Makefiles

app/Makefile

 app.exe: main.o gcc -o $@ $^ -L../lib -lhello # '-o $@' => output file => $@ = the target file (app.exe) # ' $^' => no options => Link all depended files # => $^ = main.o and other if any # '-L../lib' => look for libraries in directory ../lib # '-lhello => use shared library hello (libhello.so or hello.dll) %.o: %.c gcc -o $@ -c $< -I ../lib # '-o $@' => output file => $@ = the target file (main.o) # '-c $<' => COMPILE the first depended file (main.cpp) # '-I ../lib' => look for headers (*.h) in directory ../lib clean: rm -f *.o *.so *.dll *.exe 

lib/Makefile

 UNAME := $(shell uname) ifeq ($(UNAME), Linux) TARGET = libhello.so else TARGET = hello.dll endif $(TARGET): hello.o gcc -o $@ $^ -shared # '-o $@' => output file => $@ = libhello.so or hello.dll # ' $^' => no options => Link all depended files => $^ = hello.o # '-shared' => generate shared library %.o: %.c gcc -o $@ -c $< -fPIC # '-o $@' => output file => $@ = the target file (main.o) # '-c $<' => comstack the first depended file (main.cpp) # '-fPIC' => Position-Independent Code (required for shared lib) clean: rm -f *.o *.so *.dll *.exe 

Le code source

app/main.c

 #include "hello.h" //hello() #include  //puts() int main() { const char* str = hello(); puts(str); } 

lib/hello.h

 #ifndef __HELLO_H__ #define __HELLO_H__ const char* hello(); #endif 

lib/hello.c

 #include "hello.h" const char* hello() { return "hello"; } 

La construction

Corrige le copier-coller des Makefiles (remplace les espaces de début par des tabulations).

 > sed -i 's/^ */\t/' */Makefile 

La commande make est la même sur les deux plates-formes. La sortie donnée est pour MS-Windows (lignes inutiles supprimées).

 > cd lib > make clean > make gcc -o hello.o -c hello.c -fPIC gcc -o hello.dll hello.o -shared > cd ../app > make clean > make gcc -o main.o -c main.c -I ../lib gcc -o app.exe main.o -L../lib -lhello 

La course

L’application nécessite de savoir où se trouve la bibliothèque partagée.

Sur MS-Windows, la méthode la plus simple, la plus simple et la plus stupide consiste à copier la bibliothèque où l’application est:

 > cp -v lib/hello.dll app `lib/hello.dll' -> `app/hello.dll' 

Sous Linux, utilisez la variable d’environnement LD_LIBRARY_PATH :

 > export LD_LIBRARY_PATH=lib 

La ligne de commande et la sortie sont identiques sur les deux plates-formes:

 > app/app.exe hello 

Il y a quelques années, j’ai eu un problème similaire et j’ai constaté que cmake est beaucoup plus facile pour la compilation multiplate-forme ET utilisera le compilateur natif pour ce système. La syntaxe est plus claire et résume les détails inutiles pour la plupart (parfois cela gênait, mais il y avait généralement un moyen de les contourner)

En tant que personne ayant utilisé à la fois les outils automatiques et CMake, je vous recommanderais d’utiliser CMake plutôt que de lancer vos propres Makefiles et d’utiliser des outils automatiques. CMake présente de nombreux avantages utiles et faciles à utiliser, même s’il s’agit d’un projet simple. Par exemple, CMake créera un programme d’installation NSIS, gérera la compilation production / débogage et disposera d’un cadre de test intéressant. L’un des coups que j’ai eu était qu’il était assez difficile de trouver de vrais exemples d’utilisation. Tellement de logiciels open source utilisent autotools que des exemples du monde réel sont faciles à trouver. Cependant, si vous téléchargez la source CMake, il existe de nombreux exemples dans les répertoires Example et Test.

En d’autres termes, le jus vaut la peine d’être pressé.

Comme conseil principal, je suggère d’utiliser libtool, autoconf et automake; ils rendent la compilation croisée très facile et beaucoup plus facile que CMake.

Si vous choisissez la route à la main, je vous suggérerais de choisir des cibles différentes. Changer de fichier makefile a tendance à masquer des erreurs évidentes dans le fichier Makefile, par exemple des objects utilisés en double avec des règles différentes. Exemple: l’object foo.o est compilé pour la cible DLL et pour la cible .so, mais avec des indicateurs différents. Si quelqu’un change de fichier Makefile, le fichier .o existant avec des indicateurs incorrects est utilisé, ce qui interrompt la construction. Si vous utilisez un Makefile, cela deviendra évident grâce aux conflits de règles.