Linux-make

部署运行你感兴趣的模型镜像

简介

  • make,即make工程管理器
  • Linux下的管理编译的工具(编译好了=代码可以运行了=工程管理好了)
    • Linux没有IDE(集成开发环境)用于代码编写编译和运行
    • 直接拿着gcc搞有点难受,相当于用 make+Makefile 封装了一下
  • 通过读入 Makefile文件(规则),执行编译工作
    • make命令是就是执行这个文件里的语句
    • make 可以理解成一种编程语言(python .py)
  • 优点
    • 对于维护一些具有相互依赖关系的文件特别有用(自动关联)
    • 自动确定需要重新编译的文件,不做无用功,提高效率
    • 起初适用于C语言,但只要提供相应的编译器,mM可以满足你的所有幻想(管理编译器的工具)
  • 一般直接拿着gcc搞是怎样的过程呢?
    • 准备几个文件:add.c add.h sub.c sub.h main.c
      # main.c
      #include<stdio.h>
      #include "add.h"
      #include "sub.h"
      
      int main(){
          int a,b;
          printf("请输入a和b的值:");
          scanf("%d%d",&a,&b);
          printf("%d + %d = %d\n",a,b,add(a,b));
          printf("%d + %d = %d\n",a,b,sub(a,b));
      
          return 0;
      }
      
      # add.c
      int add(int a, int b){
          return a+b;
      }
      
      # sub.c
      int sub(int a, int b){
          return a-b;
      }
      
      # add.h
      int add(int, int);
      # sub.h
      int sub(int, int);
      
    • 执行命令
      gcc -c main.c
      gcc -c add.c
      gcc -c sub.c
      # 生成.o文件
      
      # 生成可执行文件
      gcc *.o -o calc.exe
      ./calc.exe
      
  • 以上就是使用gcc的完整过程,很麻烦吧,使用Makefile直接写好,make执行
  • 详细的gcc过程可以参看这里,主要是预处理-编译-汇编-链接

机制

  • make命令参数
    1
  • make 使用格式:make [options] [target]
    • make命令的执行过程分析
      2
      3

目标

  • Makefile基本语法格式
    target:relys
    	 command
    	 ...
    
    # target 目标文件,多个文件的话以空格分开,make后会生成这个名字的文件
    # relys 生成目标所依赖的文件
    # command 命令行,这里的命令和/bin/bash类似,很多也是直接交给shell执行,所以...
    # command可以跟人rely后使用分号隔开,也可以换行+Tab(必须用Tab!!!)
    # 一行太长都可以用 \ 换行
    
    • 就是依靠一个接一个目标完成编译过程的
  • 程序片段,说明语法
    all : main.o keys.o
    	gcc main.o -o all	# 使用gcc编译,生成all文件,必须Tab
    	
    # 可以使用通配符 * ? [...]
    clean : 
    	rm -f *.o	# 删除所有当前的.o文件
    	# 或者直接使用 $(RM) $(OBJS) $(TARGET)
    	
    # 这里的clean是伪目标,后面没有依赖并不会直接生成clean文件
    # 为了防止和目标文件重名,可以
    .PHONY : clean
    # 伪目标特性:总是被执行,所以比较新,如下:
    all : p1 p2 p3
    .PHONY : all
    p1 :  xxx
    	xxx
    p2 : ...	# 这里all被定义为伪目标,每次重新编译会被执行而且最新,所以后面的依赖也会重新编译!
    
    # 赋值
    objects = *.o	# 这里是单纯的将 *.o 给变量,不展开
    # 如果要展开
    objects := $(wildcard *.o)	# 这样赋值; wildcard是mk的关键字
    
    • 伪目标一般会跟着make命令一起,例如:make clean all,如果不指定就从头到尾执行
    • clean删除已生成的编译文件,all刷新(重编译)
    # make 会按顺序执行Mf中的文件
    # 如果要让上一条命令的结果应用于下一条,必须使用分号
    exec :
    	cd /home/dev
    	pwd		# 这里显示的还是Mf所在的目录
    # 应该这样
    exec :
    	- cd /home/dev; pwd		# 有点像管道
    # - 作用是不管是否出错都认为是成功的,不中断执行
    
  • 具体说说,Makefile文件包含的内容:
    • 显式规则,生成目标的格式
    • 隐式规则,自动编译依赖文件,得到.o
    • 变量定义,函数调用
    • 文件引用,引用其他Makefile,include <filename>
    • 注释

