迪庆藏族自治州网站建设_网站建设公司_Redis_seo优化
2025/12/18 14:41:46 网站建设 项目流程

1.makefile的规则和make的工作原理

第一部分:Makefile 的规则 (The Rules)

Makefile 的核心就是规则 (Rules)。无论文件多复杂,最终都可以拆解为一条条如下格式的规则:

target ... : prerequisites ...command...

1. 规则的三大要素:

  1. 目标 (Target)
    • 通常是你想要生成的文件名(例如可执行文件 app,或者中间文件 main.o)。
    • 也可以是一个执行动作的名称(伪目标),例如 clean
  2. 依赖 (Prerequisites)
    • 生成该目标所需要的文件。
    • 例如:要生成 main.o,通常依赖于 main.cmain.h
    • 核心逻辑:如果依赖文件比目标文件“新”(修改时间更晚),或者目标文件不存在,Make 就会执行下方的命令。
  3. 命令 (Command)
    • 构建目标所要执行的 Shell 命令(如 gcc -c main.c)。
    • 关键注意点:命令行的行首必须使用 Tab 键 缩进,不能使用空格(这是初学者最容易报错的地方)。

示例:

# 目标: main.o
# 依赖: main.c
# 命令: gcc -c main.c
main.o: main.cgcc -c main.c

第二部分:make 的工作原理 (How it Works)

当你输入 make 命令时,它并不是无脑地重新编译所有文件,而是非常“智能”地通过文件时间戳依赖关系来决定做什么。

1. 寻找入口

  • make 会在当前目录下寻找名为 Makefilemakefile 的文件。
  • 它默认会把文件中的 第一个目标 作为最终构建目标(通常命名为 all 或者项目的主程序名)。

2. 递归解析 (依赖树)

  • 为了生成第一个目标,Make 会检查它的依赖列表。
  • 如果依赖文件也是由其他规则生成的,Make 会先跳过去处理那个依赖的规则。
  • 这形成了一个依赖树 (Dependency Graph),Make 会从树的底部(源文件)向树的顶部(最终可执行文件)层层构建。

3. 核心判断逻辑 (时间戳比较)

这是 Make 最强大的地方——增量编译。对于每一个规则,Make 会进行如下判断:

  • 情况 A:目标文件不存在 $\rightarrow$ 执行命令(必须生成)。
  • 情况 B:目标文件存在,但某个依赖文件的修改时间比目标文件更晚(更新) $\rightarrow$ 执行命令(说明源码改了,需要重新编译)。
  • 情况 C:目标文件存在,且所有依赖文件都比目标文件旧 $\rightarrow$ 不执行命令(说明也就是最新的,无需浪费时间重编)。

一个完整的执行流程示例

假设你有三个文件:main.c, func.c, func.h。

Makefile 内容如下:

# 规则1:最终目标 app
app: main.o func.ogcc main.o func.o -o app# 规则2:编译 main.o
main.o: main.c func.hgcc -c main.c# 规则3:编译 func.o
func.o: func.c func.hgcc -c func.c# 规则4:清理
clean:rm *.o app

场景推演:

  1. 第一次执行 make
    • Make 发现需要 app,但 app 不存在。
    • app 依赖 main.ofunc.o,它们也不存在。
    • Make 递归找到规则2和3,先执行 gcc -c main.c 生成 main.o,再执行 gcc -c func.c 生成 func.o
    • 最后依赖齐了,执行 gcc main.o func.o -o app 生成 app
  2. 你修改了 func.c,再次执行 make
    • Make 检查 app,发现依赖 func.o 可能需要更新。
    • 检查 func.o 的规则:发现依赖 func.c 的时间戳比 func.o 新。
    • 动作:重新编译 func.o
    • 检查 main.o 的规则:发现 main.cfunc.h 都没变,且 main.o 比它们都新。
    • 动作:跳过 main.o 的编译(省时)。
    • 回到 app 规则:因为 func.o 刚刚被更新了(时间戳变新了),现在 func.oapp 新。
    • 动作:重新链接生成 app

总结

  • 规则告诉 Make “做什么”(构建什么文件,依赖谁,怎么构建)。
  • 原理则是利用“文件修改时间”来决定“是否真的需要做”,从而极大提高了大型项目的编译效率。

2.单文件编译和多文件编译

一、 单文件编译 (Single-file Compilation)

