Using make for cross platform compilation
Use a single make file and put the platform-specifics in conditionals, eg
ifeq ($(OS),Windows_NT) DLLEXT := .dllelse DLLEXT := .soendifDLL := libfoo$(DLLEXT)lib : $(DLL)
I use UNAME := $(shell uname)
within my Makefile
to detect the platform (Linux or MS-Windows).
I provide below a complete example based on make
and gcc
to build a shared library: *.so
or *.dll
depending on the platform.
The example is basic/simple/stupid to be more understandable :-)
To use make
and gcc
on MS-Windows, Cygwin or MinGW can be installed.
The example uses five files:
├── app │ └── Makefile │ └── main.c └── lib └── Makefile └── hello.h └── hello.c
The 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 ../libclean: rm -f *.o *.so *.dll *.exe
lib/Makefile
UNAME := $(shell uname)ifeq ($(UNAME), Linux)TARGET = libhello.soelseTARGET = hello.dllendif$(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 $<' => compile the first depended file (main.cpp) # '-fPIC' => Position-Independent Code (required for shared lib)clean: rm -f *.o *.so *.dll *.exe
The source code
app/main.c
#include "hello.h" //hello()#include <stdio.h> //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";}
The build
Fix the copy-paste of Makefiles
(replace leading spaces by tabulation).
> sed -i 's/^ */\t/' */Makefile
The make
command is the same on both platforms. The given output is for MS-Windows (unnecessary lines removed).
> cd lib> make clean> makegcc -o hello.o -c hello.c -fPICgcc -o hello.dll hello.o -shared> cd ../app> make clean> makegcc -o main.o -c main.c -I ../libgcc -o app.exe main.o -L../lib -lhello
The run
The application requires to know where is the shared library.
On MS-Windows, the simple/basic/stupid way is to copy the library where the application is:
> cp -v lib/hello.dll app`lib/hello.dll' -> `app/hello.dll'
On Linux, use the LD_LIBRARY_PATH
environment variable:
> export LD_LIBRARY_PATH=lib
The run command line and output are the same on both platforms:
> app/app.exehello
As somebody who has used both autotools and CMake, I would recommend using CMake over rolling your own Makefiles and using autotools. CMake has so many useful, easy to use benefits, even if it is a simple project. For example, CMake will create an NSIS installer, manage production vs. debug compilation and has a nice testing framework. The one knock I had was that it was kind of hard to find real examples of how to use it. So much open source software uses autotools that realworld examples for it are easy to find. However, if you download the CMake source, there are lots of examples in the Example directory and Test directory.
In other words, the Juice is worth the squeeze.