您当前的位置:首页 > 计算机 > 编程开发 > 编译原理

【Linux】交叉编译

时间:11-24来源:作者:点击数:
CDSY,CDSY.XYZ

前言

linux交叉编译学习过程中第一次遇到的玩意、学习前提悉知的知识点?

linux交叉编译实操——交叉编译wiringOP库

linux交叉编译wiringOP库学习过程中遇到的一些疑惑点?

linux交叉编译实操——交叉编译智能分类项目


1.aarch64-none-linux-gnu-gcc是什么东西?

aarch64-none-linux-gnu-gcc 是一个交叉编译工具链,用于在其他架构的系统(通常是 x86_64 架构的计算机)上编译 64 位 ARM 架构的程序 例如在 x86 架构的 PC 上编译出能运行在 ARM 架构的嵌入式设备上的程序。它基于 GCC(GNU Compiler Collection)编译器。

2.为什么叫交叉编译工具链?

2.1为什么叫工具链?

为什么叫工具链?在计算机科学中,“工具链”一词通常指的是一组按照特定顺序执行的工具,它们共同完成一个复杂的任务。

对于编译过程来说,通常包括以下几个步骤:

预处理:对源代码进行文本替换,例如包含头文件、宏展开等。

编译:将预处理后的代码编译为汇编语言或中间代码。

汇编:将汇编语言或中间代码转换为机器语言的目标文件。

链接:将目标文件和所需的库链接在一起,生成最终的可执行文件。

这些步骤通常由不同的工具来完成,例如编译器、汇编器、链接器等。当这些工具按照正确的顺序和配置组合在一起时,就形成了一个“工具链”

2.2为什么叫交叉编译?

它被称为“交叉编译工具链”是因为它涉及了两种不同的平台:编译平台(宿主机)和目标平台(目标机)。

开发者在宿主机上使用这个工具链编译代码,生成的程序可以在目标机上运行。这个过程就像是在两个不同的平台之间“交叉”工作,因此得名为“交叉编译工具链”。

一、交叉编译wiringOP库的配置过程解释(对照文档的实现过程)

主要讲述了如何使用交叉编译工具链来编译 wiringOP 库,并将其安装到目标设备上。下面我会用通俗的语言解释每一步的作用,并做出相应的解释。

1. 修改 build.sh 脚本

在 echo "WiringPi Library" 之前添加以下代码,用于创建一些目录:

mkdir $PWD/_INSTALL/usr/local/bin -p
mkdir $PWD/_INSTALL/usr/local/include -p
mkdir $PWD/_INSTALL/usr/local/lib -p

这些目录将用于存放编译后的库文件和头文件。

2. 修改 Makefile

修改 CC 变量

将所有 Makefile 中的 CC := gcc 改成 CC := aarch64-none-linux-gnu-gcc

这是因为我们使用交叉编译工具链来编译库文件,而不是直接在目标设备上编译。

修改 DESTDIR 变量

将 DESTDIR?=/usr 替换为 DESTDIR?= $(shell pwd)/../_INSTALL/usr

这样可以指定编译后的库文件和头文件的安装路径。

3. 修改 wiringPi/Makefile

修改库文件的符号链接,使其指向正确的路径。具体来说,就是将 $Q ln -sf $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION) $(DESTDIR)/lib/libwiringPi.so

修改为

$Q ln -sf $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libwiringPi.so

4. 修改 devLib/Makefile

同上,修改 devLib/Makefile 中的符号链接,使其指向正确的路径。

5. 修改 INCLUDE 变量

将 INCLUDE = -I. 修改为 INCLUDE = -I. -I$(DESTDIR)$(PREFIX)/include

以确保在编译时包含正确的头文件路径。

6. 执行编译命令

执行 ./build 命令来编译库文件。在选择板子时,输入 26,选择 orangepizero2

7. 检查生成的文件

在 wiringOP-master 目录下会生成一个 _INSTALL 目录,里面包含了编译好的库文件和头文件。

8. 将文件拷贝到目标设备

将 _INSTALL 目录中的内容拷贝到目标设备(香橙派)的根目录下。