变量

  • 作为语言,自然有变量/流程控制等等
  • 变量命名规则和其他语言类似,但是可以数字开头
  • 声明变量必须赋初值,使用时需要加上$符号,可以使用()/{}括起来
    objects = main.o test.o fun.o
    program : $(objects)
    	cc -o p1 ${objects}
    
  • 如果以:=赋值,代表只能使用之前声明的变量
    y := ${x}bar
    x := foo
    all : 
    	echo ${y}	# 输出的是bar,而不是foobar
    
  • 类似的,?= 表示如果之前有这个变量就啥也不做,如果没有就按现在这么定义
    foo ?= hello.o		# 相当于带了条件判断语句
    
  • 使用 += 追加
    objects = main.o test.o
    objects += hello.o
    # 会继承之前的赋值操作符,例如之前使用 := ,加上之后就还是 :=
    
  • 几个特殊的变量
    请添加图片描述
    • 类似简介部分的例子,编写Makefile
    # first_make
    # $^ 依赖项,不重复
    # $@ 目标名称
    # @ 不在控制台显示此编译命令
    # - 失败后继续执行后面的代码
    
    TARGET=first_make
    LIBS=-lpthread	# 库,一般都叫这个名
    
    $(TARGET):first_make.cpp xdata.cpp
    	-@rm test
    	@$(CXX) $^ -o $@ $(LIBS)
    	echo "$(TARGET) build success!"	# 代码区间
    	@# 代码区间的注释也要加@,不然也会显示出来
    

预定义变量

  • 预定义变量可以简化Makefile文件
    4
    • CXXCXXFLAGS常用到,对于使用c++程序而言

自动推导

  • 也叫隐式规则
  • 还是以上面的代码为例,我们可以不写依赖项(first_make.cpp xdata.cpp),make可以自动推导,编译出对应的.o文件
  • 但是,例如当我们的代码中使用了其他路径下的.h文件,需要使用CXXFLAGS,指明参数,改进如下:
    TARGET=first_make
    LIBS=-lpthread	# 库,一般都叫这个名
    OBJS=first_make.o xdata.o
    CXXFLAGS=-I../test.h	# -I 选项	指明头文件路径
    
    $(TARGET):$(OBJS)
    	-@rm test
    	@$(CXX) $^ -o $@ $(LIBS)
    	echo "$(TARGET) build success!"	# 代码区间
    
  • 其实可以发现,这种推导很有限,直说得了!

函数

  • 格式;这里都是调用,没说定义,记着第一个是函数名就行,可以再查
    $(func_name arg1,arg2,arg3...)	# 也可以是 {},不同 参数都要逗号隔开的
    
  • 字符串替换函数
    $(subst <from>, <to>, <text>)	# 将text中的from替换成to,返回被替换的字符串
    
  • 查找字符串函数
    $(findstring <find>, <in>)	# 在字符串in中查找find
    
  • 过滤函数
    $(filter <pattern>, <text>)	# 过滤出符合pattern规则的字符串
    # 例子
    foo := main.c test.c hello.s fuck.h
    source : $(foo)
    	cc $(filter %.c %.s, $(foo)) -o source	# 要用()就都用()
    
    • filter-out函数返回去除掉符合pattern的字符串
  • 排序函数
    $(sort <list>)	# 按字母排序list中的元素
    
  • 取单词函数
    $(word <n>, <text>)	# 取text中第n个单词
    
    # 取单词串
    $(wordlist <s>,<e>,<text>)	# 取出text中从s个到e个的单词,单词计数都是从1开始
    
    # 单词个数统计函数
    $(words <text>)
    
    # 取首单词函数
    $(firstword <text>)
    
  • 取目录函数
    $(dir <dirs>)
    # 例子
    $(dir /usr/local/bin/nginx hello.c)	# /usr/local/bin ./	hello是当前目录下的,所以返回./
    
    # 取文件名函数
    $(notdir <names>)
    # 例子
    $(dir /usr/local/bin/nginx, hello.c)	# nginx hello.c
    
    # 取后缀名函数
    $(suffix <names>)	# 如果文件没有后缀名返回空字符
    
    # 取前缀函数
    # 例子
    $(basename usr/local/bin/nginx hello.c /etc/yum.repo.d)	# usr/local/bin/nginx hello /etc/yum.repo
    
    • 类似的,有加后缀加前缀函数
  • 循环函数
    names := a,b,c,d
    files := $(foreach i, $(names), $(i).c)	# 得到a.c b.c c.c d.c
    # 类似python的for i in some,这里将表达式直接放到最后而已
    
  • 条件判断函数
    # 格式
    $(if <condition>, <then-part>, <else-part>)	# 只有一个part会被执行
    