这是初学者最熟悉的方式。所有的代码(main 函数、自定义函数、全局变量等)都写在一个 .c.cpp 文件里。

1. 工作流程

编译器一次性把这一个文件从源码直接变成可执行文件。

  • 命令示例

    gcc main.c -o app
    
  • 内部过程:预处理 $\rightarrow$ 编译 $\rightarrow$ 汇编 $\rightarrow$ 链接(虽然都在一个命令里完成,但内部还是经过了这些步骤)。

2. 优缺点

  • 优点:简单,一行命令搞定,适合写几十行的练习代码(比如“Hello World”或简单的算法题)。
  • 缺点
    • 难以维护:如果代码有几千行,全堆在一个文件里,找代码会非常痛苦。
    • 编译慢:哪怕你只改了代码里的一行注释,编译器也必须把这几千行代码从头到尾重新编译一遍。

二、 多文件编译 (Multi-file Compilation)

这是工程化开发的标准方式。代码被拆分到不同的文件中,通常按照功能模块划分(例如:main.c 负责主流程,math_func.c 负责数学计算,ui.c 负责界面显示)。

1. 核心概念:编译与链接分离

在多文件编译中,我们引入了 “目标文件” (.o 文件) 的概念。

  • 步骤 1:单独编译 (Compile)
    • 把每个 .c 源文件单独编译成对应的 .o 目标文件(二进制机器码,但还不能运行,因为缺少由于其他文件提供的函数地址)。
    • 命令:gcc -c main.c (生成 main.o)
    • 命令:gcc -c func.c (生成 func.o)
  • 步骤 2:链接 (Link)
    • 把所有的 .o 文件“打包”拼接在一起,生成最终的可执行文件。
    • 命令:gcc main.o func.o -o app

2. 为什么要这么麻烦?(核心优势)

多文件编译配合 Makefile 实现了 增量编译 (Incremental Compilation)

  • 场景:假设你有 100 个文件。你今天只修改了 func.c
  • 发生的事情
    1. 编译器发现只有 func.c 变了,所以只重新编译 func.c $\rightarrow$ 生成新的 func.o
    2. 其他 99 个 .o 文件保持不变(直接用上次生成的)。
    3. 链接器把新的 func.o 和旧的 99 个 .o 链接起来。
  • 结果:编译速度极快(几秒钟 vs 几十分钟)。

三、 图解对比

为了更直观地理解,我们可以看下面这个对比流程:

单文件模式

graph LRA["main.c \n (包含所有代码)"] -->|gcc main.c| B["app \n (可执行文件)"]

多文件模式

graph LRA[main.c] -->|gcc -c| B[main.o]C[func.c] -->|gcc -c| D[func.o]E[utils.c] -->|gcc -c| F[utils.o]B --> G((链接器 Linker))D --> GF --> GG --> H["app \n (可执行文件)"]

四、 它们与 Makefile 的关系

  • 单文件编译通常不需要 Makefile,手动敲一行命令很快。
  • 多文件编译必须依赖 Makefile(或 CMake 等工具)。因为手动敲 gcc -c file1.c ... file100.c 是不可能的任务,而且人脑记不住到底刚才修改了哪个文件、需要重编哪个文件。

3.Makefile的参数传递

—— Makefile 的参数传递

这是你从“写死代码”进阶到“通用脚本”的关键。在实际项目中,我们经常需要根据不同环境(Debug版/Release版、不同的编译器、不同的安装路径)来改变构建行为,而不是去修改 Makefile 源码。

Makefile 参数传递主要有三种方式:

1. 在 Make 命令中直接定义变量 (Command Line Arguments)

这是最常用的方式,用于临时覆盖 Makefile 中的变量。

  • 场景:Makefile 里默认用 gcc,但你想临时测试 clang

  • Makefile 内容

    CC = gcc   # 默认变量
    app: main.c$(CC) main.c -o app
    
  • 你的操作

    make CC=clang
    
  • 结果:Make 会忽略文件里的 CC=gcc,直接使用你传入的 clang

2. 环境变量 (Environment Variables)

Make 启动时,会自动把系统环境变量加载为 Makefile 的变量。

  • 场景:系统里设置了 CFLAGS 包含了一些特定的头文件路径。

  • 操作

    export CFLAGS="-I/usr/local/include"
    make
    
  • 注意:如果有同名变量,命令行参数 > Makefile 内定义 > 环境变量