9. 更新创建 符号链接(软链)

因为前面已经修改了几个文件的符号链接(软链),所以在目标设备(香橙派)上执行 sudo ldconfig 命令更新一下符号链接(软链)。

sudo ldconfig 是一个命令,用于更新系统的动态链接库缓存。在将编译好的库文件和头文件拷贝到香橙派的根目录后,需要运行这个命令来更新系统的动态链接库缓存(ldconfig 命令会扫描指定目录中的动态库文件,并将它们的信息添加到系统的动态链接库缓存中),这样,当其他程序需要使用这些库文件时,系统能够快速找到并加载它们。

10. 测试库是否链接正常

在目标设备上执行 sudo gpio readall 命令,以测试库是否链接正常。

10.1 为什么测试 wiringPi 库是否正确链接要用 sudo gpio readall 命令?

因为 gpio readall 命令就是 wiringPi 库提供的一个命令行工具,这个命令用来读取树莓派或其他单板计算机(香橙派)GPIO 引脚状态的,

那我们在安装和配置好 wiringPi 库后,运行 sudo gpio readall,如果命令能够正确输出每个引脚的状态,那么说明 wiringPi 库已经正确链接并能够正常工作。

通过以上步骤,我们就可以使用交叉编译工具链来编译 wiringOP 库,并将其安装到目标设备上,以便在目标设备上使用该库进行开发。

(一)疑问点

1. 为什么只修改这三个文件?wiringPi/Makefile 、devLib/Makefile、 INCLUDE 变量

在嵌入式开发中,像香橙派这样的单板计算机,需要构建特定的库来提供对硬件的控制。wiringPi 和 devLib 是两个常用的库。

总结
  • wiringPi 库提供了对 GPIO 引脚的简单易用的访问,适用于各种硬件控制任务。
  • devLib 库扩展了 wiringPi 的功能,提供了对特定设备或项目需求的支持。
  • INCLUDE 定义在 Makefile 文件中,用于指定编译时包含的头文件路径,以确保编译器找到所需的头文件。
  • 只构建这两个库是因为它们提供了项目所需的所有硬件访问和控制功能。
1.1 wiringPi 库

wiringPi 是一个用于树莓派的 GPIO(通用输入输出)访问库,也适用于其他类似的单板计算机,如香橙派。它提供了一组简单易用的函数,用于设置和读取 GPIO 引脚的状态,控制 LED、传感器和其他外设。

  • 作用wiringPi 库的主要作用是简化 GPIO 操作,使得开发者可以使用 API 进行硬件控制,而无需直接处理底层的硬件寄存器。这对于快速原型开发特别有用。
1.2 devLib 库

devLib 是一个与 wiringPi 相关的库,提供了额外的设备支持或特定的功能。它包含一些特定硬件或项目需求的自定义代码和函数。

  • 作用devLib 库的作用是扩展 wiringPi 的功能,提供对特定设备或应用场景的支持。它使得开发者可以轻松地集成自定义的硬件或实现特定的项目需求,而无需从头开始编写所有代码。
1.3 INCLUDE 变量

INCLUDE 变量是一个在 Makefile 文件中定义的变量,用于指定编译时包含的头文件路径。它通常在编译命令中使用,以确保编译器能够找到所需的头文件。

2. 为什么只构建这两个库?

  • wiringPi 库提供了对 GPIO 引脚的简单易用的访问,适用于各种硬件控制任务。
  • devLib 库扩展了 wiringPi 的功能,提供了对特定设备或项目需求的支持。
  • 只构建这两个库是因为它们提供了项目所需的所有硬件访问和控制功能。

3. 他们四个之间又是啥关系? build.sh、 wiringPi/Makefile 和 devLib/Makefile、 INCLUDE 变量

关系总结
  • build.sh 脚本调用 Makefile 文件中的规则来执行编译和构建任务。
  • devLib/Makefile 和 wiringPi/Makefile 包含了构建 wiringPi 和 devLib 库的 所需得Makefile规则构建文件。
  • INCLUDE 变量在 Makefile 文件中定义,用于指定编译时包含的头文件路径。
