Linux学习 -- Makefile

本文介绍Makefile的基础概念及其在Linux编程中的应用。通过一个逐步进化的Makefile实例,讲解如何利用Makefile自动化编译流程,提高开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux学习 – Makefile


什么是makefile

什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的programmer,makefile还是要懂。这就好像现在有这么多的HTML的编辑器,但如果你想成为一个专业人士,你还是要了解HTML的标识的含义。特别在Unix下的软件编译,你就不能不自己写makefile了,会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。

因为,makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。

makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。

makefile的作用

make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。
1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。

只要我们的Makefile写得够好,所有的这一切,我们只用一个make命令就可以完成,make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。

makefile的规则

在讲述这个Makefile之前,还是让我们先来粗略地看一看Makefile的规则。

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

target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。

prerequisites就是,要生成那个target所需要的文件或是目标。
command也就是make需要执行的命令。(任意的Shell命令)
这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。
说到底,Makefile的东西就是这样一点,好像我的这篇文档也该结束了。呵呵。还不尽然,这是Makefile的主线和核心,但要写好一个Makefile还不够,我会以后面一点一点地结合我的工作经验给你慢慢到来。内容还多着呢。:)

一个简单的makefile的进化过程

Demo目录下有如下源码
1. main.c
2. sub.h sub.cpp
3. add.h add.cpp

代码如下

main.c

#include <stdio.h>
#include "sub.h"
#include "add.h"

int main()
{
    printf("Add: %d\n",add(5,3));
    printf("Sub: %d\n",sub(5,3));

    return 0;
}

sub.h

#ifndef __SUB_H__
#define __SUB_H__

int sub(int l, int r);

#endif

sub.cpp

#include "sub.h"

int sub(int l, int r)
{
    return l+r;
}

add.h

#ifndef __ADD_H__
#define __ADD_H__

int add(int l, int r);

#endif

add.cpp

#include "add.h"

int sub(int l, int r);

gcc -c hello.c -o hello.o
利用GCC编译要执行如下指令

gcc -Wall -g -c sub.c -o sub.o
gcc -Wall -g -c add.c -o add.o
gcc -Wall -g -c main.c -o main.o
gcc -Wall -g main.o sub.o add.o -o main

一个最简单的最直观的makefile就是直接对上述指令进行封装
makefile 第一版本

main: main.o sub.o add.o
    gcc -Wall -g *.o -o main
main.o: main.c
    gcc -Waa -g -c main.c -o main.o
sub.o: sub.c sub.h
    gcc -Waa -g -c sub.c -o sub.o
add.o: add.c add.h
    gcc -Wall -g -c sub.c -o sub.o
clear:
    rm -f *.o main

makefile 第二版本

.PHONY:clear
OBJS=main.o sub.o add.o
main: $(OBJS)
    gcc -Wall -g $^ -o $@
sub.o: sub.c sub.h
    gcc -Wall -g -c $< -o $@
add.o: add.c add.h
    gcc -Wall -g -c $< -o $@
clear:
    rm -f $(OBJS) main

$@ $^ $<为makefile自动化变量其含义如下

  • $@: 规则的目标文件名 在上述makefile中分别表示 main sub.o add.o
  • $^: 规则的目标文件名 在上述makefile中分别表示 main.o sub.o add.o
  • $<: 规则的第一个依赖文件 在上述makefile中分别表示 sub.c add。c
    OBJS为定义的变量,在makefile中变量引用要加入 $(变量名)

makefile 第三版本

.PHONY:all clear

SRC=$(wildcard *.c)
BIN=$(SRC:%.c=%)
OBJS=$(SRC:%.c=%.o)

CC=gcc
CFLAGS=-Wall -g

all:$(BIN)

$(BIN):%:%.o
    $(CC) $(CFLAGS) $< -o $@

.c.o:
    $(CC) $(CFLAG) -c $< -o $@

clear:
    rm -f $(BIN) $(OBJ)

