【问题标题】:Why makefile requests folder for generated file为什么makefile为生成的文件请求文件夹
【发布时间】:2020-09-29 04:25:15
【问题描述】:

我写了一个makefile来编译生成的文件

%.o: %.cc
    g++ -c $< -o $@

default: gen main.o

gen:
    touch main.cc

得到了

$ make default
touch main.cc
make: *** No rule to make target 'main.o', needed by 'default'.  Stop.

但是如果我为生成的 .cc 和 .o 文件添加一个文件夹,它就可以工作

obj/%.o: src/%.cc
    g++ -c $< -o $@

default: gen obj/main.o

gen:
    touch src/main.cc

为什么需要文件夹 obj 和 src

【问题讨论】:

    标签: makefile


    【解决方案1】:

    他们不是。这种行为来自make 的优化以及您的Makefile 对其所做的事情不真诚。

    make 读取一个目录时,它会散列所有目录内容并在此hash 中执行未来的文件查找。第一次启动make 时,没有main.cc 文件,因此目录哈希不包含main.cc 的条目。由于您的Makefile 生成了这个文件,但没有声明它,make 不知道有一个目标生成main.cc 文件,当它在哈希中查找时,该文件不存在。因此,您的隐含规则被拒绝为不可能:

    $ make -dr default
    ...
    Considering target file 'default'.
     File 'default' does not exist.
     Looking for an implicit rule for 'default'.
     No implicit rule found for 'default'.
      Considering target file 'gen'.
       File 'gen' does not exist.
       Finished prerequisites of target file 'gen'.
      Must remake target 'gen'.
    touch main.cc
    Putting child 0x55de3daaa6a0 (gen) PID 6075 on the chain.
    Live child 0x55de3daaa6a0 (gen) PID 6075
    Reaping winning child 0x55de3daaa6a0 PID 6075
    Removing child 0x55de3daaa6a0 PID 6075 from chain.
      Successfully remade target file 'gen'.
      Considering target file 'main.o'.
       File 'main.o' does not exist.
       Looking for an implicit rule for 'main.o'.
       Trying pattern rule with stem 'main'.
       Trying implicit prerequisite 'main.cc'.
       Trying pattern rule with stem 'main'.
       Trying implicit prerequisite 'main.cc'.
       Looking for a rule with intermediate file 'main.cc'.
        Avoiding implicit rule recursion.
       No implicit rule found for 'main.o'.
       Finished prerequisites of target file 'main.o'.
      Must remake target 'main.o'.
    make: *** No rule to make target 'main.o', needed by 'default'.  Stop.
    

    如果您 strace 进行此调用,您会注意到两件事:a)读取目录内容(用于查找和散列)的 getdents64 调用和 b)main.cc 根本没有 stat,意味着没有检查磁盘上的文件是否存在,因为它的存在是从缓存中解析的:

    $ strace make -r default
    ...
    stat(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
    openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
    fstat(3, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
    getdents64(3, /* 3 entries */, 32768)   = 80
    getdents64(3, /* 0 entries */, 32768)   = 0
    close(3)                                = 0
    ...
    stat("gen", 0x7fff4f66a3d0)             = -1 ENOENT (No such file or directory)
    stat("main.o", 0x7fff4f66a460)          = -1 ENOENT (No such file or directory)
    ... <-- locale-related calls for error message
    write(2, "make: *** No rule to make target"..., 71make: *** No rule to make target 'main.o', needed by 'default'.  Stop.
    

    现在,如果您重新运行相同的命令,它将成功,因为 main.cc 文件已经创建并且在目录被缓存时可用:

    $ make -dr default
    ...
    Considering target file 'default'.
     File 'default' does not exist.
     Looking for an implicit rule for 'default'.
     No implicit rule found for 'default'.
      Considering target file 'gen'.
       File 'gen' does not exist.
       Finished prerequisites of target file 'gen'.
      Must remake target 'gen'.
    touch main.cc
    Putting child 0x555646acc6a0 (gen) PID 6079 on the chain.
    Live child 0x555646acc6a0 (gen) PID 6079
    Reaping winning child 0x555646acc6a0 PID 6079
    Removing child 0x555646acc6a0 PID 6079 from chain.
      Successfully remade target file 'gen'.
      Considering target file 'main.o'.
       File 'main.o' does not exist.
       Looking for an implicit rule for 'main.o'.
       Trying pattern rule with stem 'main'.
       Trying implicit prerequisite 'main.cc'.
       Found an implicit rule for 'main.o'.
        Considering target file 'main.cc'.
         Looking for an implicit rule for 'main.cc'.
         No implicit rule found for 'main.cc'.
         Finished prerequisites of target file 'main.cc'.
        No need to remake target 'main.cc'.
       Finished prerequisites of target file 'main.o'.
      Must remake target 'main.o'.
    g++ -c main.cc -o main.o
    Putting child 0x555646ad0690 (main.o) PID 6080 on the chain.
    Live child 0x555646ad0690 (main.o) PID 6080
    Reaping winning child 0x555646ad0690 PID 6080
    Removing child 0x555646ad0690 PID 6080 from chain.
      Successfully remade target file 'main.o'.
     Finished prerequisites of target file 'default'.
    Must remake target 'default'.
    Successfully remade target file 'default'.
    

    如果您遵守规则并正确编写Makefile,声明正在创建的文件,make 将按预期运行:

    $ cat Makefile
    %.o: %.cc
            g++ -c $< -o $@
    
    default: main.o
    
    main.cc:
            touch $@
    
    $ make default
    touch main.cc
    g++ -c main.cc -o main.o
    

    【讨论】:

      猜你喜欢
      • 2020-03-22
      • 2020-08-28
      • 2021-04-28
      • 2012-02-15
      • 2010-09-15
      • 2020-01-27
      • 1970-01-01
      • 2013-11-08
      • 1970-01-01
      相关资源
      最近更新 更多