2.1 build.sh

build.sh 是一个 shell 脚本,用于自动化编译和构建库文件的过程。它会调用 Makefile 文件中的规则来执行具体的编译和构建任务。

2.2 devLib/Makefile 和 wiringPi/Makefile

devLib/Makefile 和 wiringPi/Makefile 是具体到 devLib 和 wiringPi 库的构建文件。它们是 Makefile 文件,包含了与 devLib 和 wiringPi 库相关的编译和构建规则。

2.3 INCLUDE 变量

INCLUDE 变量是一个在 Makefile 文件中定义的变量,用于指定编译时包含的头文件路径。它通常在编译命令中使用,以确保编译器能够找到所需的头文件。

4. INCLUDE = -I. -I$(DESTDIR)$(PREFIX)/include 是什么意思?

INCLUDE 变量用于指定编译时包含的头文件路径。

在 Makefile 中,-I 选项用于指定头文件的搜索路径。

因此,-I 加一个. 表示在当前目录中搜索头文件,

而 -I$(DESTDIR)$(PREFIX)/include 表示在由 DESTDIR 和 PREFIX 变量指定的目录中搜索头文件。

将 INCLUDE = -I. 修改为 INCLUDE = -I. -I$(DESTDIR)$(PREFIX)/include。这一步多添加了一个新的头文件搜索路径

5. 修改的路径是干嘛呢?

例如,将:
$Q ln -sf $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION) $(DESTDIR)/lib/libwiringPi.so
修改为:
$Q ln -sf $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libwiringPi.so

这个路径是我们创建出来的临时目录,在交叉编译项目的时候,临时放一些库文件和头文件。

6. $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION) 和 $(DESTDIR)$(PREFIX)/lib/libwiringPi.so 的区别

6. 为什么不直接写其中一条路径,而是要写两条路径,例如:

修改后是:
$Q ln -sf $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libwiringPi.so

为什么不直接写这一条:
 $Q ln -sf $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION)`
又或者只写这一条: 
 `$(DESTDIR)$(PREFIX)/lib/libwiringPi.so
 
而是要两条都写呢?
答:因为这是一条软链,其中一个路径是真正实际用到的库,另外一个只是名字,是个入口。
  • $Q:这是一个在 Makefile 中常用的变量,用于控制命令的输出。如果 $Q 被设置为空字符串,则命令将被执行并显示输出;如果 $Q 被设置为 @,则命令将被执行但不显示输出
  • ln -sf:这是 ln 命令的选项,用于创建符号链接。-s 表示创建符号链接,-f 表示强制覆盖已有的符号链接。
  • $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION) 是源文件,也就是实际存在的库文件。
  • $(DESTDIR)$(PREFIX)/lib/libwiringPi.so 就只是个名字。是入口指针,指向源文件。所以,在使用软链接后,libwiringPi.so 成为一个指向 libwiringPi.so.$(VERSION) 的符号链接。当程序在寻找 libwiringPi.so 库时,实际上会通过软链找到 libwiringPi.so.$(VERSION)库。在这个过程中,libwiringPi.so本身不会被程序使用到,它只是个名字,作为一个链接到libwiringPi.so.$(VERSION)库的入口点(符号链接)的存在。实际被使用的是 libwiringPi.so.$(VERSION)库文件,因为它包含了库的功能和代码。
那有什么作用呢?

libwiringPi.so 实际上就是一个方便的引用,我们在很多其它程序中,可以直接写libwiringPi.so 。然后libwiringPi.so又指向libwiringPi.so.$(VERSION),而libwiringPi.so.$(VERSION)中的$(VERSION)是一个版本变量,这个变量我们只需要修改它一次,就可以让所有调用过libwiringPi.so的程序,指向libwiringPi.so.$(VERSION),从而指向最新的版本或某个特定版本的库文件。

