【Android篇】MakeFile初识

简介: C语言中,我们使用visual studio开发软件时候,写程序开始时候都会创建一个project项目文件,然后在文件里面编译 .h 和 .c 的文件。在Linux中,有一个叫做make的指令,就相当于C语言的IDE,我们只需要敲make指令,make会去查看当前的Makefile或makefile(小写编译优先级更高)文件。不过我们创建的项目不叫project,而是称为Makefile,打开一个make源程序包,发现很多Makefile的文件,说明里面有很多的项目,并且Makefile管理着这些依赖关系。

MakeFile是什么


C语言中,我们使用visual studio开发软件时候,写程序开始时候都会创建一个project项目文件,然后在文件里面编译 .h 和 .c 的文件。

在Linux中,有一个叫做make的指令,就相当于C语言的IDE,我们只需要敲make指令,make会去查看当前的Makefile或makefile(小写编译优先级更高)文件。

不过我们创建的项目不叫project,而是称为Makefile,打开一个make源程序包,发现很多Makefile的文件,说明里面有很多的项目,并且Makefile管理着这些依赖关系。

在源程序包里面,也有名为makefile的文件(m是小写),两个命名同时存在,这是合理的,在开发一个项目的时候,工程师一般都会命名为Makefile然后打包交给用户,用户觉得某个Makefile需要改动,用户改动后或者新建后的项目定义为makefile,并且在运行时候,先执行makefile,再执行Makefile文件。



Makefile格式


Makefile中规则的格式通常为:

target: prerequisites
    recipe


其中,target代表要生成的文件(可以是一个文件或多个文件),prerequisites表示生成该文件所需要的依赖文件,recipe则是生成该文件的命令。

例如,如果要编译一个名为hello.c的C程序,则可以定义如下规则:

hello: hello.c
    gcc -o hello hello.c


其中,hello是目标文件,即要生成的可执行程序;hello.c是依赖文件,即要编译的源代码文件;gcc -o hello hello.c是用于生成目标文件的命令。

除了显式指定目标和依赖文件外,还可以使用一些内置变量来简化Makefile的编写。例如,下面是一个常见的Makefile示例:


CC=gcc
CFLAGS=-Wall -O2
hello: hello.c
    $(CC) $(CFLAGS) -o $@ $<


在这里,CCCFLAGS分别代表编译器和编译选项; $@表示目标文件名; $<表示第一个依赖文件名。通过这种方式定义变量和使用内置变量可以提高Makefile的可维护性和灵活性。



以下是本篇文章正文内容


一、gcc是什么?


gcc编译器是一个流行的开源编译器集合,它可以将高级语言代码(比如C、C++、Objective-C等)转换成二进制机器码,从而生成可执行文件

其主要用途包括:

1.编写和编译程序:gcc编译器常常被用来编写和编译各种程序,包括系统软件、应用程序、库等。

2.执行调试:通过gcc编译器输出的调试信息可以帮助程序员找到代码中潜在的错误并进行修复。

3.优化性能:gcc具有强大的优化功能,可以对代码进行多种优化处理以提高程序性能。

4.支持多种平台:gcc可以在不同的平台上运行,并支持多种硬件架构和操作系统。

当我们写好了C语言的代码之后,下一步就是gcc编译运行。

一个程序最终是为了生成一个可执行文件。

完整的流程:源程序,预处理,编译生成汇编,可执行文件

——————.c————.i—————.s————(自动执行)

总之,使用gcc编译器可以帮助开发者更快速地将代码转换成可执行的程序,并且提高代码质量和性能。

二、Makefile文件手写步骤(从易到难)


传统写法,进行编译生成可执行文件out,然后执行文件即可


gcc *.c (编译)

./a.out(执行)


为什么文件名是a,因为编译自动生成的是可执行文件默认为a.out,当然可以通过-o选项更改生成文件的名字


1.Makefile文件初始写法

3a7d33c7df4a4b1fbc67a2c62fdf6f58.jpg

退出编辑后,敲make指令进行编译生成可执行文件,然后执行文件即可。


2.第一次简化