此makefile可以根据多个.c文件生成对应的可执行文件
解释:

  • .c.o 相当于 %.c:%.o 为makefile匹配模式,代表生成对用的.o文件要依赖对应的.c文件。
  • 编译器自动推导:同名的.c文件生成同名的目标文件

  • $(wildcard 模式) -列出当前目录下匹配模式的文件, $(wildcard *.c) 列出当前目录下所有的.c文件,

  • BIN: 代表要生成的目标,BIN=$(SRC:%.c=%) 及将所有.c文件生成对应的可执行文件

以下还有其他几个版本拿出来一起参考一下

.PHONY:all clear

SRC=$(wildcard *.c)
BIN=$(SRC:%.c=%)
OBJS=$(SRC:%.c=%.o)

CC=gcc
CFLAGS=-Wall -g

all:$(BIN)

%:%.o
    $(CC) $(CFLAGS) $^ -o $@

.c.o:
    $(CC) $(CFLAG) $< -o $@

clear:
    rm -f $(BIN) $(OBJ)

.PHONY:all clear

SRC=$(wildcard *.c)
BIN=$(SRC:%.c=%)
OBJS=$(SRC:%.c=%.o)

CC=gcc
CFLAGS=-Wall -g

print:
    @echo SRC=$(SRC)
    @echo BIn=$(BIN)
    @echo OBJS=$(OBJS)

all:$(BIN)

$(BIN):%:%.o
    $(CC) $(CFLAGS) $< -o $@

#.c.o:
$(OBJ):%.o:%.c
    $(CC) $(CFLAG) -c $< -o $@

clear:
    rm -f $(BIN) $(OBJS)
.PHONY:clear all
CC=gcc
CFLAGS=-Wall -g
all: main hello hello1
OBJS=main.o sub.o add.o
main:$(OBJS)
    $(CC) $(CFLAGS) $^ -o $@

hello:hello.o
    $(CC) $(CFLAGS) $^ -o $@

hello1:hello1.o
    $(CC) $(CFLAGS) $^ -o $@

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

clear:
    rm -f main *.o

.PHONY:all clear

SRC=$(wildcard *.c)
BIN=$(SRC:%.c=%)
OBJS=$(SRC:%.c=%.o)

CC=gcc
CFLAGS=-Wall -g

print:
    @echo SRC=$(SRC)
    @echo BIn=$(BIN)
    @echo OBJS=$(OBJS)

all:$(BIN)

$(BIN):%:%.o
    $(CC) $(CFLAGS) $< -o $@

#.c.o:
$(OBJ):%.o:%.c
    $(CC) $(CFLAG) -c $< -o $@

clear:
    rm -f $(BIN) $(OBJS)

.PHONY:all clear

SRC=$(wildcard *.c)
BIN=$(SRC:%.c=%)
OBJS=$(SRC:%.c=%.o)

CC=gcc
CFLAGS=-Wall -g

print:
    @echo SRC=$(SRC)
    @echo BIn=$(BIN)
    @echo OBJS=$(OBJS)

all:$(BIN)

$(BIN):%:%.o
    $(CC) $(CFLAGS) $< -o $@

#.c.o:
$(OBJ):%.o:%.c
    $(CC) $(CFLAG) -c $< -o $@

clear:
    rm -f $(BIN) $(OBJS)

此makefile可用于多级目录编译,每个目录有自己的makefile,然后再最外层执行这个makefile

SUBDIRS=$(shell ls -d */)

#printf:
#   @echo SUBDIRS=$(SUBDIRS)

.PHONY:default all clean $(SUBDIRS)

default:all
    @echo -----------0
all clean:
    $(MAKE) $(SUBDIRS) TARGET=$@
    @echo ----------1

$(SUBDIRS):
    $(MAKE) -C $@ $(TARGET)  #make -C Demo1 all
    @echo -----------2

makefile这个东西对于在Linux下编程,十分重要,多写一些就能掌握其中的规则,同时要懂一些shell编程,可更容易写出高质量的makefile

