makefile詳解-實例版 四個特殊符號的意義@、$@、$^、$< makefile編譯子目錄 makefile中 rm、@rm 和 -rm的區別

雖然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使用場景。

其他

  makefile中 rm、@rm 和 -rm的區別

  • @告訴make在運行時不要回顯要輸出的配方。
  • -告訴make忽略配方的返回值

參考

cmake版本可以參見https://www.cnblogs.com/zhjh256/p/15637621.html 

https://www.gnu.org/prep/standards/html_node/index.html#SEC_Contents  GNU編碼規範

https://makefiletutorial.com/

https://www.gnu.org/software/make/manual/make.html

https://stackoverflow.com/questions/5541946/cflags-ccflags-cxxflags-what-exactly-do-these-variables-control

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章