所以只要在程序中调用libwiringPi.so,它都是指向libwiringPi.so.$(VERSION)它总是指向最新或者我们需要的某个特定版本的库文件。应用程序可以通过这个软链接找到并使用正确版本库文件。

  • $(DESTDIR)$(PREFIX)/lib/libwiringPi.so.$(VERSION):这是一个指向特定版本库文件的路径。其中,$(VERSION) 是库文件的版本号,用于区分不同版本的库文件。这个路径通常用于在编译和运行时指定要使用的库文件版本。
  • $(DESTDIR)$(PREFIX)/lib/libwiringPi.so:这是一个指向最新版本库文件的符号链接。它不包含版本号,用于方便地更新和使用最新版本的库文件。在编译和运行时,通常使用这个符号链接来指定要使用的库文件。

二、交叉编译 —— 智能分类项目 工程代码

下面这部分描述了如何在Linux环境下的宿主机进行交叉编译,以便在目标设备(例如香橙派)上运行智能分类工程代码。下面我将用通俗易懂的语言解释这段话,帮助我们理解。

1. 原有项目的目录结构

原有项目的目录结构如下:

test@test:~/garbage$ tree
.
├── garbage.c
├── garbage.h
├── garbage.py
├── main.c
├── myoled.c
├── myoled.h
├── pwm.c
├── pwm.h
├── socket.c
├── socket.h
├── uartTool.c
└── uartTool.h

这里列出了项目中的所有文件,包括源代码文件(.c)、头文件(.h)和Python脚本文件(.py)。

2. 调整目录结构

为了更好地组织项目文件,我们将调整目录结构:

test@test:~/garbage$ tree -a
.
├── inc
│   ├── garbage.h
│   ├── myoled.h
│   ├── pwm.h
│   ├── socket.h
│   └── uartTool.h
├── src
│   ├── garbage.c
│   ├── garbage.py
│   ├── main.c
│   ├── myoled.c
│   ├── pwm.c
│   ├── socket.c
│   └── uartTool.c

我们创建了两个新的目录:

inc 用于存放头文件(这里放都是我们自己封装的头文件,就是上面那些.h结尾的,为了让我们的主程序main更加简洁,还有就是方便管理和开发程序),

src 用于存放源代码文件。这样可以使项目结构更清晰,便于管理和维护。

3. 增加3rd目录

我们需要增加一个名为3rd的目录(这个3代表第三方的意思),用于存放第三方依赖库和头文件,例如wiringOP库和python3.10库。

4. 先把编译好的wiringOP库复制到3rd目录下

wiringOP库 :

这里提到的wiringOP库就是(一、交叉编译wiringOP库的配置过程解释)这一节讲的编译的东西。

新增的宿主机的3rd目录就是用来存放编译wiringOP库出来的头文件和库文件。

因为我们已经编译过了,我们直接拷贝那一节(一、交叉编译wiringOP库的配置过程解释)讲的wiringOP库编译出来的头文件和库文件到宿主机的3rd目录下就好了。

当时编译完就放在了目录 wiringOP-master下的_INSTALL 中 。

①cd切换到该路径:

~$ cd test/wiringOP-master

~$ cd test/wiringOP-master

②cp复制 wiringOP-master 目录下的 _INSTALL 的全部文件到~/garbage/3rd 下:

