LV070-指定生成文件路径
一、文件准备
1. 目录结构
各文件所在目录的结构如下:
c
.
├── main.c
├── Makefile
├── test1.c
├── test1.h
├── test2.c
└── test2.h
0 directories, 6 files2. 源码
2.1 main.c
c
#include <stdio.h>
#include "test1.h"
#include "test2.h"
int main(int argc, const char *argv[])
{
printf("This is main file!\n");
test1Fun();
test2Fun();
return 0;
}2.2 test1.c
c
#include <stdio.h>
void test1Fun()
{
printf("This is test1.c file\n");
}2.3 test2.c
c
#include <stdio.h>
void test2Fun()
{
printf("This is test2.c file\n");
}2.4 test1.h
c
#ifndef __TEST1_H__
#define __TEST1_H__
void test1Fun();
#endif2.5 test2.h
c
#ifndef __TEST2_H__
#define __TEST2_H__
void test2Fun();
#endif二、生成文件路径
1. 指定生成文件路径
我们每次生成的一大堆的文件都是存放在当前目录下的,文件多了之后,就会看起来很乱,我们能否指定生成文件路径呢?当然是可以的啦。这里直接以实例来说明。
1.1 文件准备
首先我们新建一个 obj 文件夹用于存放生成的 .o 中间文件。
shell
mkdir obj我们使用上边的文件搜索相关测试文件来测试,我们新建两个目录,并将两个相关文件移动到指定目录:
shell
mkdir test1 test2 user
mv test1.c test1.h test1
mv test2.c test2.h test2
mv main.c user所以现在的目录结构为:
shell
.
├── Makefile
├── obj
├── test1
│ ├── test1.c
│ └── test1.h
├── test2
│ ├── test2.c
│ └── test2.h
└── user
└── main.c
4 directories, 6 files1.2 两次试错
1.2.1 测试1
我们编写 Makefile 内容如下:
makefile
OBJ := main.o test1.o test2.o
INCLUDE := -I test1 -I test2
OBJDIR := ./obj
vpath %.c user test1 test2
# 可执行程序 main
main: $(OBJ)
gcc $(OBJ) -o main $(INCLUDE)
$(OBJ):$(OBJDIR)/%.o: %.c
gcc -c $< -o $@ $(INCLUDE)
print:
@echo "OBJ = $(OBJ)"
.PHONY: clean
clean:
rm -rf $(OBJDIR)/*.o main然后我们执行 make 命令,会发现报了下边的错误:
shell
Makefile:11: target 'main.o' doesn't match the target pattern
Makefile:11: target 'test1.o' doesn't match the target pattern
Makefile:11: target 'test2.o' doesn't match the target pattern
gcc -c -o main.o -I test1 -I test2
gcc: fatal error: no input files
compilation terminated.
Makefile:12: recipe for target 'main.o' failed
make: *** [main.o] Error 1我们看报错的说明,就是生成 main 的时候,找不到它所依赖的 main.o 、 test1.o 和 test2.o ,并且在模式规则的匹配中,匹配也会有问题,我们来分析一下,我们拆开上边的模式规则,并将变量进行替换,可以得到:
makefile
OBJ := main.o test1.o test2.o
INCLUDE := -I test1 -I test2
OBJDIR := ./obj
vpath %.c user test1 test2
# 可执行程序 main
main: main.o test1.o test2.o
gcc main.o test1.o test2.o -o main $(INCLUDE)
# =============================================================================
# 以下是模式规则的展开形式(但这种写法是错误的,仅用于演示问题)
# =============================================================================
# 规则1: main.o 的编译规则
# 目标: main.o (位于当前目录)
# 依赖: ./obj/%.o (模式匹配,期望在obj目录下)
# %.c (源文件,使用vpath搜索)
# 问题: 目标文件名 main.o 与依赖模式 ./obj/%.o 不匹配!
# % 会匹配 "main",但依赖路径会是 ./obj/main.o,而实际目标是 main.o
main.o:./obj/%.o:%.c
gcc -c $< -o $@ $(INCLUDE)
# $< = 源文件路径 (如 user/main.c)
# $@ = 目标文件路径 (如 main.o,但这里期望是 ./obj/main.o)
# 规则2: test1.o 的编译规则(与上面同样的问题)
# 目标: test1.o
# 依赖: ./obj/%.o (期望在obj目录下)
# %.c (源文件)
# 问题: 目标路径与依赖路径不一致
test1.o:./obj/%.o:%.c
gcc -c $< -o $@ $(INCLUDE)
# 规则3: test2.o 的编译规则(与上面同样的问题)
# 目标: test2.o
# 依赖: ./obj/%.o (期望在obj目录下)
# %.c (源文件)
# 问题: 目标路径与依赖路径不一致
test2.o:./obj/%.o:%.c
gcc -c $< -o $@ $(INCLUDE)
# =============================================================================
# 总结:这种写法的错误在于
# 1. 目标文件名 (main.o) 与依赖模式 (./obj/%.o) 不匹配
# 2. 编译输出的目标文件应该在 ./obj/ 目录下,但这里指定的是当前目录
# 3. 正确的写法应该是: ./obj/main.o:./obj/%.o:%.c
# =============================================================================
.PHONY: clean
clean:
rm -rf $(OBJDIR)/*.o main我们会发现, % 匹配的时候,前边的目标文件格式与后边匹配的不太一致,并且我们生成的 .o 文件是存放在 obj 目录下的, gcc 编译的时候是找不到这几个文件的。
1.2.2 测试2
我们可以修改如下:
makefile
OBJ := main.o test1.o test2.o
INCLUDE := -I test1 -I test2
OBJDIR := ./obj
vpath %.c user test1 test2
# 可执行程序 main
main: ./obj/main.o ./obj/test1.o ./obj/test2.o
gcc ./obj/main.o ./obj/test1.o ./obj/test2.o -o main $(INCLUDE)
./obj/main.o:./obj/%.o:%.c
gcc -c $< -o $@ $(INCLUDE)
./obj/test1.o:./obj/%.o:%.c
gcc -c $< -o $@ $(INCLUDE)
./obj/test2.o:./obj/%.o:%.c
gcc -c $< -o $@ $(INCLUDE)
.PHONY: clean
clean:
rm -rf $(OBJDIR)/*.o main然后我们再执行 make 命令,会发现有如下提示:
shell
gcc -c user/main.c -o obj/main.o -I test1 -I test2
gcc -c test1/test1.c -o obj/test1.o -I test1 -I test2
gcc -c test2/test2.c -o obj/test2.o -I test1 -I test2
gcc ./obj/main.o ./obj/test1.o ./obj/test2.o -o main -I test1 -I test2此时文件结构如下:
shell
.
├── main
├── Makefile
├── obj
│ ├── main.o
│ ├── test1.o
│ └── test2.o
├── test1
│ ├── test1.c
│ └── test1.h
├── test2
│ ├── test2.c
│ └── test2.h
└── user
└── main.c发现,生成的中间文件全部进入了 obj 目录。
1.3 正确的格式
经过前边的试错,我们可以将 Makefile 写成如下内容:
makefile
INCLUDE := -I test1 -I test2
OBJDIR := ./obj
OBJ := $(OBJDIR)/main.o $(OBJDIR)/test1.o $(OBJDIR)/test2.o
TARGET := main
vpath %.c user test1 test2
# 可执行程序 main
$(TARGET): $(OBJ)
gcc $(OBJ) -o $@ $(INCLUDE)
$(OBJ):$(OBJDIR)/%.o:%.c
gcc -c $< -o $@ $(INCLUDE)
# 创建 obj 目录
$(OBJDIR):
@mkdir -p $@
.PHONY: clean
clean:
rm -rf $(OBJDIR)/*.o main这样的 Makefile 就简洁了很多了。
2. 最终目标路径
上边我们已经把中间文件的路径设置过了,那还有最终生成的目标文件的,但其实也没什么必要,毕竟最终生成的文件只有一个,也不会很乱,不过这里还是记录一下吧,我们只需要在写目标文件的时候加上路径就可以了:
makefile
INCLUDE := -I test1 -I test2
OBJDIR := ./obj
OBJ := $(OBJDIR)/main.o $(OBJDIR)/test1.o $(OBJDIR)/test2.o
TARGET := ./bin/main
vpath %.c user test1 test2
# 可执行程序 main
$(TARGET): $(OBJ)
gcc $(OBJ) -o $@ $(INCLUDE)
$(OBJ):$(OBJDIR)/%.o:%.c
gcc -c $< -o $@ $(INCLUDE)
.PHONY: clean
clean:
rm -rf $(OBJDIR)/*.o main这样我们最终生成目标文件为 main ,并且,它将会被存放到 ./bin 目录下。