how - Usando make para compilación multiplataforma
makefile executable linux (5)
Como alguien que ha usado tanto autotools como CMake, recomendaría usar CMake sobre rodar tus propios Makefiles y usar autotools. CMake tiene muchos beneficios útiles y fáciles de usar, incluso si se trata de un proyecto simple. Por ejemplo, CMake creará un instalador de NSIS, administrará la producción frente a la compilación de depuración y tendrá un marco de prueba agradable. El único golpe que tuve fue que era un poco difícil encontrar ejemplos reales de cómo usarlo. Tanto el software de código abierto utiliza autotools que los ejemplos del mundo real son fáciles de encontrar. Sin embargo, si descarga la fuente de CMake, hay muchos ejemplos en el directorio de ejemplo y el directorio de prueba.
En otras palabras, el jugo vale la pena apretarlo.
Actualmente estoy desarrollando un proyecto de C en Linux y Win32. El ''entregable'' es una biblioteca compartida, y todo el desarrollo se realiza bajo Linux con la cadena de herramientas GNU. Estoy utilizando un Makefile para compilar la biblioteca compartida.
De vez en cuando tengo que construir un .dll bajo Win32 desde el mismo src.
He instalado MinGW en la caja de Win32, de modo que puedo usar make y obtener muchas menos quejas del compilador (en comparación con MSVC). Estoy en una etapa donde el código src se compila en ambas plataformas
Pero el Makefile de Linux y el Makefile de Win32 son diferentes. Tengo curiosidad por saber cómo manejar mejor esto: debería:
tiene 2 makefiles, por ejemplo, Makefile para linux y Makefile.WIN32 y luego ejecuta
make -f Makefile.WIN32
en el cuadro de WindowsDebería hacer un objetivo diferente en un solo Makefile y hacer algo como
make WIN32
en el cuadro de Windows¿Debería tirar y usar CMake (vale la pena exprimirlo para un proyecto tan simple, es decir, una biblioteca compartida)?
Como consejo principal, sugiero usar libtool, autoconf y automake; Hacen la compilación cruzada muy fácil y mucho más fácil que CMake.
Si va por la ruta hecha a mano, sugeriría ir con diferentes objetivos. El cambio entre makefiles tiende a ocultar errores obvios en Makefiles, por ejemplo, objetos usados de forma duplicada con diferentes reglas. Ejemplo: El objeto foo.o se compila para el destino de DLL y para el destino .so, pero con indicadores diferentes. Si alguien cambia Makefiles, se usa el archivo .o existente con banderas incorrectas, rompiendo la construcción. Si está utilizando un Makefile, esto se volverá obvio a través de los conflictos de reglas.
Tuve un problema similar hace unos años, y descubrí que cmake es mucho más fácil para la compilación multiplataforma Y utilizará cualquier compilador nativo para ese sistema. La sintaxis es más clara y abstrae los detalles que son innecesarios en su mayor parte (a veces eso se interpuso en el camino, pero generalmente había una forma de evitarlo)
Use un solo archivo de creación y ponga los específicos de la plataforma en conditionals , por ejemplo,
ifeq ($(OS),Windows_NT)
DLLEXT := .dll
else
DLLEXT := .so
endif
DLL := libfoo$(DLLEXT)
lib : $(DLL)
Uso UNAME := $(shell uname)
dentro de mi Makefile
para detectar la plataforma (Linux o MS-Windows).
Proporciono a continuación un ejemplo completo basado en make
y gcc
para construir una biblioteca compartida: *.so
o *.dll
según la plataforma.
El ejemplo es básico / simple / estúpido para ser más comprensible :-)
Para usar make
y gcc
en MS-Windows, Cygwin o MinGW se pueden instalar.
El ejemplo usa cinco archivos:
├── app
│ └── Makefile
│ └── main.c
└── lib
└── Makefile
└── hello.h
└── hello.c
Los 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 $<'' => compile the first depended file (main.cpp)
# ''-fPIC'' => Position-Independent Code (required for shared lib)
clean:
rm -f *.o *.so *.dll *.exe
El código fuente
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";
}
La construcción
Corrija la copia y pegado de Makefiles
(reemplace los espacios iniciales por tabulación).
> sed -i ''s/^ *//t/'' */Makefile
El comando make
es el mismo en ambas plataformas. La salida dada es para MS-Windows (líneas innecesarias eliminadas).
> 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 carrera
La aplicación requiere saber dónde está la biblioteca compartida.
En MS-Windows, la forma simple / básica / estúpida es copiar la biblioteca donde está la aplicación:
> cp -v lib/hello.dll app
`lib/hello.dll'' -> `app/hello.dll''
En Linux, use la LD_LIBRARY_PATH
entorno LD_LIBRARY_PATH
:
> export LD_LIBRARY_PATH=lib
La línea de comando de ejecución y la salida son las mismas en ambas plataformas:
> app/app.exe
hello