makefile(07)_路径搜索

17.Make中的路径搜索_上

17.0.实验素材

源文件位于: src目录下
源文件:main.c

#include <stdio.h>
#include "func.h"

int main()
{
    foo();

    return 0;
}   

源文件:func.c

#include <stdio.h>
#include "func.h"

void foo()
{
    printf("void foo() : %s\n", "Hello, D.T.Software ...");
}

头文件位于: inc目录下
头文件func.c

#ifndef FUNC_H
#define FUNC_H

void foo();

#endif

17.1. 工程源码组织方式

问题:实际的工程项目中,所有的源文件和头文件都放在一个文件夹吗?
实际的工程中,源码和头文件都是按照模块划分的,举例如下:

项目中的makefile必须能够正确的定位源文件和依赖文件,最终编译产生可执行程序。
实验1:
当源码组织目录如下时,下面的makefile能够编译成功吗?

OBJS := func.o main.o

hello.out : $(OBJS)
    @gcc -o [email protected] $^
    @echo "Target File ==> [email protected]"

$(OBJS) : %.o : %.c func.h
    @gcc  -o [email protected] -c $<

编译结果:

由于make默认只会在makefile所在的文件家去查找源文件和头文件,所以自然会导致编译报错。

17.2. 预定义变量VPATH

VPATH变量的值用于指示make如何查找文件,不同文件夹可以作为VPATH的值同时出现,可以使用空格,冒号,分号这样的分隔符进行分隔,如VPATH := src inc
实验2:源码目录如下(注意这里存在两个同名的C文件),下面的makefile编译结果如何?

OBJS := func.o main.o
INC := inc
SRC := src
VPATH := $(INC) $(SRC)

hello.out : $(OBJS)
    @gcc -o [email protected] $^
    @echo "Target File ==> [email protected]"

$(OBJS) : %.o : %.c func.h
    @gcc  -o [email protected] -c $<

编译结果:

原因:VPATH只能决定make的搜索路径,无法决定命令的搜索路径,对特定编译命令(gcc),需要独立指定编译搜索路径。

修改makefile 内容如下,再进行实验:

OBJS := func.o main.o
INC := inc
SRC := src
VPATH := $(INC) $(SRC)
CFLAGS := -I $(INC)

hello.out : $(OBJS)
    @gcc -o [email protected] $^
    @echo "Target File ==> [email protected]"

$(OBJS) : %.o : %.c func.h
    @gcc $(CFLAGS) -o [email protected] -c $<

编译和运行结果:

此时,我们看到编译结果正确,但运行后发现被编译的时inc目录下的func.c文件,这可能不是我们预期的。
原因:当make在当前文件夹找不到需要的文件时,VPATH会被使用,make会在VPATH指定的文件夹中依次进行搜索文件,当多个文件夹存在同名文件时,选择第一次搜索到的文件。
实验3:
此时,我们讲源文件中的.c文件更名为.cpp文件,进行上面的实验

当inc文件夹意外出现文件(c/cpp文件),那么可能产生编译错误。

17.3. vpath关键字

为了解决上面的问题,我们使用vpath关键字,为不同类型的文件指定不同的搜索路径
语法:eg : vpath %.h inc或者 vpath %.h src
取消搜索路径:
取消已经设置的某个所搜规则:vpath patten,
eg: vpath %.h inc # 在inc中搜索.h文件
vpath %.h # 不再inc中搜索.h文件
取消所有已经设置的规则
vpath

17.4. 总结:

VPATH变量用于指示make如何查找文件,make会在VPATH指定的文件夹中依次搜索文件
vpath 关键字可以为不同类型的文件指定不同的搜索路径,比VPATH更灵活易用,可动态设置取消搜索路径。

18.Make中的路径搜索_下

18.1.VPATH和vpath同时使用

问题一:VPATH和vpath同时出现时,make会如何处理?

实验结果:

实验结论:
make首先在当前文件夹搜索需要的文件,如果失败:
Make优先在vpath指定的文件夹中搜索目标文件
当vpath搜索失败时,转而搜索VPATH指定的文件夹

18.2.vpath对同一个patten指定多个文件夹

问题2:当vpath对同一个patten指定多个文件夹时,make会如何处理?

实验结果:
实验结论:当vpath对同一个patten指定多个文件夹时,make会依次自上而下,搜索指定的文件夹,直到找到目标。

18.3.目标文件位置