# OBJS   代替  依赖文件
# CC     代替  gcc
# CFLAGS 代替  编译命令
OBJS=main.o tool1.o tool2.o
CC=gcc
CFLAGS=-c -Wall -g
mytool:$(OBJS)
  $(CC) $(OBJS) -o mytool
main.o:main.c
  $(CC) main.c $(CFLAGS)  -o main.o
tool1.o:tool1.c
  $(CC) tool1.c $(CFLAGS) -o tool1.o
tool2.o:tool2.c
  $(CC) tool2.c $(CFLAGS) -o tool2.o
clean:
  rm *.o mytool -rf


如果想要用clean指令,则敲指令


make clean


便可递归清除掉可执行文件。


3.第二次简化

# $^ 代替 上面的指令
# RM 代替 rm -f
# $@ 代替 目标文件
OBJS=main.o tool1.o tool2.o
CC=gcc
CFLAGS=-c -Wall -g
mytool:$(OBJS)
  $(CC) $^ -o mytool
main.o:main.c
  $(CC) $^ $(CFLAGS)  -o $@
tool1.o:tool1.c
  $(CC) $^ $(CFLAGS) -o $@
tool2.o:tool2.c
  $(CC) $^ $(CFLAGS) -o $@
clean:
  $(RM) *.o mytool -r


4.第三次简化

# 6-11行代码相似性很强,可以提取出一个公式模版
#      %.o:%.c
#        $(CC) $^ $(CFLAGS)  -o $@
# 百分号相当于一个通配符
OBJS=main.o tool1.o tool2.o
CC=gcc
CFLAGS=-c -Wall -g
mytool:$(OBJS)
  $(CC) $^ -o mytool
%.o:%.c
  $(CC) $^ $(CFLAGS)  -o $@
clean:
  $(RM) *.o mytool -r


这是一个Makefile规则中的命令,其中包含了一些变量:

$(CC):代表编译器的名称,例如gcc或clang。

$^:代表所有依赖文件的列表,以空格分隔。

-o:表示指定输出文件名的选项。

$@:代表目标文件的名称。

-$(RM):代表rm -f。

因此,这个命令表示将所有依赖文件编译链接为一个可执行文件,并将其命名为目标文件。例如,如果有一个名为test.c的源代码文件作为依赖项,那么这个命令可能会被展开为类似于以下内容:

gcc test.c -o target

其中target是Makefile中定义的目标文件名。



疑问


1.gcc main.c -c -o main.o这句话是什么意思


这句话是在使用GCC编译器将C语言源代码文件main.c编译成目标文件main.o的命令。具体解释如下:

gcc是GNU Compiler Collection(GNU编译器集合)的缩写,是一种流行的开源编译器。

main.c是指要编译的C语言源代码文件,通常以.c为后缀。

-c选项表示只进行编译而不进行链接操作,生成目标文件。

-o main.o选项表示将目标文件输出到名为main.o的文件中。

因此,这个命令会将main.c编译成一个名为main.o的目标文件,可以在链接时和其他目标文件一起使用。

2.gcc main.c -c -Wall -g -o main.o这句话是什么意思


这句话是在使用GCC编译器将C语言源代码文件main.c编译成目标文件main.o的命令,并开启了-warnings(警告)、-debugging(调试)等选项。具体解释如下:

gcc是GNU Compiler Collection(GNU编译器集合)的缩写,是一种流行的开源编译器。

main.c是指要编译的C语言源代码文件,通常以.c为后缀。

-c选项表示只进行编译而不进行链接操作,生成目标文件。

-Wall选项表示开启所有警告信息输出。

-g选项表示在生成目标文件时添加调试信息,方便调试程序。

-o main.o选项表示将目标文件输出到名为main.o的文件中。

因此,这个命令会将main.c编译成一个名为main.o的目标文件,并开启了警告输出和调试信息。该目标文件可以在链接时和其他目标文件一起使用。

3.目标文件和可执行文件的区别


目标文件(Object file)是源代码编译后生成的文件,包含了链接器所需要的程序段和数据段等信息,但是还没有完全生成可执行文件。

可执行文件(Executable file)是目标文件最终经过链接器处理后的结果,它包含了完整的可执行二进制代码,可以直接在操作系统上运行。

