【问题标题】:Making Make Automatically Compile Objs from Headers into Archive then into Main ProgramMake 自动将 Objs 从 Headers 编译到 Archive 再到 Main 程序
【发布时间】:2021-06-06 16:28:14
【问题描述】:

我有一个包含大量头文件和源文件的大型项目。我想要的是以下但自动的

生成文件

INCLUDE = -Iinc
CFLAGS = -g
LIB = lib/foo_bar.a

all: obj/foo_print_hello.o\
    obj/foo_print_goodbye.o\
    obj/bar_print_goodbye.o\
    obj/bar_print_hello.o\
    obj/main.o\
    lib/foo_bar.a\
    bin/main\


obj/foo_print_hello.o: src/foo_print_hello.c inc/foo.h
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

obj/foo_print_goodbye.o: src/foo_print_goodbye.c inc/foo.h
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

obj/bar_print_goodbye.o: src/bar_print_goodbye.c inc/bar.h
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

obj/bar_print_hello.o: src/bar_print_hello.c inc/bar.h
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

obj/main.o: src/main.c inc/foo.h inc/bar.h
    $(CC) $(INCLUDE) -c $(CFLAGS) -o $@ $<
    
    
lib/foo_bar.a:  obj/bar_print_hello.o obj/bar_print_goodbye.o obj/foo_print_goodbye.o obj/foo_print_hello.o inc/bar.h inc/foo.h
    ar -crv  $@ $^ 


bin/main: obj/main.o 
    $(CC) -o $@ $< $(LIB)

文件架构如下

.
├── bin
│   └── main
├── inc
│   ├── bar.h
│   └── foo.h
├── lib
│   └── foo_bar.a
├── makefile
├── obj
│   ├── bar_print_goodbye.o
│   ├── bar_print_hello.o
│   ├── foo_print_goodbye.o
│   ├── foo_print_hello.o
│   └── main.o
└── src
    ├── bar_print_goodbye.c
    ├── bar_print_hello.c
    ├── foo_print_goodbye.c
    ├── foo_print_hello.c
    └── main.c

我想要这种精确的编译结构,它将它们全部编译成目标文件,然后是库函数,然后是用 main 编译。我在 GNU 和互联网上看到了很多模板,但都没有什么效果。如何让我的 makefile 自动生成 obj(将它们放在 obj 目录中)然后编译到存档中?

编译什么

cc -Iinc -c -g -o  obj/foo_print_hello.o src/foo_print_hello.c
cc -Iinc -c -g -o  obj/foo_print_goodbye.o src/foo_print_goodbye.c
cc -Iinc -c -g -o  obj/bar_print_goodbye.o src/bar_print_goodbye.c
cc -Iinc -c -g -o  obj/bar_print_hello.o src/bar_print_hello.c
cc -Iinc -c -g -o obj/main.o src/main.c
ar -crv  lib/foo_bar.a obj/bar_print_hello.o obj/bar_print_goodbye.o obj/foo_print_goodbye.o obj/foo_print_hello.o inc/bar.h inc/foo.h 
r - obj/bar_print_hello.o
r - obj/bar_print_goodbye.o
r - obj/foo_print_goodbye.o
r - obj/foo_print_hello.o
r - inc/bar.h
r - inc/foo.h
cc -o bin/main obj/main.o lib/foo_bar.a

主输出

Hello
Goodbye
Hello
Goodbye

代码仓库:

git@github.com:tlplayer/dummy_makefile_question.git

跟进

所以我尝试了以下makefile