3. 递归 Make 时的参数传递 (Recursive Make)

当你的项目很大,有子目录(比如 src/, lib/),每个目录下都有自己的 Makefile 时,顶层 Makefile 需要调用子目录的 Makefile。

  • 场景:你在顶层定义了 DEBUG=1,希望这个变量能传给子目录的 Makefile 使用。

  • 关键字export

  • 示例

    # 顶层 Makefile
    export TARGET_ARCH = x86_64  # 将此变量导出,传递给下层subsystem:cd subdir && $(MAKE)
    
  • 原理:加上 export 后,该变量会被放入子 make 进程的环境变量中,从而实现跨层传递。

4.多目录文件夹递归编译与嵌套执行 make

1. 项目目录结构示例

假设你的项目结构如下:

Project/
├── Makefile           <-- 主 Makefile (入口)
├── include/           <-- 头文件
├── lib/               <-- 库文件源码
│   ├── mylib.c
│   └── Makefile       <-- 子 Makefile (编译库)
└── src/               <-- 应用程序源码├── main.c└── Makefile       <-- 子 Makefile (编译主程序)

2. 主 Makefile 的编写 (Project/Makefile)

主 Makefile 的核心任务不是编译具体的 .c 文件,而是遍历子目录并告诉它们去执行各自的 Makefile。

关键指令是:$(MAKE) -C dir

  • $(MAKE): 调用 make 程序本身(比直接写 make 更安全)。
  • -C: 切换到指定目录 (Change directory)。

代码示例:

# 定义子目录列表
SUBDIRS = lib src# 定义编译器和通用参数,并通过 export 传递给子目录
CC = gcc
CFLAGS = -I../include -Wall
export CC CFLAGS# 伪目标:防止目录下有同名文件导致 make 误判
.PHONY: all clean $(SUBDIRS)# 默认目标
all: $(SUBDIRS)# 递归规则:进入子目录并执行 make
$(SUBDIRS):@echo "正在编译子目录: $@"$(MAKE) -C $@# 注意:这里可能需要控制顺序,比如 src 依赖 lib
src: lib# 清理规则:递归清理
clean:@for dir in $(SUBDIRS); do \$(MAKE) -C $$dir clean; \done@echo "清理完成"

3. 子目录 Makefile 的编写

子目录的 Makefile 只需要关注自己目录下的文件编译。它可以直接使用主 Makefile export 下来的变量(如 CCCFLAGS)。

A. lib/Makefile (生成静态库示例):

OBJ = mylib.oall: libmy.alibmy.a: $(OBJ)ar rcs $@ $^%.o: %.c$(CC) $(CFLAGS) -c $< -o $@clean:rm -f *.o *.a

B. src/Makefile (生成可执行文件示例):

TARGET = main
OBJ = main.o
# 引用兄弟目录的库
LDFLAGS = -L../lib -lmyall: $(TARGET)$(TARGET): $(OBJ)$(CC) $^ -o $@ $(LDFLAGS)%.o: %.c$(CC) $(CFLAGS) -c $< -o $@clean:rm -f *.o $(TARGET)

4. 关键技术点总结

  1. $(MAKE) -C subdir: 这是递归编译的核心。它告诉 Make 工具:“进入 subdir 目录,并在那里像在新环境中一样运行 make”。
  2. export 关键字: 在主 Makefile 中定义的变量(如编译器 CC,编译选项 CFLAGS),默认不会传递给子 make。使用 export 可以让所有子 Makefile 共享这些配置,保证构建的一致性。
  3. .PHONY (伪目标): 必须将子目录名声明为 .PHONY。否则,如果你的磁盘上正好有一个叫 src 的文件夹(确实有),Make 会认为这个“文件”已经存在且是最新的,从而跳过编译。
  4. 构建顺序: 如果 src 中的代码依赖 lib 生成的库,你必须在主 Makefile 中显式声明依赖关系(如上面代码中的 src: lib),确保 lib 先被编译。

5.Makefile的通配符,伪目标,文件搜索

1. 通配符 (Wildcards)

在 Makefile 中,通配符的使用与 Linux Shell(命令行)中的通配符非常相似。最常用的有两个:

  • * :匹配任意长度的任意字符。
  • ? :匹配单个任意字符。

A. 在规则中使用

