【问题标题】:Makefile for multiple files with `main`带有`main`的多个文件的Makefile
【发布时间】:2015-08-25 14:40:11
【问题描述】:

我的目录结构如下:

tests\
        test1.c
        test2.c
        test3.c
        build\
                 test1
                 test2
                 test3

具体来说,我想构建test1.ctest2.ctest3.c 并在tests\build 中输出可执行文件。

为了实现这一点,我有以下 Makefile sn-p:

TSTS = $(wildcard $(TEST_DIR)/*.c)
EXE = $(patsubst $(TEST_DIR)/%.c,$(TEST_BUILD)/%, $(TSTS))
TEST_DIR = tests
TEST_BUILD = tests/build

$(EXE): $(TSTS)
        $(CC) $< $(CFLAGS_OUT) $@ (CFLAGS_LIBS_FLAG)$(LIB_NAME)

这给了我类似的东西:

gcc -L/home/projects/loc/build -Iinclude tests/test1.c -o tests/build/test1 -lsalmalloc
gcc -L/home/projects/loc/build -Iinclude tests/test1.c -o tests/build/test2 -lsalmalloc
gcc -L/home/projects/loc/build -Iinclude tests/test1.c -o tests/build/test3 -lsalmalloc

输出路径正确,但源文件都是tests/test1.c

忽略include 和其他lib 相关文件,因为我已删除标志以使其简单。本质上,make 正在从单个文件构建测试用例,也就是说,对于单个源文件 test1.c,正在生成三个具有不同名称的构建,而不是每个测试都有自己的构建。我查看了herehereherehere,但找不到问题。

我该如何解决这个问题?

【问题讨论】:

    标签: c makefile


    【解决方案1】:

    你的目标

    $(EXE): $(TSTS)
    

    扩展到

    tests/build/test1 tests/build/test2 tests/build/test3: tests/test1.c tests/test2.c tests/test3.c
    

    其中,阅读关于Multiple Targets in a Rule的手册,

    具有多个目标的规则相当于编写许多规则,每个规则都有一个目标,除此之外都相同。相同的配方适用于所有目标,但其效果可能会有所不同,因为您可以使用“$@”将实际目标名称替换为配方。该规则也为所有目标提供了相同的先决条件。

    类似的配方适用于所有目标。配方不需要完全相同,因为自动变量“$@”可用于将要重新制作的特定目标替换为命令(请参阅自动变量)。例如:

    bigoutput littleoutput : text.g
       generate text.g -$(subst output,,$@) > $@
    

    等价于

    bigoutput : text.g
       generate text.g -big > bigoutput
    littleoutput : text.g
       generate text.g -little > littleoutput
    

    相同
    tests/build/test1: tests/test1.c tests/test2.c tests/test3.c
    tests/build/test2: tests/test1.c tests/test2.c tests/test3.c
    tests/build/test3: tests/test1.c tests/test2.c tests/test3.c
    

    所以当你在配方中使用$&lt; 时,你总是会得到tests/test1.c

    您想要的是 pattern rule

    因此,形式的规则

    %.o : %.c ; recipe…
    

    指定如何制作一个文件 n.o,以另一个文件 n.c 作为其先决条件,前提是 n.c 存在或可以制作。

    所以你想要

    TSTS = $(wildcard $(TEST_DIR)/*.c)
    EXES = $(patsubst $(TEST_DIR)/%.c,$(TEST_BUILD)/%, $(TSTS))
    
    all: $(EXES)
    
    $(TEST_BUILD)/%: $(TEST_DIR)/%.c
            $(CC) $< $(CFLAGS_OUT) $@ (CFLAGS_LIBS_FLAG)$(LIB_NAME)
    

    【讨论】:

    • 谢谢@Etan。不过有个问题,all: $(EXES) 在这里的作用是什么,因为这个目标随后没有被使用。
    【解决方案2】:

    这根本不是multiple targets 的工作方式。当你有:

    test1 test2 test3 : test1.c test2.c test3.c
        $(CC) $< ...
    

    扩展为:

    test1 : test1.c test2.c test3.c
        $(CC) $< ...
    
    test2 : test1.c test2.c test3.c
        $(CC) $< ...
    
    test3 : test1.c test2.c test3.c
        $(CC) $< ...
    

    既然$&lt; 抓住了第一个先决条件,这就是为什么你到处都能看到test1.c。你想做的是static pattern:

    $(EXE) : $(TEST_BUILD)/% : $(TEST_DIR)/%.c
        $(CC) $< ...
    

    【讨论】:

      【解决方案3】:

      你的规则相当于

      tests/build/test1 tests/build/test2 tests/build/test3: \
          tests/test1.cc tests/test2.cc tests/test3.cc
              $(CC) $< $(CFLAGS_OUT) $@ (CFLAGS_LIBS_FLAG)$(LIB_NAME)
      

      $&lt; 表示第一个先决条件,因此在这种情况下为tests/test1.cc。您的意思可能是 $^,所有先决条件的名称,但这对您没有帮助,因为您会尝试将所有 .cc 文件一起编译成一个目标文件,共 3 次。

      您可能想要的是模式规则:

      $(TEST_BUILD)/%: $(TEST_DIR)/%.cc
              $(CC) $< $(CFLAGS_OUT) $@ (CFLAGS_LIBS_FLAG)$(LIB_NAME)
      

      您可能还需要添加一个虚拟目标

      all: $(EXE)
      

      取决于您在该 makefile 中还有什么。

      【讨论】:

      • 为什么我需要一个虚拟目标?
      • @user1343318 这取决于你如何调用makefile。如果您不带参数调用 make,它将查找第一条规则并构建它。但是,这样做时不考虑模式规则。我建议用模式规则替换普通规则,如果你这样做,make 将不再知道要构建什么(除非你明确指定)。明确告诉 make 默认构建什么的典型方法是定义一个带有目标 all 和默认目标作为依赖项的规则,作为 makefile 中的第一个正常规则。理想情况下,一切都应该是假的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-09
      • 1970-01-01
      • 2021-05-17
      • 2013-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多