makepp/make for prebuild target

前言:什么是makepp

makepp 关键字介绍参考这里。
makepp介绍参考这里。

makepp之所以称为makepp,是因为其针对C++设计了特定的支持,比如支持自动将cpp file include的头文件作为该cpp文件的依赖,换言之如果该头文件是需要用脚本来生成的话,cpp文件在被编译之前,调用脚本生成该头文件的目标-依赖-命令 块会得到执行,参考这里和这里:“automatic scanning for include files”。

makepp之于make,有点儿类似于CPP之于C,makepp几乎100%兼容make,只是在make的基础上增加了一些新的特性来方便使用。

遇到的问题

考虑一个场景,某些cpp文件是通过脚本生成的,并且生成的文件数目还很多,所以想用$(wildcard )来模糊匹配出生成的所有cpp文件,想用make/makepp来构建工程。
Linux 实验环境如下:
make版本: /home/utils/make-3.81/bin/make
makepp版本: /home/utils/makepp-2.0/bin/makepp

 main.cpp

#include 
using namespace std;

int main() {
 cout << "hello world" << endl;

}
### gen.sh

#!/bin/bash
mkdir gen
cd gen
echo "" > a.cpp
echo "" > b.cpp

不可行的方案

gen:
    gen.sh

CPPFILES = main.cpp
CPPFILES += $(wildcard gen/*.cpp)

main : $(CPPFILES)
    @echo CPPFILES = $(CPPFILES)
    @echo folder = $(folder)
    /home/utils/gcc-5.2.0/bin/g++ $(CPPFILES) -o main

$(phony clean):
    rm -rf gen main

$ makepp main
makepp: Loading makefile /home/<…>/File/cpp/makepp/Makeppfile
makepp: Entering directory /home/<…>/File/cpp/makepp
CPPFILES = main.cpp
folder =
/home/utils/gcc-5.2.0/bin/g++ main.cpp -o main
makepp: 1 file updated and 0 phony targets built

可以看到a.cpp和b.cpp 并没有被编译。这主要是因为求CPPFILES这个变量的值的时候,gen这个target还没有被执行(在本例中其压根不会被执行),所以$(wildcard )执行结束之后结果为空。

可行方案

make 方案

### Makefile
$(shell gen.sh)

CPPFILES = main.cpp
CPPFILES += $(wildcard gen/*.cpp)

main : $(CPPFILES)
    @echo CPPFILES = $(CPPFILES)
    /home/utils/gcc-5.2.0/bin/g++ $(CPPFILES) -o main

$(phony clean):
    rm -rf gen main

$ make main
make: Warning: File `gen/a.cpp’ has modification time 0.0044 s in the future
CPPFILES = main.cpp gen/a.cpp gen/b.cpp
/home/utils/gcc-5.2.0/bin/g++ main.cpp gen/a.cpp gen/b.cpp -o main
make: warning: Clock skew detected. Your build may be incomplete.

注意此时有warning,不确定是否有影响。

makepp 方案

### Makeppfile
gen:
    gen.sh
#prebuild gen
folder := $(shell echo $(prebuild gen))

CPPFILES = main.cpp
CPPFILES += $(wildcard gen/*.cpp)

main : $(CPPFILES)
    @echo CPPFILES = $(CPPFILES)
    @echo folder = $(folder)
    /home/utils/gcc-5.2.0/bin/g++ $(CPPFILES) -o main

$(phony clean):
    rm -rf gen main

pdx-xterm-24:/home/<…>/File/cpp/makepp> makepp main
makepp: Loading makefile /home/<…>/File/cpp/makepp/Makeppfile
makepp: Entering directory /home/<…>/File/cpp/makepp
gen.sh
makepp: Scanning /home/<…>/File/cpp/makepp/gen/a.cpp
makepp: Scanning /home/<…>/File/cpp/makepp/gen/b.cpp
CPPFILES = main.cpp gen/a.cpp gen/b.cpp
folder = gen
/home/utils/gcc-5.2.0/bin/g++ main.cpp gen/a.cpp gen/b.cpp -o main
makepp: 2 files updated and 0 phony targets built

  1. 可以直接把folder := $(shell echo $(prebuild gen)) 换为 prebuild gen, 这也是更加推荐的方式。此处的prebuild关键字的介绍参考这里。
  2. 也可以在wildcard那里修改:CPPFILES += $(wildcard $(prebuild gen)/*.cpp),这是第二推荐的方式, $(prebuild )函数的用法参考这里。
  3. folder := $(shell echo $(prebuild gen)), 注意此处的 :=, 这是很重要的,对于makepp,也可以使用 ;=, 参考这里对;的介绍,其 和 := 的作用相同,唯一不同的是右边的表达式只会被执行一次并记录下来,对于那些开销大的命令比较有用。后续如果还有相同的表达式,会直接使用记录值来减小开销。

如果此处改为 =, 会产生奇怪的现象,我目前还无法解释:

### Makeppfile2
gen:
    gen.sh
#prebuild gen
folder = $(shell echo $(prebuild gen))

CPPFILES = main.cpp
CPPFILES += $(wildcard gen/*.cpp)

main : $(CPPFILES)
    @echo CPPFILES = $(CPPFILES)
    @echo folder = $(folder)
    /home/utils/gcc-5.2.0/bin/g++ $(CPPFILES) -o main

$(phony clean):
    rm -rf gen main

$ makepp main
makepp: Loading makefile /home/<…>/File/cpp/makepp/Makeppfile
makepp: Entering directory /home/<…>/File/cpp/makepp
gen.sh
makepp: Scanning /home/<…>/File/cpp/makepp/gen/a.cpp’
makepp: Scanning `/home/<…>/File/cpp/makepp/gen/b.cpp’
CPPFILES = main.cpp
folder = gen
/home/utils/gcc-5.2.0/bin/g++ main.cpp gen/a.cpp gen/b.cpp -o main
makepp: 2 files updated and 0 phony targets built

后记

  1. 如果想要在Makefile中打印string到屏幕,可以使用 $(info )来打印变量的名字,或者打印字符串。除此之外还有$(warning ) 来打印warning,warning的好处是会显示该warning在Makefile中的具体位置;另外还有$(error )来提示错误并退出。
  2. 关于make的$(shell )函数, 这个函数是执行shell命令,返回值是该命令的输出,即意味着这条命令的输出并不会像真的在shell执行那样把输出打印在屏幕上,而只是作为返回值。如果想要打印该返回值的话,可以使用$(info <…>)函数来打印。
  3. 关于make方案中的Warning: File `gen/a.cpp’ has modification time 0.0044 s in the future, 简单查找了下相关的文章,参考这里。用我们上面的Makefile举例来说,如果用touch命令把可执行文件main的时间戳改成未来的某个时间,此时运行make main的话,会出现类似的warning。并且,假如你此时修改了main.cpp的话,运行make main,main也不会被更新,因为在增量编译的背景下,由于main的时间戳是未来的某个时间,意味着其仍然比main.cpp新,make会认为此时不需要更新main。不过这个解释好像在我上面的例子中不适用,并且即使我make clean之后,上面的warning仍然存在。
  4. 想要查看一个文件的具体的时间,可以使用shell命令:stat .
  5. similar interesting issue,也是关于执行顺序的一个例子:https://stackoverflow.com/questions/53603406/makefile-with-generated-c-source-files
    5.1 这涉及到Makefile remade, 参考 How Makefiles Are Remade

你可能感兴趣的:(linux,makefile,makeppfile)