链接库

  • 包括动态库和静态库,以项目说明
    5
  • 先准备动态库的文件,xthread
    6
    7
  • 使用-shared参数编译成动态库,编写Makefile文件:
    8
    • LDFLAGS指定链接选项,LD指定链接器,一般是ld
  • 使用编译的动态库,在xserver中,编译时指定链接的动态库(LIBS),运行时还要指定动态库路径(位置)
    9
    10
  • 静态库:待续

小结

  • make是管理编译器的语言,使用该语言编写Makefile文件,即可按规则编译,得到需要的目标文件(可执行文件)

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think>我们注意到用户提供了一个`echo`命令的输出,显示`MAKE_FLAGS`的内容。这些内容定义了一系列工具链变量,如`AR`、`AS`、`CC`等,并且这些工具链都是针对32位ARM架构的(`arm-buildroot-linux-gnueabi-`前缀)。然而,在之前的编译错误中,我们遇到的是64位ARM(aarch64)架构的问题。因此,关键问题在于:`MAKE_FLAGS`中设置的工具链是32位的,而我们需要的是64位工具链。 ### 问题分析: 在Makefile的`Build/Compile`部分,我们传递了以下变量: ```makefile $(MAKE) -C $(PKG_BUILD_DIR) compile ... \ $(MAKE_FLAGS) \ $(KERNEL_MAKE_FLAGS) \ CROSS_COMPILE=$(KERNEL_CROSS) \ EXTRA_CFLAGS="$(EXTRA_CFLAGS)" ``` 由于`$(MAKE_FLAGS)`和`$(KERNEL_MAKE_FLAGS)`都是变量扩展,它们会将其中定义的变量传递给子make。根据输出,`MAKE_FLAGS`中设置了`ARCH=arm64`,但同时设置了32位的工具链(如`CC=arm-buildroot-linux-gnueabi-gcc`)。而`KERNEL_CROSS`被设置为`aarch64-buildroot-linux-gnu-`(64位工具链)。但是,由于`$(MAKE_FLAGS)`在命令行中先出现,它设置的`CC`等变量会覆盖后面通过`CROSS_COMPILE`设置的变量(因为`CROSS_COMPILE`只是一个前缀,而`CC`是直接指定的编译器路径)。 ### 具体冲突: - `MAKE_FLAGS`设置了`CC=arm-buildroot-linux-gnueabi-gcc`(32位编译器),但`ARCH=arm64`(64位架构),这导致编译器与目标架构不匹配。 - 尽管后面通过`CROSS_COMPILE`试图设置为64位工具链(`aarch64-buildroot-linux-gnu-`),但`CC`已经被`MAKE_FLAGS`设置为32位编译器,所以实际使用的是32位编译器编译64位内核模块,从而导致之前看到的`__uint128_t`错误(因为32位编译器不支持128位整数类型)。 ### 解决方案: 1. **修改`MAKE_FLAGS`的来源**:检查构建系统中定义`MAKE_FLAGS`的位置(可能在`rules.mk`或其他包含文件中),确保它不会错误地设置32位工具链。对于64位架构,应该使用64位工具链。 2. **在Makefile中覆盖`MAKE_FLAGS`**:在`Build/Compile`中,避免传递错误的`MAKE_FLAGS`。可以重新定义`MAKE_FLAGS`为空或覆盖其中的工具链变量。例如: ```makefile define Build/Compile $(MAKE) -C $(PKG_BUILD_DIR) compile ... \ AR=$(KERNEL_CROSS)ar \ AS="$(KERNEL_CROSS)gcc -c" \ CC=$(KERNEL_CROSS)gcc \ CXX=$(KERNEL_CROSS)g++ \ ... \ $(KERNEL_MAKE_FLAGS) \ CROSS_COMPILE=$(KERNEL_CROSS) \ EXTRA_CFLAGS="$(EXTRA_CFLAGS)" endef ``` 这样显式设置工具链变量,确保使用64位工具链。 3. **调整变量顺序**:将`CROSS_COMPILE`和显式工具链变量放在`$(MAKE_FLAGS)`和`$(KERNEL_MAKE_FLAGS)`之前,这样后面出现的变量赋值会覆盖前面的。例如: ```makefile define Build/Compile $(MAKE) -C $(PKG_BUILD_DIR) compile ... \ CROSS_COMPILE=$(KERNEL_CROSS) \ $(MAKE_FLAGS) \ $(KERNEL_MAKE_FLAGS) \ EXTRA_CFLAGS="$(EXTRA_CFLAGS)" endef ``` 注意:如果`MAKE_FLAGS`中设置了`CC`,那么即使`CROSS_COMPILE`在前面,也会被覆盖。所以这种方法不一定有效,需要确保`MAKE_FLAGS`中没有设置工具链变量。 ### 建议: 根据打印的`MAKE_FLAGS`,它包含32位工具链,这是一个明显的配置错误。最佳做法是修复构建系统中生成`MAKE_FLAGS`的部分,使其根据目标架构(`ARCH=arm64`)选择正确的工具链。如果无法修改全局构建系统,可以在包级别的Makefile中覆盖`MAKE_FLAGS`,例如: ```makefile ifeq ($(ARCH),aarch64) MAKE_FLAGS:= ... # 重新定义为64位工具链,或者置空 endif ``` 总之,问题根源是32位工具链被用于64位架构编译,导致不兼容。需要确保在编译64位内核模块时使用64位工具链。 §§相关问题§§ 1. 如何判断当前OpenWrt构建系统中的`MAKE_FLAGS`变量是在哪里定义的? 2. 在Makefile中,如何覆盖从上级包含文件(如`rules.mk`)中继承的变量? 3. 对于ARM64架构,正确的工具链前缀应该是什么?如何验证工具链是否支持目标架构? 4. 如果同时设置了`CC`变量和`CROSS_COMPILE`变量,哪个优先级更高? </think>### 回答: 根据您提供的 `echo MAKE_FLAGS` 输出,可以确认 **`MAKE_FLAGS` 确实覆盖了工具链设置**,具体分析如下: 1. **工具链冲突分析**: - `MAKE_FLAGS` 显式设置了 **32位工具链**(`arm-buildroot-linux-gnueabi-*`),包括: ```bash CC=arm-buildroot-linux-gnueabi-gcc CROSS=arm-buildroot-linux-gnueabi- ARCH=arm64 ``` - 但在 `Build/Compile` 中,您试图通过 `CROSS_COMPILE=$(KERNEL_CROSS)` 设置 **64位工具链**(`aarch64-buildroot-linux-gnu-`)。 - **问题根源**:由于 `$(MAKE_FLAGS)` 在命令行中**先出现**(如 `$(MAKE) ... $(MAKE_FLAGS) $(KERNEL_MAKE_FLAGS) CROSS_COMPILE=...`),它定义的 `CC` 和 `CROSS` 会覆盖后续的 `CROSS_COMPILE` 设置。 2. **具体覆盖证据**: - 在原始编译错误日志中可见: ``` CC="arm-buildroot-linux-gnueabi-gcc" # 32位工具链 CROSS="arm-buildroot-linux-gnueabi-" # 32位前缀 ARCH="arm64" # 64位架构 ``` 这导致 **32位编译器尝试编译64位内核模块**,引发 `__uint128_t` 等兼容性错误。 3. **解决方案**: - **方案1:调整变量顺序** 在 `Build/Compile` 中将 `CROSS_COMPILE` 移到最前面: ```makefile define Build/Compile $(MAKE) ... \ CROSS_COMPILE=$(KERNEL_CROSS) \ # 先设置64位工具链 $(MAKE_FLAGS) \ # 后执行其他标志 $(KERNEL_MAKE_FLAGS) \ ... endef ``` - **方案2:覆盖 `MAKE_FLAGS` 中的工具链** 在 Makefile 开头强制重新定义: ```makefile ifeq ($(ARCH),aarch64) MAKE_FLAGS := $(filter-out CC=% CROSS=%, $(MAKE_FLAGS)) # 移除原有设置 MAKE_FLAGS += CC=$(KERNEL_CROSS)gcc CROSS=$(KERNEL_CROSS) # 添加64位工具链 endif ``` - **方案3:直接传递正确工具链**(推荐) 显式覆盖关键变量: ```makefile $(MAKE) ... \ CC=$(KERNEL_CROSS)gcc \ CROSS=$(KERNEL_CROSS) \ ARCH=arm64 \ $(MAKE_FLAGS) \ # 其他通用标志保留 ... ``` ### 关键结论: 工具链冲突的直接原因是 **`MAKE_FLAGS` 中硬编码的32位工具链与64位架构不兼容**。通过调整变量顺序或显式覆盖工具链可解决此问题。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Roy_Allen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值