SRCS := $(wildcard src/*.c)
OBJS := $(patsubt src/%.c, obj/%.o, $(SRCS))
LIB := lib/foo_bar.a

all: bin/main

obj/%.o: src/%.c
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

$(LIB): obj/%.o
    ar -crv  $@ $^ 

bin/main: obj/main.o $(LIB) 
    $(CC) -o $@ $< $(LIB)

问题是它不知道我想要它做什么。是否将标头添加到 makefile(依赖项中的 foo.h bar.h)

make: *** No rule to make target `obj/%.o', needed by `lib/foo_bar.a'.  Stop.

跟进 2

当前生成文件

INCLUDE = -Iinc
CFLAGS = -g
LIB = lib/foo_bar.a

SRCS := $(wildcard src/*.c)
OBJS := $(patsubt src/%.c, obj/%.o, $(SRCS))
OBJS := $(filter-out obj/main.o, $(OBJS))

all: bin/main\

obj/%.o: src/%.c 
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<
 
$(LIB): $(OBJS)
    ar -crv $@ $^
 
bin/main: obj/main.o $(LIB)
    $(CC) -o $@ $^

错误是什么

cc -Iinc -c -g -o  obj/main.o src/main.c
ar -crv lib/foo_bar.a 
cc -o bin/main obj/main.o lib/foo_bar.a
obj/main.o: In function `main':
test/src/main.c:6: undefined reference to `foo_print_hello'
test/src/main.c:7: undefined reference to `foo_print_goodbye'
test/src/main.c:8: undefined reference to `bar_print_hello'
test/src/main.c:9: undefined reference to `bar_print_goodbye'
collect2: error: ld returned 1 exit status
make: *** [bin/main] Error 1

由于某种原因,它先编译 main.o,然后编译归档文件。但是存档缺少所有目标文件。那么为什么不包含对象呢?

解决方案

感谢 MadScientist 和 Topology

INCLUDE = -Iinc
CFLAGS = -g
LIB = lib/foo_bar.a

SRCS := $(wildcard src/*.c)
OBJS := $(patsubst src/%.c, obj/%.o, $(SRCS))
OBJS := $(filter-out obj/main.o, $(OBJS))

all:bin/main\

clean:
    rm -f obj/* bin/* lib/*

obj/%.o: src/%.c
    $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<

lib/foo_bar.a: $(OBJS)
    ar -crv $@ $^
 
bin/main: obj/main.o $(LIB)
    $(CC) -o $@ $^

【问题讨论】:

  • 你能定义自动是什么意思吗?我的意思是,make 总是要求你输入make,所以它不会 100% 自动发生。一旦你输入make,它应该“做所有的事情”,而不需要你的任何输入。您希望看到哪些不是“自动”的?
  • @MadScientist 自动我的意思是像this 这样它会从 src 目录中找到所有 .c 文件,然后将它们编译成 obj 目录中的 .o 文件。然后将其编译到 lib 目录中的 .a lib 中。我有 30 个源文件和 3 个头文件,我想成为 .o 文件,然后是 .a 文件。
  • 上述错误的原因是模式规则必须在目标中有模式(带有%的单词)。如果目标中没有模式,这不是模式规则,它只是一个常规的显式规则,你说它取决于文字文件名obj/%.o
  • 在模式规则中考虑模式是错误的,比如 shell 通配符 (*)。它们不是那样的,模式规则不是这样工作的。模式规则是一个模板,用于如何构建目标的,而无需指定目标的精确名称。模式规则不是关于如何从一堆其他文件构建特定文件的语句。这是一个明确的规则。
  • 问题是您在 makefile 中拼写了 patsubst 错误。你拼写了patsubt

标签: c makefile


【解决方案1】:

只需将lib/foo_bar.a 添加到bin/main 规则的依赖项中即可。

bin/main: obj/main.o $(LIB) 
    $(CC) -o $@ $< $(LIB)

此外,您还可以使用模式和函数进一步简化您的 Makefile。

这样整个makefile就可以变成

INCLUDE = -Iinc
CFLAGS = -g
LIB = lib/foo_bar.a

SRCS := $(wildcard src/*.c)
SRCS := $(filter-out src/main.c $(SRCS))
LIBOBJS := $(patsubst src/%.c, obj/%.o, $(SRCS))

all: bin/main

obj/%.o: src/%.c
        $(CC) $(INCLUDE) -c $(CFLAGS) -o  $@ $<
 
$(LIB): $(OBJS)
    ar -crv $@ $^

bin/main: obj/main.o $(LIB)
    $(CC) -o $@ $^

【讨论】:

  • 您最后的括号声明省略了一些复杂性,因为他们不希望将 main.o 添加到库中。
  • 我仍然得到这个错误 make: *** No rule to make target foo.h', needed by obj/main.o'。停止。
  • @user11145590 你能更新你的问题以显示你的makefile吗?
  • @topoly 可能比目标文件更干净地过滤掉源文件,并将OBJS 重命名为LIBOBJS
  • 你在这个答案中拼错了patsubst
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-05
  • 1970-01-01
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多