问题3:当使用VPATH指定搜索路径后,make如何确定最终目标文件的位置?
下面的项目中,将如何生成可执行程序?

实验结论:
当app.out完全不存在,make会在当前文件夹下创建app.out
当src文件夹下存在app.out:
所有目标和依赖的新旧关系不变,make不会重新创建app.out
当依赖文件被更新,make在当前文件夹下创建app.out
问题:当依赖关系改变时,如何使得src下的app.out被更新?

18.4.GPATH变量

上面问题,我们可以使用GPATH特殊变量指定目标文件夹来解决。
GPATH := src
当app.out完全不存在,make默认在当前文件夹创建app.out
当app.out存在于src,并且依赖文件被更新,make在src文件夹中创建目标文件
18.5.工程建议:
尽量使用vpath为不同文件指定搜索路径;
不要在源码文件夹中生成目标文件,为编译得到的结果创建独立文件夹
尽量避免使用VPATH如果一定要使用,考虑GPATH的使用

19.Make中的路径搜索_综合示例

19.1.需求分析

工程项目中不希望源文件夹在编译时被改动(只读文件夹)
在编译时自动创建文件夹(build)用于存放编译结果
编译过程中国能够自动搜索到需要的文件
makefile易于扩展,能够复用相同类型的项目
支持调试版本和编译选项

19.2.项目类型

19.3.工具原料:

$(wildcard $(DIR)/ _patten) 获取$(DIR)文件夹中满足_patten的文件
$(notdir _names) 去除_names中每一个文件名的路径前缀
$(patsubst _patten, replacement, _text) 将_text中符合_patten的部分替换为replacement

19.4.关键技巧:

1.自动获取文件列表(函数调用)
SRSC := $(waidcard src/*.c)
2.根据源文件列表生成目标文件列表(变量的替换)
OBJS := $(SRC:.c=.o)
3.替换每一个目标文件的路径前缀(函数调用)
OBJS := $(patsubst src/%, build/%, $(OBJS))

19.5.编译文件的依赖

19.6.最终方案:

.PHONY : all clean

DIR_BUILD := build
DIR_SRC := src
DIR_INC := inc

TYPE_INC := .h
TYPE_SRC := .c
TYPE_OBJ := .o

CC := gcc
LFLAGS :=
CFLAGS := -I $(DIR_INC)
ifeq ($(DEBUG),true)
CFLAGS += -g
endif

MKDIR := mkdir
RM := rm -fr

APP := $(DIR_BUILD)/app.out
HDRS := $(wildcard $(DIR_INC)/*$(TYPE_INC))
HDRS := $(notdir $(HDRS))
OBJS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC))
OBJS := $(OBJS:$(TYPE_SRC)=$(TYPE_OBJ))
OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_BUILD)/%, $(OBJS))

vpath %$(TYPE_INC) $(DIR_INC)
vpath %$(TYPE_SRC) $(DIR_SRC)

all : $(DIR_BUILD) $(APP)
    @echo "Target File ==> $(APP)"

$(DIR_BUILD) :
    $(MKDIR) [email protected]

$(APP) : $(OBJS)
    $(CC) $(LFLAGS) -o [email protected] $^

$(DIR_BUILD)/%$(TYPE_OBJ) : %$(TYPE_SRC) $(HDRS)
    $(CC) $(CFLAGS) -o [email protected] -c $<

clean :
    $(RM) $(DIR_BUILD)

问题:
对于规模较小的项目,makefile是否也需要使用自动变量生成依赖关系的解决方案?
规模较小的项目没必要使用自动生成依赖关系的解决方案,可以直接让源文件依赖于头文件(易于维护)

19.7.总结:

工程项目中不希望源码文件夹在编译时被改动
模式规则的灵活运用使得makefile具有复用性
变量的灵活运用使得makefile具有可扩展性

原文地址:http://blog.51cto.com/11134889/2108422

时间: 2024-11-14 12:51:33

makefile(07)_路径搜索的相关文章

makefile(08)_打造专业的编译环境

20.打造专业的编译环境(上)_模块Makefile设计 20.0. 实验材料 项目架构:其中各个文件的内容请自己填写. 20.1.大型项目的目录结构(无第三方库) 20.2.项目架构设计分析 项目被划分为不同的多个模块:每个模块用一个文件夹进行管理,文件由inc, src, makefile构成每个模块的对外函数统一放置于common/inc中,如common.h xxxfunc.h 20.3.项目目标 工程项目中不希望源文件夹在编译时被改动(只读文件夹)在编译时自动创建文件夹(build)用

makefile(01)_初识

0. 声明 本系列(makefile)文章,从零基础开始,通过实验逐步分析makefile的语法特性,并最终打造一个可复用.可移植的专业编译环境.参考:1.DT 唐老师门徒计划课程2.GNU make 手册:http://www.gnu.org/software/make/manual/make.html 1.Make与makefile Make是一个应用程序:接续源程序之间的依赖关系,根据依赖关系自动维护编译工作,执行宿主操作系统中的各种命令. Makefile是一个描述文件:定义了系列的规则

makefile(02)_变量

4.变量与赋值 4.1.变量 Makefile中支持程序设计语言中变量的概率,但没有变量类型,只代表文本数据:变量命名规则:变量可以包含字符.数字.下划线,单不能包含":","#", "="," ",变量名大小写敏感.变量的定义和使用: 4.2.赋值 Makefile中有4中变量赋值方式: 4.2.1.简单赋值(:=) 程序设计语言中的通用赋值方式,只针对当前语句有效,等价于C语言中的赋值.建议无特殊要求时使用简单赋值. x

makefile(03)_条件判断

8.条件判断语句 8.1.语法规则 Makefile中支持条件判断语句,可以直接比较两个不同变量的值和常量值.注意:条件判断语句只能用于控制make实际执行的语句,不能控制规则中命令的执行过程.条件判读语法说明:条件判断关键字:示例: .PHONY : test var1 := A var2 := $(var1) var3 := test: ifeq ($(var1),$(var2)) @echo "var1 == var2" else @echo "var1 != var2

makefile(04)_函数

9.函数定义及调用 Makefile中支持函数的概念,make解析器提供了一系列函数供Makefile使用.同时可以自定义函数. 9.1.自定义函数 在Makefile中支持自定义函数的实现,并调用执行,通过define关键字来实现自定义函数.函数定义的语法规则:自定义函数的本质:1.自定义函数其实是一个多行变量,无法直接调用:通过call 关键字来使用(call的作用就是将实参替换到函数体对应的位置)2.自定义函数是一种过程调用,没有任何的返回值:3.用于定义命令集合,并应用于规则中.示例:

makefile(06)_隐式规则

15.Make的隐式规则 15.1.命令覆盖 问题1:通过各目标的命令拆分写到不同的地方,会发生什么?Makefile中出现同名目标时:依赖:所有的依赖将合并到一起,成为目标的最终依赖命令:当多处出现同一目标的命令时,make发出警告,所有之前定义的命令被最后的命令取代.注意:当使用include包含其他文件(makefile)时,需要确保被包含的文件中的同名目标只有依赖,没有命令:否则,同名目标的命令将被覆盖! 15.2.隐式规则 Make中提供了一些常用的,例行的规则实现,当目标的规则未提供

makefile(09)_扩展支持

23.独立模块的支持 23.1.问题: 一般而言,不同工程师负责不同模块的开发,编译环境中如何支持模块的独立编译? 23.2.问题背景: 大型项目的代码成千上万,完整编译的时间较长, 编写模块代码时,可以通过编译检查语法错误: 为了提高开发效率,需要支持指定模块的独立编译 23.3.解决方案 将模块名(module)作为目标名(伪目标)建立规则 目标(module)对应的依赖为build build/module 规则中的命令进入对应的模块文件夹进行编译 编译结果存放于build文件夹下 23.

UI 07 _ 导航视图控制器 与 属性传值

首先, 先创建三个VC. 完成点击按钮, 进入下一页, 并能够返回. 要先把导航视图控制器创建出来. 在AppDelegate.m 文件中代码如下: #import "AppDelegate.h" #import "MainViewController.h" @interface AppDelegate () @end @implementation AppDelegate - (void)dealloc{ [_window release]; [super dea

JDK8的随笔(07)_行云流水般的Lambda表达式

好久没有更新啦,继续继续. 最近这个项目陷入了一个使用语言的怪圈.任何东西都想着原来的写法怎么能翻译到新的JDK 的写法.这其实就走入了歧途哇哇哇. 看下面这个例子,是一个很简单的例子.一般情况下我们都会这么写这样的逻辑. public static void main(String ... args) { List<String> list = new ArrayList<>(); list.add("a"); list.add("b");