<think>好的,我需要帮助用户正确配置和使用arm-linux-gnueabihf-gcc在Makefile中进行交叉编译。首先,我得回顾用户提供的引用内容,看看有哪些关键点需要涵盖。 根据引用[1]和[2],用户提到了在Makefile中设置交叉编译工具链的命令,比如CC和LD变量的定义,特别是静态链接的使用。引用[3]和[4]则涉及交叉编译器的安装和路径配置。所以,我需要将这些步骤整合成一个清晰的流程。 首先,安装交叉编译器。用户提供的引用3提到根据系统选择32位或64位的版本,并正确解压到指定目录。然后设置环境变量,如引用4中的PATH导出,这样才能在终端中找到arm-linux-gnueabihf-gcc命令。 接下来是Makefile的配置。引用2中的模板显示需要定义CROSS变量,然后设置CC、LD等工具链变量。引用1中的例子展示了如何修改Makefile中的CC和LD,特别是添加-static参数进行静态链接。需要注意的是,用户可能需要根据具体项目调整编译选项,比如CFLAGS和LDFLAGS。 然后,用户可能需要了解如何执行编译,比如使用make命令,以及如何处理依赖库的问题,如引用3提到的openssl静态库和动态库的编译。这部分可能需要指导用户如何链接这些库,或者如何交叉编译依赖项。 最后,常见问题部分应该涵盖路径错误、静态链接问题、依赖库缺失和清理构建环境。这些都是用户在配置过程中可能遇到的典型问题,需要逐一解答。 需要确保步骤清晰,语言简洁,并正确引用提供的资料。同时,生成的相关问题要围绕交叉编译的配置、优化和应用场景展开,帮助用户进一步学习。</think>### 配置和使用 `arm-linux-gnueabihf-gcc` 在 Makefile 中进行交叉编译 #### 1. **安装交叉编译工具链** - **下载工具链**:根据系统架构选择对应的版本(32位或64位),例如: ```bash wget https://releases.linaro.org/components/toolchain/binaries/5.5-2017.10/arm-linux-gnueabihf/gcc-linaro-5.5.0-2017.10-x86_64_arm-linux-gnueabihf.tar.xz ``` - **解压并安装**: ```bash tar -xvf gcc-linaro-5.5.0-2017.10-x86_64_arm-linux-gnueabihf.tar.xz -C /opt ``` - **添加环境变量**:在 `~/.bashrc` 或终端中临时设置: ```bash export PATH=/opt/gcc-linaro-5.5.0-2017.10-x86_64_arm-linux-gnueabihf/bin:$PATH ``` 然后执行 `source ~/.bashrc` 生效[^3][^4]。 --- #### 2. **配置 Makefile** - **定义交叉编译工具链**: ```makefile CROSS = arm-linux-gnueabihf- CC = $(CROSS)gcc LD = $(CROSS)ld ``` 若需静态编译(避免动态库依赖),添加 `-static` 参数: ```makefile CC = $(CROSS)gcc -static LD = $(CROSS)ld -static ``` 引用示例中的修改方法[^1][^2]。 - **设置编译选项**: ```makefile CFLAGS = -Wall -O2 LDFLAGS = -L/path/to/arm-libs ``` --- #### 3. **编译与链接** - **编译目标**:在 Makefile 中指定目标文件与依赖关系: ```makefile TARGET = my_program OBJS = main.o utils.o $(TARGET): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ ``` - **执行编译**: ```bash make ``` - **验证二进制文件**: ```bash file my_program # 应显示 "ARM EABI" 或类似标识 ``` --- #### 4. **处理依赖库** - **交叉编译第三方库**:例如 OpenSSL: ```bash ./Configure linux-armv4 -static --prefix=/opt/arm-openssl --cross-compile-prefix=arm-linux-gnueabihf- make && make install ``` 在 Makefile 中指定库路径: ```makefile CFLAGS += -I/opt/arm-openssl/include LDFLAGS += -L/opt/arm-openssl/lib -lssl -lcrypto ``` 参考 OpenSSL 交叉编译方法[^3]。 --- #### 5. **常见问题** - **路径错误**:确保 `arm-linux-gnueabihf-gcc` 在 `PATH` 中,否则报错 "command not found"。 - **静态链接失败**:检查工具链是否支持静态库,或手动安装静态库版本。 - **依赖库缺失**:交叉编译时需为 ARM 架构重新编译所有依赖库。 - **清理构建**:在 Makefile 中添加: ```makefile clean: rm -f $(TARGET) $(OBJS) ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值