因此,目标文件和可执行文件的主要区别在于是否具有可执行性。目标文件只包含了一部分可执行代码和数据,并不能直接运行;而可执行文件则包含了全部需要运行程序的信息,可以直接被操作系统加载并执行。

在软件开发过程中,通常先将源代码编译成目标文件,然后将多个目标文件链接成一个可执行程序。这种分阶段处理可以提高开发效率、便于调试和管理。


第一步,生成目标文件

gcc -c HelloWord.c 
# _这个HelloWord.c是目标文件,不是可执行文件,因为这里用到了-c,
# 告诉gcc到汇编为止,不要进行链接。
# _链接就是将目标文件、启动代码、库文件链接成可执行文件的过程,
# 这个文件可被加载或拷贝到存储器执行。
# _会生成一个HelloWord.o的文件

第二步,生成可执行文件

gcc HelloWord.o
# 生成一个默认名为a.out的可执行文件。


如果不想名字为a,想变成wujige的话,那就加 -o选项。


gcc HelloWord.o -o  wujige
# 生成一个名字为wujige的可执行文件


4.目标文件和可执行文件的文件后缀名分别是什么


目标文件的通用后缀名是.o.obj,具体取决于编译器和平台。

可执行文件的通用后缀名是.exe(Windows系统)或无后缀名(Unix/Linux系统)。但是,不同的操作系统和平台可能有不同的可执行文件后缀名。

例如,Mac OS的可执行文件后缀名为.app,在Linux中,也有常见的可执行文件格式为ELF(Executable and Linkable Format),其后缀名为.elf.bin等等。

但是但是!为什么上面的可执行文件的后缀名是.out呢?

原来,可执行文件的后缀名是由操作系统或编译器决定的,并且不同的操作系统和编译器可能会采用不同的命名规则。在大多数情况下,可执行文件的后缀名为.exe或者没有后缀名。

但是,在一些特殊情况下,可能会使用.out作为可执行文件的后缀名,例如在Unix/Linux系统中,使用C语言编写程序并进行编译时,生成的默认可执行文件名称通常是a.out。这个.out文件就是可以直接运行的二进制可执行文件。但需要注意的是,这种情况并不是所有Unix/Linux系统都适用。

5.安卓的Makefile文件包含什么?


在安卓的makefile文件中,通常会包含多种语言的文件,其中最常见的语言文件包括:

1.Java文件:这些文件是编写Android应用程序的主要语言,通常使用Android Studio或Eclipse等开发环境进行编写。

2.C/C++文件:在某些情况下,开发者可能需要使用C或C++编写一些核心功能或高性能组件,这些文件将被编译成本地代码并与Java代码一起构建应用程序。

3.XML文件:这些文件定义了Android应用程序中使用的布局、视图和数据格式等相关内容。

4.资源文件:安卓应用程序通常包含各种资源,比如图像、音频、视频等,在makefile中需要指定这些资源所在的路径和格式。

5.翻译文件(strings.xml):为了支持多语言环境,在安卓应用程序中通常会有多个翻译版本。翻译文本保存在strings.xml文件中,并需确保在makefile中正确处理它们。

总之,在安卓应用程序开发过程中,makefile需要处理多种类型的语言和资源格式。


6.安卓的Makefile文件包含go和Python文件吗?


在安卓的makefile文件中,一般情况下不会包含Go语言或Python语言的源代码文件。

因为Go语言和Python语言并不是Android系统原生支持的编程语言之一,所以需要借助额外的工具或库才能在Android系统上运行。

如果开发者想要在Android应用程序中使用Go或Python编写的模块或组件,通常需要:

1.将这些模块或组件打包成库文件(比如.so文件)的方式,然后在Java层面调用它们,在makefile中也需要指定这些库文件的路径和格式。

2.另外,在某些特殊场景下,开发者也可以通过Android NDK来直接编写C/C++代码,在代码中调用Go或Python代码运行所需的逻辑。

但第二种方式需要对底层的系统使用和内存管理有比较深入的了解,并且可能会增加应用程序体积和复杂度。

总结


