带有两个可执行文件的递归makefile

时间:2017-03-23 16:12:12

标签: c makefile

我有一个包含两个主要功能的项目,一个用于实现我的程序,另一个主要功能用于测试程序。我在网上搜索了这个,但发现几乎没有关于带有两个可执行文件的递归makefile。

生成文件

CC = clang
CFLAGS = -g -Wall

PROG = suggest

OBJDIR = objects
OBJS = $(OBJDIR)/suggest.o $(OBJDIR)/engine.o $(OBJDIR)/table.o $(OBJDIR)/levenshtein.o

# ... link them
$(PROG): $(OBJS) $(OBJDIR)
    $(CC) $(CFLAGS) $(OBJS) -o $(PROG)

$(OBJDIR)/table.o:
    $(MAKE) -C table.c
    cp ../file1/table.c/table.o $(OBJDIR)

$(OBJDIR)/levenshtein.o:
    $(MAKE) -C levenshtein.c
    cp levenshtein.c/levenshtein.o $(OBJDIR)

# build them all...
$(OBJDIR)/%.o: %.c $(OBJDIR)
    $(CC) $(CFLAGS) -c -o $@ $<

$(OBJDIR):
    mkdir -p $(OBJDIR)

clean:
    rm -rf $(OBJDIR) $(PROG)

在上面的文件中,engine.c是执行我的程序的主要功能,但我还有另一个main函数,名为test.c,对我的文件进行测试。 我想要的是,当我运行make命令时,它应该只构建engine.c main函数(默认情况下),但如果我想测试我的程序,我应该能够运行通过指定类似内容的test.c main函数,例如make test

问题是我没有测试文件的makefile。我的目录是这样的:engine.c,suggest.c,test.c然后我有另一个名为levenshtein.c的文件夹,它有自己的makefile。另外,我只想使用两个makefile。我已经写了一个在levenshtein.c文件夹中的文件,我有问题的是这个文件夹,因为这个文件夹包含两个main函数。我想要一个基于命令的条件编译。

3 个答案:

答案 0 :(得分:1)

试试这个;在engine.o或test.o中添加新目标TEST并有条件地链接。这只有在您的测试程序不需要engine.o中的任何函数或数据时才有效。如果是,您可能需要使用-DTEST_PROGRAM

等条件有条件地编译两个主函数

由于你正在使用engine.o中的其他函数和数据,你应该在engine.c中的main(...)函数周围添加类似#if USE_TEST_PROGRAM #endif的东西,并在你的makefile中定义USE_TEST_PROGRAM标志,如图所示下面。这将产生两次编译engine.c的副作用(一次进入你的程序建议的OBJDIR,一次进入你的测试程序的顶级目录)。

CC = clang
CFLAGS = -g -Wall

PROG = suggest
TEST = test

OBJDIR = objects
OBJS = $(OBJDIR)/suggest.o $(OBJDIR)/table.o $(OBJDIR)/levenshtein.o

# ... link them
$(PROG): $(OBJS) $(OBJDIR)
    $(CC) $(CFLAGS) $(OBJS) $(OBJDIR)/engine.o -o $(PROG)

$(TEST): $(OBJS) $(OBJDIR)
    $(CC) $(CFLAGS) -DUSE_TEST_PROGRAM=1 engine.c $(OBJS) $(OBJDIR)/test.o -o $(PROG)

$(OBJDIR)/table.o:
    $(MAKE) -C table.c
    cp ../file1/table.c/table.o $(OBJDIR)

$(OBJDIR)/levenshtein.o:
    $(MAKE) -C levenshtein.c
    cp levenshtein.c/levenshtein.o $(OBJDIR)

# build them all...
$(OBJDIR)/%.o: %.c $(OBJDIR)
    $(CC) $(CFLAGS) -c -o $@ $<

$(OBJDIR):
    mkdir -p $(OBJDIR)

clean:
    rm -rf $(OBJDIR) $(PROG)

答案 1 :(得分:1)

您不想对正在进行的操作使用递归。

根据您的描述的声音,您希望拥有一个makefile,它构建两个最终目标之一:testengine。对于test,它应该编译并链接test.c。对于engine,它应该编译并链接engine.c

如果这是正确的,你可以构建一个包含两个目标的makefile:

test engine: $(PROG)

.PHONY: test engine

因此每个目标都会在$(PROG)上展开。然后修改makefile以获得要构建的正确对象列表:

OBJS := ....

ifneq ($(filter test,$(MAKECMDGOALS)),)
    #test was a command goal, add test.o to the targets:
    OBJS+=$(OBJDIR)/test.o
else ifneq ($(filter engine,$(MAKECMDGOALS)),)
    #engine was a command goal, add test.o to the targets:
    OBJS+=$(OBJDIR)/engine.o
else
    #neither were command goals.  set default:
    OBJS+=$(OBJDIR)/engine.o
fi

$(PROG): $(OBJS)
     @$(CC) $(CFLAGS) $? -o $@

现在当你运行make test时,它会将test.o添加到$(OBJS),构建和链接($?将包含所有.o文件),否则它将添加{{1}到engine.o。 Make将编译所有$(OBJS)并构建你的目标。

另一件事。你有:

$(OBJS)

(这很好,但不太好,你有:

$(OBJDIR):
    mkdir -p $(OBJDIR)

首先,它会在创建对象目录之前尝试构建对象(这可能导致构建失败)。此外,如果提前$(PROG): $(OBJS) $(OBJDIR) 并且编译工作,那么每次该目录中的文件发生更改时,objdir上的时间戳都会更新...所以它可能比{{更新... 1}},导致它在不需要的时候经常重建。正确的方法是:

$(OBJDIR)

注意$(PROG),这意味着只有顺序依赖,这意味着如果$(OBJS): | $(OBJDIR) 有一个更新的时间戳,它将不会重建|,但它会在任何一个之前构建目录对象。

答案 2 :(得分:0)

以上所有答案都非常有用,但我仍然无法运行我的程序。从上面的答案得到一些提示后,我能够提出一个简化版的makefile。根据您的目录设置,上述内容也是准确的。这就是我做到的。

(注意:这可能不是最佳解决方案)

TEST = test.c engine.c levenshtein.c/levenshtein.c ../subdir1/table.c
PROG = suggest.c engine.c levenshtein.c/levenshtein.c ../subdir1/table.c
OBJ_TEST = $(TEST:.c=.o)
OBJ_PROG = $(PROG:.c=.o)

CC = clang
CFLAGS = -g -Wall

test: $(OBJ_TEST)
    $(CC) $(OBJ_TEST) -o $@

suggest: $(OBJ_PROG)
    $(CC) $(OBJ_PROG) -o $@

.SUFFIXES: .c .o
.c.o:
    $(CC) $< $(CFLAGS) -c -o $@

clean:
    rm -rf *.o

我有一个父目录,其中包含两个以上的文件夹。我的目录设置如下所示:

  

项目/

     包含table.c的

| _____子目录

     

| _____另一个包含我的engine.c,suggest.c和test.c的子目录,它还包含levenshtein.c,它是一个单独的文件夹。

因此,对于任何未来的读者,如果您正在阅读本文,请记下您的目录,然后尝试创建一个makefile,因为它会减少生活压力。