GNU make:使用生成的头文件生成自动依赖项

Che*_*evy 11 makefile gnu-make auto-generate

所以我遵循了高级自动依赖生成论文 -

Makefile:

SRCS := main.c foo.c

main: main.o foo.o

%.o: %.c
    $(CC) -MMD -MG -MT '$@ $*.d' -c $< -o $@
    cp $*.d $*.tmp
    sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$$;;' \
        -e '/^$$/d' -e 's;$$; :;' < $*.tmp >> $*.d
    rm $*.tmp

clean::
    -rm *.o *.d main

-include $(SRCS:.c=.d)
Run Code Online (Sandbox Code Playgroud)

main.c:

#include "foo.h"

int main(int argc, char** argv) {
  foo() ;
  return 0 ;
}
Run Code Online (Sandbox Code Playgroud)

foo.h:

#ifndef __FOO_H__
#define __FOO_H__

void foo() ;

#endif
Run Code Online (Sandbox Code Playgroud)

- 它就像一个魅力.


但是当foo.h成为生成的文件时 -

Makefile文件:

...

HDRS := foo.h

$(HDRS):
    mk_header.sh $*

clean::
    -rm $(HDRS)
...
Run Code Online (Sandbox Code Playgroud)

mk_header.sh:

#!/bin/bash
UP=$(tr "[:lower:]" "[:upper:]" <<< $1)

cat <<EOF > $1.h
#ifndef __${UP}_H__
#define __${UP}_H__

void $1() ;

#endif
EOF
Run Code Online (Sandbox Code Playgroud)

我第一次运行make,main.d尚未生成,因此foo.h不被视为先决条件,因此未生成:

$ ls
foo.c  main.c  Makefile  mk_header.sh*

$ make
cc -MMD -MG -MT 'main.o main.d' -c main.c -o main.o
cp main.d main.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$;;' \
    -e '/^$/d' -e 's;$; :;' < main.tmp >> main.d
rm main.tmp
cc -MMD -MG -MT 'foo.o foo.d' -c foo.c -o foo.o
cp foo.d foo.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$;;' \
    -e '/^$/d' -e 's;$; :;' < foo.tmp >> foo.d
rm foo.tmp
cc   main.o foo.o   -o main

$ ls
foo.c  foo.d  foo.o  
main*  main.c  main.d  main.o  
Makefile  mk_header.sh*
Run Code Online (Sandbox Code Playgroud)

仅在第二次调用时make,foo.h生成,因此另一个构建级联.

$ make
./mk_header.sh foo
cc -MMD -MG -MT 'main.o main.d' -c main.c -o main.o
cp main.d main.tmp
sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$;;' \
    -e '/^$/d' -e 's;$; :;' < main.tmp >> main.d
rm main.tmp
cc   main.o foo.o   -o main

$ ls
foo.c  foo.d  foo.h  foo.o  
main*  main.c  main.d  main.o  
Makefile  mk_header.sh*
Run Code Online (Sandbox Code Playgroud)

只有在那之后才make意识到:

$ make
make: `main' is up to date.
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:是否有办法扩展上述文件所建议的配方,以允许生成的头文件,而不会消除通过在包含*.d片段时不必重新评估整个make树而实现的性能增益?

Che*_*evy 12

问题是*.d必须所有标头生成完成执行Makefile-fragments生成.以这种方式,可以使用make依赖项强制正确的顺序:

SRCS := main.c foo.c
HDRS := foo.h

main: main.o foo.o

%.o: %.c | generated_headers
    $(CC) -MMD -MG -MT '$@ $*.d' -c $< -o $@
    cp $*.d $*.tmp
    sed -e 's;#.*;;' -e 's;^[^:]*: *;;' -e 's; *\\$$;;' \
        -e '/^$$/d' -e 's;$$; :;' < $*.tmp >> $*.d
    rm $*.tmp

-include $(SRCS:.c=.d)

$(HDRS):
    mk_header.sh $*

generated_headers: $(HDRS)

clean:
    -rm $(HDRS) *.o *.d main

.PHONY: clean generated_headers
Run Code Online (Sandbox Code Playgroud)

笔记:

  1. 我使用仅限订单的依赖项.

  2. 此解决方案具有相当的可扩展性:每个生成标头规则只需要是generated_headers .PHONY目标的先决条件.假设标题生成规则被正确写入,一旦正确生成,满足generated_headers目标应该是无操作.

  3. 无法编译单个对象,即使该对象不需要任何生成的头,也不能首先生成项目的所有生成的头.虽然这在技术上是合理的,但您的开发人员会抱怨.

    所以你应该考虑有一个FAST_AND_LOOSE标志,这将关闭此功能:

    %.o: %.c | $(if $(FAST_AND_LOOSE),,generated_headers)
        ...
    
    Run Code Online (Sandbox Code Playgroud)

    因此,开发人员可以发出:

    make FAST_AND_LOOSE=1 main.o
    
    Run Code Online (Sandbox Code Playgroud)