以上就是今天我总结的内容,本文仅仅简单介绍了makefile的使用以及我自己的一些问题,详细看下方up主解析makefile。


如何编写Makefile文件

目录
相关文章
|
2月前
Android.mk(makefile)中几个符号的区别:=、 :=、 ?=、 +=
本文解释了在Android.mk文件中使用的几种赋值符号的区别,包括`=`(基本赋值)、`:=`(覆盖赋值)、`?=`(条件赋值,仅在变量未赋值时操作)、`+=`(追加赋值),并通过实验演示了这些符号的具体行为和效果。
87 1
|
5月前
|
Android开发
Android Makefile中inherit-product函数和include的区别
Android Makefile中inherit-product函数和include的区别
100 0
|
11月前
|
Android开发
[√]android makefile
[√]android makefile
84 0
|
Shell 编译器 C语言
【Android构建篇】MakeFile语法&lt; 1 &gt;
对于一个看不懂Makefile构建文件规则的人来说,这个Makefile语法和shell语法是真不一样,但是又引用了部分shell语法,可以说是shell语法的子类,Makefile语法继承了它。 和shell语法不一样,这个更难一点,而且不太容易懂,所以后续还会持续更新这篇文章。
153 0
【Android构建篇】MakeFile语法&lt; 1 &gt;
|
Android开发
【Android构建篇】MakeFile语法中 := 和 = 的区别
使用:= 在Makefile中使用 := 赋值方式定义变量时,如果该变量定义中包含了其他变量的引用,那么这些引用所表示的值将在变量定义时即被展开。也就是说,子变量会被立即展开,并赋值给父变量。
112 0
|
Shell C语言 Android开发
【Android构建篇】MakeFile语法&lt; 3 &gt;
对于一个看不懂Makefile构建文件规则的人来说,这个Makefile语法和shell语法是真不一样,但是又引用了部分shell语法,可以说是shell语法的子类,Makefile语法继承了它。 和shell语法不一样,这个更难一点,而且不太容易懂,所以后续还会持续更新这篇文章。
102 0
|
存储 Shell 编译器
【Android构建篇】MakeFile语法&lt; 2 &gt;
对于一个看不懂Makefile构建文件规则的人来说,这个Makefile语法和shell语法是真不一样,但是又引用了部分shell语法,可以说是shell语法的子类,Makefile语法继承了它。 和shell语法不一样,这个更难一点,而且不太容易懂,所以后续还会持续更新这篇文章。
143 0
|
Android开发
【Android 逆向】Android 进程注入工具开发 ( Visual Studio 开发 Android NDK 应用 | 使用 Makefile 构建 Android 平台 NDK 应用 )(二)
【Android 逆向】Android 进程注入工具开发 ( Visual Studio 开发 Android NDK 应用 | 使用 Makefile 构建 Android 平台 NDK 应用 )(二)
172 0
【Android 逆向】Android 进程注入工具开发 ( Visual Studio 开发 Android NDK 应用 | 使用 Makefile 构建 Android 平台 NDK 应用 )(二)
|
编译器 Android开发 C++
【Android 逆向】Android 进程注入工具开发 ( Visual Studio 开发 Android NDK 应用 | 使用 Makefile 构建 Android 平台 NDK 应用 )(一)
【Android 逆向】Android 进程注入工具开发 ( Visual Studio 开发 Android NDK 应用 | 使用 Makefile 构建 Android 平台 NDK 应用 )(一)
151 0
【Android 逆向】Android 进程注入工具开发 ( Visual Studio 开发 Android NDK 应用 | 使用 Makefile 构建 Android 平台 NDK 应用 )(一)
|
Java 编译器 Linux
【CMake】CMake 引入 ( Android Studio 创建 Native C++ 工程 | C/C++ 源码编译过程 | Makefile 工具 | CMake 引入 )(二)
【CMake】CMake 引入 ( Android Studio 创建 Native C++ 工程 | C/C++ 源码编译过程 | Makefile 工具 | CMake 引入 )(二)
307 0
【CMake】CMake 引入 ( Android Studio 创建 Native C++ 工程 | C/C++ 源码编译过程 | Makefile 工具 | CMake 引入 )(二)