雖然cmake已經很成熟了,但是make和Ninja(pg 16採用Ninja而不是cmake)仍然廣泛在使用中,並且相比cmake,make更加的透明。可以說掌握makefile是linux下開發從入門到進階第一步。
#------------------------------------------------------------------------- # # Makefile for backend/utils # # Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # # src/backend/utils/Makefile # #------------------------------------------------------------------------- subdir = src/backend/utils top_builddir = ../../.. include $(top_builddir)/src/Makefile.global OBJS = fmgrtab.o SUBDIRS = adt cache error fmgr hash init mb misc mmgr resowner sort time # location of Catalog.pm catalogdir = $(top_srcdir)/src/backend/catalog include $(top_srcdir)/src/backend/common.mk all: distprep probes.h generated-header-symlinks distprep: fmgr-stamp errcodes.h .PHONY: generated-header-symlinks generated-header-symlinks: $(top_builddir)/src/include/utils/header-stamp $(top_builddir)/src/include/utils/probes.h $(SUBDIRS:%=%-recursive): fmgr-stamp errcodes.h # fmgr-stamp records the last time we ran Gen_fmgrtab.pl. We don't rely on # the timestamps of the individual output files, because the Perl script # won't update them if they didn't change (to avoid unnecessary recompiles). fmgr-stamp: Gen_fmgrtab.pl $(catalogdir)/Catalog.pm $(top_srcdir)/src/include/catalog/pg_proc.dat $(top_srcdir)/src/include/access/transam.h $(PERL) -I $(catalogdir) $< --include-path=$(top_srcdir)/src/include/ $(top_srcdir)/src/include/catalog/pg_proc.dat touch $@ errcodes.h: $(top_srcdir)/src/backend/utils/errcodes.txt generate-errcodes.pl $(PERL) $(srcdir)/generate-errcodes.pl $< > $@ ifneq ($(enable_dtrace), yes) probes.h: Gen_dummy_probes.sed endif # We editorialize on dtrace's output to the extent of changing the macro # names (from POSTGRESQL_foo to TRACE_POSTGRESQL_foo) and changing any # "char *" arguments to "const char *". probes.h: probes.d ifeq ($(enable_dtrace), yes) $(DTRACE) -C -h -s $< -o [email protected] sed -e 's/POSTGRESQL_/TRACE_POSTGRESQL_/g' \ -e 's/( *char \*/(const char */g' \ -e 's/, *char \*/, const char */g' [email protected] >$@ rm [email protected] else sed -f $(srcdir)/Gen_dummy_probes.sed $< >$@ endif # These generated headers must be symlinked into builddir/src/include/, # using absolute links for the reasons explained in src/backend/Makefile. # We use header-stamp to record that we've done this because the symlinks # themselves may appear older than fmgr-stamp. $(top_builddir)/src/include/utils/header-stamp: fmgr-stamp errcodes.h prereqdir=`cd '$(dir $<)' >/dev/null && pwd` && \ cd '$(dir $@)' && for file in fmgroids.h fmgrprotos.h errcodes.h; do \ rm -f $$file && $(LN_S) "$$prereqdir/$$file" . ; \ done touch $@ # probes.h is handled differently because it's not in the distribution tarball. $(top_builddir)/src/include/utils/probes.h: probes.h cd '$(dir $@)' && rm -f $(notdir $@) && \ $(LN_S) "../../../$(subdir)/probes.h" . .PHONY: install-data install-data: errcodes.txt installdirs $(INSTALL_DATA) $(srcdir)/errcodes.txt '$(DESTDIR)$(datadir)/errcodes.txt' installdirs: $(MKDIR_P) '$(DESTDIR)$(datadir)' .PHONY: uninstall-data uninstall-data: rm -f $(addprefix '$(DESTDIR)$(datadir)'/, errcodes.txt) # fmgroids.h, fmgrprotos.h, fmgrtab.c, fmgr-stamp, and errcodes.h are in the # distribution tarball, so they are not cleaned here. clean: rm -f probes.h maintainer-clean: clean rm -f fmgroids.h fmgrprotos.h fmgrtab.c fmgr-stamp errcodes.h
$(call recurse,clean)
在任何稍微複雜一點的c/c++應用中,Makefile(M大寫)幾乎是必不可少的(除非採用cmake工程),它可以用來幫助自動化的判斷哪些源程序需要重新編譯。典型的應用通常是這樣的:
make程序有很多版本的實現,通常使用最廣泛的是GNU make,它在linux下是標準make實現,目前主要版本是3和4,可以通過make --version查看版本。
[root@lightdb1 ~]# make --version GNU Make 3.82 Built for x86_64-redhat-linux-gnu Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
一般configure以及三方構建對make版本沒有要求(cmake、gcc則有版本要求居多)。
注:在大型系統中,Makefile通常不是完全人工硬編碼,而是通過automake基於configure機制從模板文件創建。
configure
對於開源庫和稍微複雜一點的系統,configure通常是採用源碼分發執行的第一個步驟。大到postgresql、小至sysbench,通常都執行./configure檢查依賴項目,並打印出每一項的符合情況,然後生成make所需的最終Makefile。
$ ./configure
The above command makes the shell run the script named configure which exists in the current directory.
The configure script basically consists of many lines which are used to check some details about the machine on which the software is going to be installed. This script checks for lots of dependencies on your system. For the particular software to work properly, it may be requiring a lot of things to be existing on your machine already.
When you run the configure script you would see a lot of output on the screen , each being some sort of question and a respective yes/no as the reply. If any of the major requirements are missing on your system, the configure script would exit and you cannot proceed with the installation, until you get those required things. ./configure may fail if it finds that dependencies are missing.
The main job of the configure script is to create a Makefile. This is a very important file for the installation process. Depending on the results of the tests (checks) that the configure script performed it would write down the various steps that need to be taken (while compiling the software) in the file named Makefile.
[zjh@hs-10-20-30-193 lightdb13.3-21.1]$ ./configure --help `configure' configures PostgreSQL 13.3 to adapt to many kinds of systems. Usage: ./configure [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print `checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for `--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or `..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [/usr/local/pgsql] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, `make install' will install all the files in `/usr/local/pgsql/bin', `/usr/local/pgsql/lib' etc. You can specify an installation prefix other than `/usr/local/pgsql' using `--prefix', for instance `--prefix=$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/postgresql] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-integer-datetimes obsolete option, no longer supported --enable-nls[=LANGUAGES] enable Native Language Support --disable-rpath do not embed shared library search path in executables --disable-spinlocks do not use spinlocks --disable-atomics do not use atomic operations --enable-debug build with debugging symbols (-g) --enable-profiling build with profiling enabled --enable-coverage build with coverage testing instrumentation --enable-dtrace build with DTrace support --enable-tap-tests enable TAP tests (requires Perl and IPC::Run) --enable-depend turn on automatic dependency tracking --enable-cassert enable assertion checks (for debugging) --disable-thread-safety disable thread-safety in client libraries --disable-largefile omit support for large files Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-extra-version=STRING append STRING to version --with-template=NAME override operating system template --with-includes=DIRS look for additional header files in DIRS --with-libraries=DIRS look for additional libraries in DIRS --with-libs=DIRS alternative spelling of --with-libraries --with-pgport=PORTNUM set default port number [5432] --with-blocksize=BLOCKSIZE set table block size in kB [8] --with-segsize=SEGSIZE set table segment size in GB [1] --with-wal-blocksize=BLOCKSIZE set WAL block size in kB [8] --with-CC=CMD set compiler (deprecated) --with-llvm build with LLVM based JIT support --with-icu build with ICU support --with-tcl build Tcl modules (PL/Tcl) --with-tclconfig=DIR tclConfig.sh is in DIR --with-perl build Perl modules (PL/Perl) --with-python build Python modules (PL/Python) --with-gssapi build with GSSAPI support --with-krb-srvnam=NAME default service principal name in Kerberos (GSSAPI) [postgres] --with-pam build with PAM support --with-bsd-auth build with BSD Authentication support --with-ldap build with LDAP support --with-bonjour build with Bonjour support --with-openssl build with OpenSSL support --with-pmem build with PMEM support --with-selinux build with SELinux support --with-systemd build with systemd support --without-readline do not use GNU Readline nor BSD Libedit for editing --with-libedit-preferred prefer BSD Libedit over GNU Readline --with-uuid=LIB build contrib/uuid-ossp using LIB (bsd,e2fs,ossp) --with-ossp-uuid obsolete spelling of --with-uuid=ossp --with-libxml build with XML support --with-libxslt use XSLT support when building contrib/xml2 --with-system-tzdata=DIR use system time zone data in DIR --without-zlib do not use Zlib --with-gnu-ld assume the C compiler uses GNU ld [default=no] Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a nonstandard directory <lib dir> LIBS libraries to pass to the linker, e.g. -l<library> CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if you have headers in a nonstandard directory <include dir> CXX C++ compiler command CXXFLAGS C++ compiler flags LLVM_CONFIG path to llvm-config command CLANG path to clang compiler to generate bitcode CPP C preprocessor PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path ICU_CFLAGS C compiler flags for ICU, overriding pkg-config ICU_LIBS linker flags for ICU, overriding pkg-config XML2_CONFIG path to xml2-config utility XML2_CFLAGS C compiler flags for XML2, overriding pkg-config XML2_LIBS linker flags for XML2, overriding pkg-config LDFLAGS_EX extra linker flags for linking executables only LDFLAGS_SL extra linker flags for linking shared libraries only PERL Perl program PYTHON Python program MSGFMT msgfmt program for NLS TCLSH Tcl interpreter program (tclsh) Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to <https://github.com/hslightdb>. PostgreSQL home page: <https://www.hs.net/lightdb/>.
config.status
================================
config.log
Makefile語法
make程序默認從當前目錄的Makefile讀取配置,也可以通過-f指定文件。如果是一個工程由很多子工程,每個子工程有自己的makefile,可以通過-I(大寫i)選項包含目錄(這種通常是遞歸)。
Makefile核心部分由一系列規則組成(最簡單的情況下,只由規則組成):
targets: prerequisites|components
command
command
command
# 或
targets ...: target-pattern: prereq-patterns ...
command
command
command
從例子可以看出,Makefile一般的格式是:
target:components
rule
第一行表示的是依賴關係,第二行是規則,特別要注意,rule這行必須是TAB鍵開頭。
targets是文件名,由空格隔開,一般一個規則一個文件名。其中除了標準規則外,還包括靜態規則、隱示規則、模式規則、雙冒號規則。
commands則用來生成目標文件,可以是任何shell能執行的命令,包括自定義程序、perl、甚至java。它必須tab開頭,不能空格(跟yaml規則類似,約定俗成)。如果需要多個命令才能完成,除了&&外,可以多個命令獨立各自一行。
prerequisites/components也是文件名,在爲目標運行命令之前,這些文件必須已經存在,也就是依賴。如果依賴文件不存在,make會先搜索有沒有對應的target,有的話先執行,如果依賴的target又依賴其它target,則一路遞歸下去。如下所示:
blah: blah.o cc blah.o -o blah # Runs third blah.o: blah.c cc -c blah.c -o blah.o # Runs second blah.c: echo "int main() { return 0; }" > blah.c # Runs first
clean:
rm -rf blah
如果執行make blah,會首先執行blah.c,再執行blah.o,最後執行blah。注1:默認情況下,make會運行第一個target(clean【用來清理目標文件】、all(用來一次性運行多個目標)這些只是約定俗稱的習慣用法,並不是make的保留關鍵字)。第二次運行的時候,如果blah.c沒有修改過,則不會重新執行,否則會重新執行。
上面說到過,一個targets可以有多個文件,此時對於每個文件,command都會爲其執行一遍,相當於for循環。要在command中引用目標名稱,可以使用$@內置變量。如下:
f1.o f2.o: echo $@
定義變量
有時候Makefile程序比較複雜,就可以定義一些變量。變量必須是字符串,在定義中,字符串一般是不雙引號括起來的,這個要注意下。可以通過$()或${}引用(和velocity有點類似)。
files = file1 file2 some_file: $(files) echo "Look at this variable: " ${files} touch some_file file1: touch file1 file2: touch file2
file3 file4:
echo $@ # 內置變量$@代表target name clean: rm -f file1 file2 some_file
=和:=的關係
=
在真正執行命令的時候纔會對變量求值,所以變量值可能會在中間因爲其他引用的其他變量被改變而不是預期的。
=:
在賦值的時候直接對變量求值,以後如果不重新賦值是不會變化的。
自動變量和通配符
$(TARGET):$(CXX_FILES) $(SRC_DIRS) $(info target: $@) $(info all: $^) $(info first: $<) $(info SRC_DIRS_all: $(SRC_DIRS)) $(CC) -o $@ $^ $(INC_DIRS) $(CXXFLAGS) $(LIBS)
$@ 表示目標文件
$^ 表示所有的依賴文件
$< 表示第一個依賴文件
$? 表示比目標還要新的依賴文件列表
詳細可參見http://www.360doc.com/content/21/1111/12/18945873_1003705062.shtml
hey: one two # Outputs "hey", since this is the first target echo $@ # Outputs all prerequisites newer than the target echo $? # Outputs all prerequisites echo $^ touch hey one: touch one two: touch two clean: rm -f hey one two
thing_wrong := *.o # Don't do this! '*' will not get expanded thing_right := $(wildcard *.o) # *主要用於文件搜索。可以用在target, prerequisites和wildcard函數中。但是不建議直接用於變量定義和直接在依賴中引用,否則如果沒有匹配的話,會被當做本文量處理 all: one two three four # Fails, because $(thing_wrong) is the string "*.o" one: $(thing_wrong) # Stays as *.o if there are no files that match this pattern :( two: *.o # Works as you would expect! In this case, it does nothing. three: $(thing_right) # Same as rule three four: $(wildcard *.o)
%則主要用於通配符,和sql like有點類似。
objects = foo.o bar.o all.o all: $(objects) # These files compile via implicit rules # Syntax - targets ...: target-pattern: prereq-patterns ... # In the case of the first target, foo.o, the target-pattern matches foo.o and sets the "stem" to be "foo". # It then replaces the '%' in prereq-patterns with that stem $(objects): %.o: %.c # 可以用來代替枚舉下列三個顯示target # foo.o: foo.c # bar.o: bar.c # all.o: all.c all.c: echo "int main() { return 0; }" > all.c %.c: touch $@ clean: rm -f *.c *.o all
隱示規則
- 編譯c程序。n.o目標如果未明確聲明,則會自動在n.c上執行$(CC) -c $(CPPFLAGS) $(CFLAGS)生成。
- 編譯c++程序。n.o目標如果未明確聲明,則會自動在n.cc和n.cpp上執行$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)生成。
- 鏈接對象文件。 n目標如果未明確聲明,則會自動在n.cc和n.cpp上執行$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)生成。
所以會自動使用一些變量(他們默認由具體的c編譯器定義,如gcc https://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules):
- CC
- CXX
- CFLAGS
- CXXFLAGS
- CPPFLAGS
- LDFLAGS
CC = gcc # Flag for implicit rules CFLAGS = -g # Flag for implicit rules. Turn on debug info # Implicit rule #1: blah is built via the C linker implicit rule # Implicit rule #2: blah.o is built via the C compilation implicit rule, because blah.c exists blah: blah.o blah.c: echo "int main() { return 0; }" > blah.c clean: rm -f blah*
命令執行
命令必須在單獨一行,這個java調用是一樣的。默認shell是/bin/sh,Linux下默認是/bin/bash。make中可通過SHELL修改。
SHELL=/bin/bash
all: cd .. # The cd above does not affect this line, because each command is effectively run in a new shell echo `pwd` # This cd command affects the next because they are on the same line cd ..;echo `pwd` # Same as above cd ..; \ echo `pwd`
shell執行的結果可以賦值給變量,如下:
# Find all the C and C++ files we want to compile # Note the single quotes around the * expressions. Make will incorrectly expand these otherwise. SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s') INC_DIRS := $(shell find $(SRC_DIRS) -type d)
條件判斷
foo = ok all: ifeq ($(foo), ok) echo "foo equals ok" else echo "nope" endif # 變量空值判斷 nullstring = foo = $(nullstring) # end of line; there is a space here all: ifeq ($(strip $(foo)),) echo "foo is empty after being stripped" endif ifeq ($(nullstring),) echo "nullstring doesn't even have spaces" endif
# 查看變量是否已定義 bar = foo = $(bar) all: ifdef foo echo "foo is defined" endif ifdef bar echo "but bar is not" endif
# 查看make的選項 bar = foo = $(bar) all: # Search for the "-i" flag. MAKEFLAGS is just a list of single characters, one per flag. So look for "i" in this case. ifneq (,$(findstring i, $(MAKEFLAGS))) echo "i was passed to MAKEFLAGS" endif
函數
函數一般通過$(fn, arguments)
or ${fn, arguments}調用,也可使用call顯示調用
,make包含很多內置函數。注意事項比較多,尤其是拼接和空格。
comma := , empty:= space := $(empty) $(empty) foo := a b c bar := $(subst $(space), $(comma) , $(foo)) all: # Output is ", a , b , c". Notice the spaces introduced @echo $(bar)
四個特殊符號的意義@、$@、$^、$<
$@、$^、$<
這三個分別表示:
$@ --代表目標文件(target)
$^ --代表所有的依賴文件(components)
$< --代表第一個依賴文件(components中最左邊的那個)。
main.out:main.o line1.o line2.o
g++ -o $@ $^
main.o:main.c line1.h line2.h
g++ -c $<
line1.o:line1.c line1.h
g++ -c $<
line2.o:line2.c line2.h
g++ -c $<
參見https://www.jianshu.com/p/9756f766700a最後。
Makefile文件中的 .PHONY 的作用
https://zhuanlan.zhihu.com/p/347929747
調用子目錄makefile
makefile編譯子目錄 包括調用xxx_src對應的xxx_src_test目錄執行測試用例。
https://zhuanlan.zhihu.com/p/362922473 include使用場景。
其他
- @告訴make在運行時不要回顯要輸出的配方。
- -告訴make忽略配方的返回值
參考
cmake版本可以參見https://www.cnblogs.com/zhjh256/p/15637621.html
https://www.gnu.org/prep/standards/html_node/index.html#SEC_Contents GNU編碼規範