~/test/wiringOP-master$ cp _INSTALL/* ~/garbage/3rd/ -af

~/test/wiringOP-master$ cp _INSTALL/* ~/garbage/3rd/ -af
5. 再下载其它所需的依赖库并解压到3rd目录下

①切换到香橙派系统的命令窗口,在香橙派的命令窗口使用apt download命令下载所需的依赖库包

apt download zlib1g zlib1g-dev libpython3.10 libpython3.10-dev  libexpat1 libexpat1-dev libcrypt1 libcrypt-dev

②从香橙派上,scp复制刚刚下载的所有.deb包到宿主机上(例如你的开发电脑)。

scp *.deb test@192.168.1.10:/home/test #test为宿主机用户名, 192.168.1.10为宿主机ip
## cp命令用于本地复制
## scp命令用于在本地系统和远程服务器之间复制,需要远程服务器的密码

③然后,切换到宿主机上(例如你的开发电脑),使用dpkg -x命令将这些.deb包文件解压到garbage/3rd目录下。

下面这 libpython3.10 压缩包解压下来里面是库文件, libpython3.10-dev带dev后缀的压缩包解压下来是这些库文件的头文件,我们是通过头文件来引用这些库的。

dpkg -x libpython3.10_3.10.12-1~22.04.2_arm64.deb garbage/3rd

dpkg -x libpython3.10-dev_3.10.12-1~22.04.2_arm64.deb garbage/3rd/

原始是这样的:

 dpkg -x libpython3.10_3.10.12-1~22.04.2_arm64.deb  garbage/3rd
 dpkg -x libpython3.10-dev_3.10.12-1~22.04.2_arm64.deb  garbage/3rd/
 dpkg -x libcrypt1_1%3a4.4.27-1_arm64.deb  garbage/3rd/
 dpkg -x libexpat1_2.4.7-1ubuntu0.2_arm64.deb  garbage/3rd/
 dpkg -x libpython3.10-dev_3.10.12-1~22.04.2_arm64.deb  garbage/3rd/
 dpkg -x libpython3.10_3.10.12-1~22.04.2_arm64.deb  garbage/3rd
 dpkg -x libexpat1_2.4.7-1ubuntu0.2_arm64.deb  garbage/3rd/
 dpkg -x libexpat1-dev_2.4.7-1ubuntu0.2_arm64.deb  garbage/3rd/
 dpkg -x libcrypt1_1%3a4.4.27-1_arm64.deb  garbage/3rd/
 dpkg -x zlib1g_1%3a1.2.11.dfsg-2ubuntu9.2_arm64.deb  garbage/3rd/
 dpkg -x libcrypt-dev_1%3a4.4.27-1_arm64.deb  garbage/3rd/
 dpkg -x zlib1g-dev_1%3a1.2.11.dfsg-2ubuntu9.2_arm64.deb  garbage/3rd/

下面是我把库文件和对应的库文件的头文件空行出来,发现有些重复:

 dpkg -x libpython3.10_3.10.12-1~22.04.2_arm64.deb  garbage/3rd
 dpkg -x libpython3.10-dev_3.10.12-1~22.04.2_arm64.deb  garbage/3rd/
 
 dpkg -x libcrypt1_1%3a4.4.27-1_arm64.deb  garbage/3rd/
 dpkg -x libcrypt1_1%3a4.4.27-1_arm64.deb  garbage/3rd/
 dpkg -x libcrypt-dev_1%3a4.4.27-1_arm64.deb  garbage/3rd/
 
 dpkg -x libpython3.10_3.10.12-1~22.04.2_arm64.deb  garbage/3rd
 dpkg -x libpython3.10-dev_3.10.12-1~22.04.2_arm64.deb  garbage/3rd/
 
 dpkg -x libexpat1_2.4.7-1ubuntu0.2_arm64.deb  garbage/3rd/
 dpkg -x libexpat1_2.4.7-1ubuntu0.2_arm64.deb  garbage/3rd/
 dpkg -x libexpat1-dev_2.4.7-1ubuntu0.2_arm64.deb  garbage/3rd/
 

 dpkg -x zlib1g_1%3a1.2.11.dfsg-2ubuntu9.2_arm64.deb  garbage/3rd/
 dpkg -x zlib1g-dev_1%3a1.2.11.dfsg-2ubuntu9.2_arm64.deb  garbage/3rd/
6. 安装合适的交叉编译工具

由于默认提供的交叉编译工具版本(aarch64-none-linux-gnu-gcc 9.2.0)如果去加载上面的依赖库,会出现库版本的依赖问题。 因此,宿主机安装(aarch64-linux-gnu-gc 11.2)版本并使用该交叉编译工具:

sudo apt intall  gcc-aarch64-linux-gnu
6.1问题分析

make运行时,发现报错,找到其中一条报错:前面有aarch64-none-linux-gnu相关字符,说明是编译器的问题,然后后面直接报错undefined reference to 'mknod@GLIBC_2.33

再找到另外十几条报错都是前面一样,后面都是直接报这个undefined reference to '(只是这里不一样,都是不同的字符)@GLIBC_2.33

所以报错都是前面一模一样,后面只有一个地方不一样,而且都是各种不同的变量或字符,说明就是编译器可能版本依赖问题,才导致编译的时候全部都是只有那个编译不了不同的变量出问题。

然后我们使用aarch64-none-linux-gnu --version命令查看一下宿主机这里的编译器版本,会发现是9.2.1版本,显示如果如下:

aarch64-none-linux-gnu-gcc (GCC) 9.2.1 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

因为之前在香橙派直接编译的时候是可以的,我们看看香橙派是什么版本的。

切换到香橙派命令窗口,使用dpkg -l | grep libc命令查找与libc相关的已安装软件包列出来,然后直接看到libc6:arm64的版本是2.35的,它的ibc的库是2.35版本的。而刚刚我们上面在(pc)宿主机报错是链接的2.33的libc。宿主机的libc版本是比较低的。

然后我们再用gcc --version命令看看香橙派默认的编译器版本,会发现是11.2.0版本,显示如果如下:

gcc(ubuntu 11.2.0-19ubuntu1)11.2.0 Copyright (C) 2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

所以我们用9.2.1版本的编译器去编译的时候,宿主机上的lib c的版本是比较低比较老的,所以在链接一些库的时候会链接失败。

6.2 解决

到软件源上看看官方有没有提供交叉编译工具,执行 apt-cache search gcc | grep aarch64,从APT数据库缓存中搜索所有描述中含有gcc关键字的软件包,并从中筛选出那些名称或描述中还含有aarch64字样的软件包(这可以找到适用于ARM 64位架构的GCC交叉编译器或其他相关工具)。

发现软件源里是有提供交叉编译工具的,输出里有条:gcc-aarch64-linux-gnu - GNU C compiler for the arm64 architecture,我们直接命令去安装这个软件源提供的交叉编译器:

sudo apt intall  gcc-aarch64-linux-gnu

安装完后,用命令再查一下这个交叉编译器的版本,会发现是11.4.0版本的,然后就make执行,发现是可以的。:

aarch64-linux-gnu-gcc --version
6.21 APT是什么?

APT是在Debian中使用的包管理系统。

6.22 软件源是什么?

软件源通常包括官方仓库和其他第三方仓库

7. Makefile文件内容(下面有个人解析版)

我们需要编写一个Makefile文件,用于配置编译选项和依赖关系。Makefile文件的内容如下:

CC := aarch64-linux-gnu-gcc
 SRC := $(shell find  src -name "*.c")
 INC := ./inc \
        ./3rd/usr/local/include  \
        ./3rd/usr/include \
        ./3rd/usr/include/python3.10 \
        ./3rd/usr/include/aarch64-linux-gnu/python3.10 \
        ./3rd/usr/include/aarch64-linux-gnu
 OBJ := $(subst src/,obj/,$(SRC:.c=.o))
 TARGET=obj/garbage
 CFLAGS := $(foreach item, $(INC),-I$(item))  # -I./inc -I./3rd/usr/local/include
 LIBS_PATH  :=  ./3rd/usr/local/lib \
                                ./3rd/lib/aarch64-linux-gnu \
                                ./3rd/usr/lib/aarch64-linux-gnu \
                                ./3rd/usr/lib/python3.10
 LDFLAGS := $(foreach item, $(LIBS_PATH),-L$(item)) # -L./3rd/usr/local/libs
 LIBS := -lwiringPi -lpython3.10 -pthread -lexpat -lz -lcrypt
 obj/%.o:src/%.c
        mkdir -p obj
        $(CC) -o $@ -c $< $(CFLAGS)
 $(TARGET) :$(OBJ)
        $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS)
 compile : $(TARGET)
 clean:
        rm $(TARGET) obj $(OBJ) -rf
 debug:
        echo $(CC)
        echo $(SRC)
        echo $(INC)
        echo $(OBJ)
        echo $(TARGET)
        echo $(CFLAGS)
        echo $(LDFLAGS)
        echo $(LIBS)
 .PHONY: clean compile debug

这个Makefile文件定义了编译器gcc、源文件SRC、头文件INC、目标文件OBJ、编译选项CFLAGS 和链接选项LDFLAGS、LIBS等;

规定了临时目标文件 OBJ、最终目标可执行文件TARGET的生成规则;

以及伪目标clean、compile、debug的执行规则。

8. 编译并运行

最后,我们在工程目录下执行make命令进行编译,会在garbage项目的obj目录下生成一堆.o文件和garbage可执行文件。然后scp命令将garbage可执行文件复制到香橙派:/home/orangepi目录上,就可以在香橙派上运行了:

:~/garbage/obj$ scp garbage orangepi@192.168.1.18:/home/orangepi #orangepi为宿主机用户名, 192.168.1.18为香橙派的ip

:~/garbage/obj$ scp garbage orangepi@192.168.1.18:/home/orangepi  #orangepi为宿主机用户名, 192.168.1.18为香橙派的ip

最后可能也要把garbage项目的src目录下的garbage.py也复制到香橙派上

:~/garbage/src$ scp garbage.py orangepi@192.168.1.18:/home/orangepi  #orangepi为宿主机用户名, 192.168.1.18为香橙派的ip

返回到香橙派上,用ls命令列出所有文件,看到garbage可执行文件,

使用命令执行garbage可执行文件,运行成功。

:~$ sudo -E ./garbage

sudo -E ./garbage

通过以上步骤,我们就可以在Linux环境下的宿主机进行交叉编译,并将智能分类的工程代码运行在目标设备(例如香橙派)上。

2.1 Makefile 文件内容完整解析:

#—— —— —— —— —— 【一、定义】—— —— —— —— ——
 CC := aarch64-linux-gnu-gcc  #定义编译器
 
 #———— 1.设置临时目标文件的名称。
 #在生成(最终目标文件 TARGET )前,
 #我们要生成(临时目标文件 OBJ ),这是生成目标文件所需要的依赖文件
 #这里先把(临时目标文件 OBJ )路径名字都定义好,真正的实现在下面的目标(主程序),有实现的规则。
 SRC := $(shell find  src -name "*.c")  #查找src所有.c文件名 (源文件列表)
 OBJ := $(subst src/,obj/,$(SRC:.c=.o)) #把刚刚的查找到的SRC所有的.c文件名替换为.o文件名,
 										#并将路径从src/替换成obj/ (把源文件列表转换为目标文件列表)
 
 #———— 2.设置最终生成的可执行文件的名称。
 #本项目中想要生成的(最终目标文件 TARGET )
 #这里也一样先把(最终目标文件 TARGET )路径名字都定义好,真正的实现在下面的目标(主程序),有实现的规则。
 TARGET=obj/garbage


 #———— 3.头文件的路径
 INC := ./inc \        #原始我们自己封装的头文件路径
        ./3rd/usr/local/include  \      #编译wiringOP库得到的库文件所在路径
        ./3rd/usr/include \             #这个包括下面这些都是步骤4解压出来的
        ./3rd/usr/include/python3.10 \
        ./3rd/usr/include/aarch64-linux-gnu/python3.10 \
        ./3rd/usr/include/aarch64-linux-gnu
 #设置编译器的选项 —— 头文件的路径,全部加个-I
 #在命令行中输入命令需要附加头文件路径时候要加-I
 CFLAGS := $(foreach item, $(INC),-I$(item))  # -I./inc -I./3rd/usr/local/include
 


 #———— 4.库文件的路径 (这个工程依赖的第三方头文件和库)
 #在香橙派直接编译智能垃圾桶代码的时候,因为这些库 -lwiringPi -lpython3.10 香橙派本来就有,
 #在香橙派系统的默认库路径下,所以直接在命令后面加具体的库就行 -lwiringPi -lpython3.10 ,
 #( gcc -o garbagetest garbagetest.c garbage.c garbage.h -I /usr/include/python3.10 -lwiringPi -l python3.10 )
 #但是在宿主机(PC X86)交叉编译项目的时候,这些库宿主机是没有的,
 #所以需要把用到的库,它们的路径都写出来加上-L ,提供给编译器。
 #在编译这些库的时候,可以让编辑器去这些库的路径下去找到这些库  
 # -L./3rd/usr/local/libs -L... -lwiringPi -lpython3.10 。
 LIBS_PATH  :=  ./3rd/usr/local/lib \    #编译wiringOP库得到的库文件路径
                ./3rd/lib/aarch64-linux-gnu \    #这个包括下面这些都是步骤4解压出来的
                ./3rd/usr/lib/aarch64-linux-gnu \
                ./3rd/usr/lib/python3.10                                
 #设置链接器的选项 —— 库文件的路径,全部加个-L
 #在命令行中输入命令需要附加库文件的路径时要加个-L
 LDFLAGS := $(foreach item, $(LIBS_PATH),-L$(item)) # -L./3rd/usr/local/libs
 
 #———— 5.设置需要用的具体的库 —— 链接器会从上面的库路径去找这几个库链接
 LIBS := -lwiringPi -lpython3.10 -pthread -lexpat -lz -lcrypt



#—— —— —— —— —— 【二、目标(主程序)真正生成文件的地方】—— —— —— —— —

 #规则 —— 从源文件生成目标文件(临时文件)
 # obj文件的生成规则(OBJ变量下所有叫obj的文件)
 obj/%.o:src/%.c
        mkdir -p obj
        $(CC) -o $@ -c $< $(CFLAGS)
        
 #规则 —— 从目标文件生成最终可执行文件(最终文件)
 $(TARGET) :$(OBJ)
        $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS)



#—— —— —— —— ——【三、伪目标】—— —— —— —— ——
 #伪目标 —— 编译项目。
 compile : $(TARGET)

 #伪目标 —— 清理项目。
 clean:
        rm $(TARGET) obj $(OBJ) -rf
 
 #伪目标 —— 调试,定义一个伪目标来打印变量的值以进行调试。
 debug:
        echo $(CC)
        echo $(SRC)
        echo $(INC)
        echo $(OBJ)
        echo $(TARGET)
        echo $(CFLAGS)
        echo $(LDFLAGS)
        echo $(LIBS)
        
#定义伪目标属性 —— 将上面的伪目标标记为伪目标,以防止Makefile将它们视为文件名。
 .PHONY: clean compile debug

2.2 在香橙派中使用 apt download 命令下载依赖库文件是从哪里下载?那这个软件包仓库是在哪里?

总结

在香橙派中使用apt download命令下载依赖库文件,apt download命令会从香橙派的软件包仓库中下载指定的依赖库文件,这些文件通常以.deb格式存在。这些软件包可以被系统包管理器(如 apt)用来安装、更新或删除软件。

当你使用apt download命令下载软件包时,这些软件包并不是存储在香橙派本身的内存或存储空间中,而是从互联网上的软件包仓库服务器获取。

详细

具体来说,软件包仓库通常是由香橙派所使用的Linux发行版维护者提供的一个远程服务器集合。例如,如果你的香橙派上安装的是Armbian或者Ubuntu等操作系统,那么软件包仓库会是CanonicalDebian项目或其他相关组织维护的服务器。这些服务器存放了大量的.deb格式的软件包文件,用户可以通过网络访问它们来下载和安装所需的软件。

在你的香橙派上,/etc/apt/sources.list文件以及 /etc/apt/sources.list.d/目录下的文件定义了哪些软件包仓库将被用来搜索和下载软件包。当你执行 apt update命令时,APT会读取这些配置文件,并且连接到指定的仓库更新本地的软件包索引。而当你使用apt downloadapt-get install等命令时,APT将会根据这个索引来查找并下载相应的.deb文件。

总结一下,.deb格式的依赖库文件并不直接存放在香橙派的内存或存储设备里,而是存放在由操作系统提供商或第三方维护的远程服务器上。香橙派通过网络与这些仓库进行通信,以完成软件包的下载和安装。

CDSY,CDSY.XYZ
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
上一篇:gcc编译过程简介 下一篇:很抱歉没有了
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