在规则的命令(command)中,通配符由 Shell 自动展开;在规则的目标(target)和依赖(prerequisite)中,通配符由 make 程序展开。

示例:

clean:rm -f *.o  # Shell 会将其展开为所有 .o 文件

B. 在变量定义中的陷阱(重要!)

如果你在变量定义中直接使用通配符,make 不会自动展开它。

  • 错误写法:

    OBJECTS = *.o
    # 此时 OBJECTS 的值就是字符串 "*.o",而不是具体的文件列表
    
  • 正确写法(使用 wildcard 函数):

    如果想获取当前目录下所有的 .c 文件列表,必须使用 wildcard 关键字。

    SRC = $(wildcard *.c)
    # 此时 SRC 的值例如是: "main.c utils.c config.c"
    

2. 伪目标 (Pseudo-targets / Phony Targets)

什么是伪目标?

伪目标是指那些不代表具体文件的目标。我们执行它只是为了运行下面的命令,而不是为了生成一个名为该目标的文件。

为什么需要伪目标?

  1. 避免文件名冲突: 假设你的目录下刚好有一个文件叫 clean。如果你运行 make clean,make 会检查 clean 文件,发现它“已经存在且是最新的”,因此不会执行删除命令。
  2. 提高执行效率: 明确告诉 make 这是一个伪目标,make 就不必去文件系统中查找隐式规则或检查时间戳。

如何声明?

使用特殊目标 .PHONY 来声明。

示例:

.PHONY: clean all installall: programprogram: main.ogcc -o program main.o# 即使当前目录下有一个叫 'clean' 的文件,下面的命令依然会被强制执行
clean:rm -f *.o program

在大型项目中,源代码(.c)、头文件(.h)和二进制文件通常放在不同的目录(如 src/, include/, bin/)。默认情况下,make 只在当前目录查找依赖文件。如果找不到,就会报错。

为了解决这个问题,Makefile 提供了路径搜索机制。

A. VPATH 变量 (全大写)

这是一个特殊变量。定义后,如果当前目录找不到文件,make 会去 VPATH 指定的目录列表查找。目录间用冒号 : 分隔。

示例:

VPATH = src:../headers
# make 会先找当前目录,找不到再去 src,还找不到再去 ../headers

B. vpath 关键字 (全小写)

vpathVPATH 更灵活,它可以针对特定模式的文件指定搜索路径。

语法: vpath <pattern> <directories>

示例:

# 所有的 .c 文件,去 src 目录找
vpath %.c src# 所有的 .h 文件,去 include 目录找
vpath %.h includemain.o: main.cgcc -c $< -o $@
# 即使 main.c 在 src/ 目录下,make 也能通过 vpath 找到它

总结与最佳实践

概念 核心作用 最佳实践
通配符 批量匹配文件名 在变量赋值时,务必配合 $(wildcard *.c) 函数使用,不要直接写 *.c
伪目标 定义动作而非文件 凡是不生成文件的命令(如 clean, install, test),都应该用 .PHONY 声明。
文件搜索 解决多目录依赖问题 推荐使用小写的 vpath,因为它能更精确地控制哪类文件去哪个目录找,避免不同类型文件重名带来的混乱。

6.Makefile的操作函数和特殊语法

一、 Makefile 的常用函数 (Functions)

Makefile 的函数调用语法是 $(function arguments),参数之间用逗号 , 分隔。

1. 字符串处理函数

这是最常用的,用于批量转换文件名。

  • $(patsubst pattern,replacement,text)

    • 作用: 模式替换。查找 text 中符合 pattern 的单词,替换为 replacement

    • 场景: 将所有的 .cpp 文件替换为 .o 文件。

    • 示例:

      SRC = main.cpp utils.cpp
      OBJ = $(patsubst %.cpp, %.o, $(SRC))
      # 结果:OBJ = main.o utils.o
      
    • 简写技巧: 上面的写法常简写为 OBJ = $(SRC:.cpp=.o)

  • $(subst from,to,text)

    • 作用: 简单的文本替换(不支持 % 通配符)。
    • 场景: 处理路径字符串等。
  • $(strip string)

    • 作用: 去除字符串开头和结尾的空格,并将中间多个空格合并为一个。
    • 场景: 在比较变量值时非常有用,防止因为多余空格导致判断错误。

2. 文件名操作函数

