Introducing GNU Build System (aka Autotools)
by $4
https://fourdollars.github.io/autotools/
git clone https://github.com/fourdollars/autotools.git
by $4
https://fourdollars.github.io/autotools/
git clone https://github.com/fourdollars/autotools.git
#include <stdio.h> int main(int argc, char* argv[]) { printf("Hello World!\n"); return 0; }
$ gcc hello.c -o hello
hello: hello.o gcc -o hello hello.o hello.o: hello.c gcc -c hello.c
$ make
target: dependencies ... ; command commands ...
hello: hello.o gcc -o hello hello.o hello.o: hello.c gcc -c hello.c假目標 .PHONY 以及 潛規則 Implicit Rules
hello: hello.o clean: ; $(RM) hello hello.o @echo done .PHONY: clean
%.o : %.c $(CC) -Wall -c $(CFLAGS) $(CPPFLAGS) -o $@ $< %.o : CFLAGS = -g
objects = program.o foo.o utils.o program : $(objects) cc -o program $(objects) $(objects) : defs.h
foo = $(bar) bar = $(ugh) ugh = Huh? all:;@echo $(foo)
x := foo y := $(x) bar x := later xy:;@echo $(x) $(y)
a := foo a ?= bar b := $(a) ab:;@echo $(a) $(b)
objects = main.o foo.o bar.o utils.o objects += another.o
files := $(wildcard *.c *.html) files:;@echo $(files)
subst := $(subst html,HTML,$(files)) subst:;@echo $(subst)
patsubst := $(patsubst %.c,%.o,$(files)) patsubst:;@echo $(patsubst)
list := 'a b c ' list:;@echo $(list) strip := $(strip $(list)) strip:;@echo $(strip)
suffix := $(suffix $(files)) suffix:;@echo $(suffix)
$(error This is error.) $(warning This is warning.) $(info This is info.)
ifeq ($(CC),gcc) CPPFLAGS = -Wall CFLAGS = -g endif
ifdef DEBUG CPPFLAGS = -Wall CFLAGS = -g endif
list_files = $(foreach item,$(1),$(info $(item))) info_msg = $(info $(1)) $(if $(findstring make.html, $(files)), \ $(call list_files, $(files)), \ $(call info_msg, 'I can not find make.html') \ )
/* hello_world.h */ #ifndef __HELLO_WORLD__ #define __HELLO_WORLD__ #ifdef __cplusplus extern "C" /* Or using G_BEGIN_DECLS from <glib.h> */ { #endif /** * @brief This is a hello world library. * * @return The reture value of printf. */ int hello_world(void); #ifdef __cplusplus } /* Or using G_END_DECLS from <glib.h> */ #endif #endif
/* hello_world.c */ #include <stdio.h> #include "hello_world.h" int hello_world(void) { return printf("Hello World!\n"); }
靜態函式庫 Static Library
$ gcc -c hello_world.c $ ar cr libhello_world.a hello_world.o
動態函式庫 Shared Library
$ gcc -c -fPIC hello_world.c $ gcc -shared -fPIC -o libhello_world.so hello_world.o
/* main.c */ #include <hello_world.h> int main(int argc, char* argv[]) { hello_world(); return 0; }
$ gcc -I. -c main.c $ gcc -static -o main main.o -L. -lhello_world $ ./main
$ gcc -I. -c main.c $ gcc -o main main.o -L. -lhello_world $ LD_LIBRARY_PATH=. ./main
SOURCES = hello_world.c HEADERS = hello_world.h CPPFLAGS += -fPIC PREFIX ?= /usr/local LIB = $(addprefix lib,$(patsubst %.c,%,$(firstword $(SOURCES)))) SHARED = $(addsuffix .so,$(LIB)) STATIC = $(addsuffix .a,$(LIB)) OBJS = $(patsubst %.c,%.o, $(SOURCES)) all: $(SHARED) $(STATIC) $(SHARED): $(OBJS) $(CC) -shared -fPIC -o $@ $^ $(STATIC): $(OBJS) $(AR) cr $@ $^ $(SOURCES): $(HEADERS) clean: $(RM) $(SHARED) $(STATIC) $(OBJS)
install: $(SHARED) $(STATIC) $(HEADERS) mkdir -p $(DESTDIR)$(PREFIX)/lib install $(SHARED) $(DESTDIR)$(PREFIX)/lib install $(STATIC) $(DESTDIR)$(PREFIX)/lib mkdir -p $(DESTDIR)$(PREFIX)/include install $(HEADERS) $(DESTDIR)$(PREFIX)/include uninstall: $(RM) $(DESTDIR)$(PREFIX)/lib/$(SHARED) $(RM) $(DESTDIR)$(PREFIX)/lib/$(STATIC) $(RM) $(foreach header,$(HEADERS), \ $(addprefix $(DESTDIR)$(PREFIX)/include/, $(header))) -rmdir $(DESTDIR)$(PREFIX)/lib/ -rmdir $(DESTDIR)$(PREFIX)/include/ -rmdir $(DESTDIR)$(PREFIX) .PHONY: clean uninstall
準備好 Makefile.am
bin_PROGRAMS = hello hello_SOURCES = hello.c
執行 autoscan 產生 configure.scan 再修改成 configure.ac
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS]) ↓ ↓ ↓ AC_INIT([hello], [1.0], [bugs@foo.bar]) ... AM_INIT_AUTOMAKE([foreign]) # Checks for programs.
執行 autoreconf -if 產生 configure
$ ./configure $ make $ make install
準備好 Makefile.am
lib_LTLIBRARIES = libhello_world.la libhello_world_la_SOURCES = hello_world.c include_HEADERS = hello_world.h
執行 autoscan 產生 configure.scan 再修改成 configure.ac
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS]) ↓ ↓ ↓ AC_INIT([libhello_world], [1.0], [bugs@foo.bar]) ... AM_INIT_AUTOMAKE([foreign]) LT_INIT # Checks for programs.
執行 autoreconf -if 產生 configure
$ ./configure $ make $ make install
利用 DESTDIR 安裝在指定目錄底下
$ export DESTDIR=$PWD $ make install DESTDIR=$PWD $ find ./usr/ ./usr/ ./usr/local ./usr/local/lib ./usr/local/lib/libhello_world.a ./usr/local/lib/libhello_world.so.0.0.0 ./usr/local/lib/libhello_world.la ./usr/local/lib/libhello_world.so ./usr/local/lib/libhello_world.so.0 ./usr/local/include ./usr/local/include/hello_world.h $ make uninstall DESTDIR=$PWD ...
原始碼的編譯可以跟原始碼分開在不同的目錄
$ make distclean # 要先清理一下原始碼目錄
$ mkdir build && cd build
$ ../configure && make
$ find
.
./libtool
./libhello_world.la
./config.log
./config.h
./.deps
./.deps/hello_world.Plo
./.libs
./.libs/libhello_world.a
./.libs/libhello_world.so.0.0.0
./.libs/libhello_world.la
./.libs/libhello_world.so
./.libs/libhello_world.so.0
./.libs/hello_world.o
./config.status
./hello_world.o
./hello_world.lo
./stamp-h1
./Makefile
不同類型的檔案可以安裝到各別指定的目錄底下
$ ../configure --prefix=/opt --libdir=/opt/superlib --includedir=/opt/exclude $ make && make install DESTDIR=$PWD $ find ./opt/ ./opt/ ./opt/exclude ./opt/exclude/hello_world.h ./opt/superlib ./opt/superlib/libhello_world.a ./opt/superlib/libhello_world.so.0.0.0 ./opt/superlib/libhello_world.la ./opt/superlib/libhello_world.so ./opt/superlib/libhello_world.so.0 $ ../configure --help ... Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [/usr/local] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, `make install' will install all the files in `/usr/local/bin', `/usr/local/lib' etc. You can specify an installation prefix other than `/usr/local' using `--prefix', for instance `--prefix=$HOME'. ...
$ info libtool ... 7.2 Libtool's versioning system =============================== ... Libtool's description of the interfaces that a program uses is simple: it encodes the least and the greatest interface numbers in the resulting binary (FIRST-INTERFACE, LAST-INTERFACE). ... So, libtool library versions are described by three integers: CURRENT The most recent interface number that this library implements. REVISION The implementation number of the CURRENT interface. AGE The difference between the newest and oldest interfaces that this library implements. In other words, the library implements all the interface numbers in the range from number 'CURRENT - AGE' to 'CURRENT'. ...
7.3 Updating library version information ======================================== ... 1. Start with version information of '0:0:0' for each libtool library. 2. Update the version information only immediately before a public release of your software. More frequent updates are unnecessary, and only guarantee that the current interface number gets larger faster. 3. If the library source code has changed at all since the last update, then increment REVISION ('C:R:A' becomes 'C:r+1:A'). 4. If any interfaces have been added, removed, or changed since the last update, increment CURRENT, and set REVISION to 0. 5. If any interfaces have been added since the last public release, then increment AGE. 6. If any interfaces have been removed or changed since the last public release, then set AGE to 0. ...
configure.ac
... # Before making a release, the LT_VERSION string should be modified. The # string is of the form c:r:a. Follow these instructions sequentially: # 1. If the library source code has changed at all since the last update, then # increment revision (‘c:r:a’ becomes ‘c:r+1:a’). # 2. If any interfaces have been added, removed, or changed since the last # update, increment current, and set revision to 0. # 3. If any interfaces have been added since the last public release, then # increment age. # 4. If any interfaces have been removed or changed since the last public # release, then set age to 0. AC_SUBST([LT_VERSION],[0:0:0]) ...
Makefile.am
... libhello_world_la_LDFLAGS = -version-info $(LT_VERSION) ...
如果沒有動到API,那麼應該改成 -version-info 0:1:0 的結果。
$ find ./usr ./usr ./usr/local ./usr/local/lib ./usr/local/lib/libhello_world.a ./usr/local/lib/libhello_world.la ./usr/local/lib/libhello_world.so ./usr/local/lib/libhello_world.so.0 ./usr/local/lib/libhello_world.so.0.0.1 ./usr/local/include ./usr/local/include/hello_world.h
如果只是新增API,那麼應該改成 -version-info 1:0:1 的結果。
$ find ./usr/ ./usr/ ./usr/local ./usr/local/lib ./usr/local/lib/libhello_world.a ./usr/local/lib/libhello_world.la ./usr/local/lib/libhello_world.so ./usr/local/lib/libhello_world.so.0 ./usr/local/lib/libhello_world.so.0.1.0 ./usr/local/include ./usr/local/include/hello_world.h
如果刪除或修改了API,那麼應該改成 -version-info 1:0:0 的結果。
$ find ./usr/ ./usr/ ./usr/local ./usr/local/lib ./usr/local/lib/libhello_world.a ./usr/local/lib/libhello_world.la ./usr/local/lib/libhello_world.so ./usr/local/lib/libhello_world.so.1 ./usr/local/lib/libhello_world.so.1.0.0 ./usr/local/include ./usr/local/include/hello_world.h