用于从路径中提取信息。

  • $(dir names...):取目录部分(保留最后的 /)。

  • $(notdir names...):取文件名部分(去掉目录)。

  • $(abspath names...):获取文件的绝对路径。

  • 示例:

    FILE_PATH = src/foo.c
    DIR_NAME = $(dir $(FILE_PATH))    # 结果:src/
    FILE_NAME = $(notdir $(FILE_PATH)) # 结果:foo.c
    

3. Shell 交互函数

  • $(shell command)

    • 作用: 执行 Shell 命令并返回输出结果。这非常强大。

    • 场景: 获取当前时间、查找文件、调用 pkg-config 获取库路径等。

    • 示例:

      CURRENT_DIR = $(shell pwd)
      LIB_FLAGS = $(shell pkg-config --libs opencv)
      

4. 控制与循环

  • $(foreach var,list,text)
    • 作用: 类似编程中的 for 循环。把 list 中的每个单词依次赋值给 var,然后执行 text 表达式。
    • 场景: 遍历多个目录查找源文件。

二、 Makefile 的特殊语法 (Special Syntax)

1. 自动化变量 (Automatic Variables)

这是 Makefile 的灵魂,必须死记硬背。它们只能在规则的命令中使用。

变量 含义 助记
$@ 目标文件 (Target) 的名字 "At" the target (瞄准目标)
$< 第一个依赖文件 (First Prerequisite) 的名字 指向左边(来源)
$^ 所有依赖文件 的列表(去重) 像一个屋顶(覆盖所有)
$? 所有比目标新的依赖文件 哪些文件变了?(Question)

实战示例:

main.o: main.cpp utils.h# 这里的 $@ 是 main.o# 这里的 $< 是 main.cpp# 这里的 $^ 是 main.cpp utils.hg++ -c $< -o $@

2. 变量赋值的四种方式

Makefile 中有四种赋值符号,它们的区别是面试和实际开发中的大坑:

  1. = (递归展开赋值 / Recursive)
    • 变量的值在使用时才确定。如果引用的变量后面变了,这里也会变。
    • 缺点: 容易造成无限循环。
  2. := (立即展开赋值 / Simple) 【推荐】
    • 变量的值在定义时就确定了。类似 C++ 的常规变量赋值。
    • 建议: 除非有特殊理由,否则默认使用 :=,效率更高且不易出错。
  3. ?= (条件赋值 / Conditional)
    • 只有当变量未定义时,才进行赋值。如果已经有值了,则忽略。
  4. += (追加赋值 / Appending)
    • 在原变量后面添加新值(自动加空格)。

对比示例:

x = foo
y = $(x) bar
x = xyz
# 此时 y 的值是 "xyz bar" (因为是用 =,会延后展开)a := foo
b := $(a) bar
a := xyz
# 此时 b 的值是 "foo bar" (因为是用 :=,定义时就锁死了)

3. 命令修饰符

在规则的命令前加特殊符号:

  • @:静默执行。执行命令时,不在终端打印命令本身(只打印输出结果)。
    • 常用:@echo "Compiling..."
  • -:忽略错误。即使命令报错(exit code 非 0),make 也会继续执行。
    • 常用:-rm *.o (如果文件不存在,rm 会报错,但我们不希望清理过程中断)。

三、 综合应用示例

结合上面所学,我们可以写出一个自动查找所有 .cpp 并编译的通用模块:

# 1. 使用 := 定义变量
CC := g++
CFLAGS := -Wall -g# 2. 使用 wildcard 和 shell 查找源文件
SRCS := $(wildcard src/*.cpp)# 3. 使用 patsubst 自动生成对应的 .o 文件名
OBJS := $(patsubst src/%.cpp, build/%.o, $(SRCS))# 4. 伪目标
.PHONY: all cleanall: my_program# 5. 使用自动化变量 $@ 和 $^
my_program: $(OBJS)@echo "Linking..."$(CC) $^ -o $@# 6. 模式规则 + 自动化变量 $< + @静默输出
build/%.o: src/%.cpp@echo "Compiling $<..."@mkdir -p build$(CC) $(CFLAGS) -c $< -o $@clean:-rm -rf build my_program

7.configure生成makefile的原则

1. 核心流程:输入与输出

理解这个过程的最佳方式是看它“吃进去”什么,又“吐出来”什么:

  • 输入 (Input): Makefile.in (这是一个模板文件,里面充满了占位符)。
  • 处理 (Processing): configure 脚本运行一系列测试,探测系统环境。
  • 输出 (Output): Makefile (这是一个可执行的构建文件,占位符已被实际值替换)。

2. 四大生成原则

A. 环境探测原则 (Environment Detection)

configure 不假设你的系统是什么样的,而是去“亲自检查”。它会尝试编译小程序或查找文件来确认:

  • 编译器检查: 你有 gcc 还是 clang?它的版本支持哪些特性?
  • 库文件检查: 编译所需的第三方库(如 libssl, libz)是否存在?路径在哪里?
  • 头文件检查: stdlib.h, unistd.h 等系统头文件是否存在?
  • 系统特性: 是大端序(Big Endian)还是小端序?是 32 位还是 64 位?

原则: 如果探测失败(例如缺少关键库),configure 会报错并停止,阻止生成错误的 Makefile

B. 变量替换原则 (Variable Substitution)

这是最机械但也最重要的原则。Makefile.in 中包含大量格式为 @VARIABLE@ 的占位符。configure 的任务就是根据探测结果,用实际值替换它们。

  • 模板 (Makefile.in):

    CC = @CC@
    CFLAGS = @CFLAGS@
    INSTALL_DIR = @prefix@
    
  • 探测结果: 用户系统有 gcc,用户指定了安装路径 /usr/local

  • 生成结果 (Makefile):

    CC = gcc
    CFLAGS = -g -O2
    INSTALL_DIR = /usr/local
    

C. 用户自定义优先原则 (User Customization)

configure 允许用户通过命令行参数覆盖默认探测结果。用户的指令优先级最高。

  • 路径控制: 比如 --prefix=/opt/myapp 告诉脚本把软件装到 /opt/myapp 而不是默认的 /usr/local
  • 功能开关: 比如 --enable-debug--without-gui,这些开关会改变 Makefile 中的编译选项或源码文件列表。

D. 依赖与路径绝对化原则

为了保证后续运行 make 时不出错,configure 生成的 Makefile 通常会使用绝对路径或经过验证的路径。它确保构建工具能找到源代码目录(srcdir)和构建目录(builddir),特别是在“源码目录”和“构建目录”分离(Out-of-source build)的情况下。


3. 直观示例:从模板到成品

假设我们有一个简单的 C++ 项目。

第一步:模板文件 (Makefile.in)

这是开发者(或 Automake)提供的,不包含具体的编译器信息:

# Makefile.in
CXX = @CXX@               # 编译器占位符
CXXFLAGS = @CXXFLAGS@     # 编译选项占位符
LIBS = @LIBS@             # 依赖库占位符
PREFIX = @prefix@         # 安装路径占位符main: main.o$(CXX) -o main main.o $(LIBS)install:cp main $(PREFIX)/bin/

第二步:运行 Configure

用户在 Linux 终端执行:

./configure --prefix=/home/user/app --with-mylib

此时 configure 做了这些事:

  1. 检测到系统有 g++,所以 @CXX@ -> g++
  2. 检测到用户指定了路径,所以 @prefix@ -> /home/user/app
  3. 检测到需要链接某数学库,所以 @LIBS@ -> -lm

第三步:生成的成品 (Makefile)

这是最终写在磁盘上的文件:

# Makefile (Generated)
CXX = g++
CXXFLAGS = -g -O2
LIBS = -lm
PREFIX = /home/user/appmain: main.og++ -o main main.o -lminstall:cp main /home/user/app/bin/

4. 幕后功臣:config.status

当你运行 configure 结束时,你会看到它打印 creating Makefile。实际上,configure 脚本并不直接写文件,它会生成一个名为 config.status 的 shell 脚本。

  • config.status 保存了所有的配置结果(编译器是谁、路径在哪等)。
  • 正是 config.status 利用 sed 等工具,执行了从 Makefile.inMakefile 的文本替换工作。
  • 好处: 如果你只是修改了 Makefile.in 而没有改变系统环境,你只需要重新运行 ./config.status 就能瞬间重新生成 Makefile,而不需要重新跑一遍耗时的 configure 检测。

总结

configure 生成 Makefile 的原则就是:Makefile.in 为骨架,以系统环境探测结果为血肉,以用户参数为灵魂,最终通过文本替换技术组装成可用的构建脚本。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询