目录
前言
内容是有的,但是你可能感觉我是在吐槽!
也许不该吐,因为我是站在菜鸟的立场的,而原作是站在培养大神的立场的。
正文
能看这篇文章的都应该或多或少的知道make吧!
初级用法:
在代码共同的文件夹下面新建一个Makefile文件,然后输入CFLAGS=-Wall -g,保存。编译ex22.c,就make ex22吧!然后就能发现一个文件大部分bug。
#Makefile
CFLAGS=—Wall -g
高级用法:
CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)
LIBS=-ldl $(OPTLIBS)
PREFIX?=/usr/local
SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))
PROGRAMS_SRC=$(wildcard bin/*.c)
PROGRAMS=$(patsubst %.c,%,$(PROGRAMS_SRC))
TARGET=build/libex29.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))
#The Target Build
all: $(TARGET) $(SO_TARGET) tests $(PROGRAMS)
dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all
$(TESTS): $(TARGET) $(SO_TARGET)
$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
ar rcs $@ $(OBJECTS)
ranlib $@
$(SO_TARGET): $(TARGET) $(OBJECTS)
$(CC) -shared -o $@ $(OBJECTS)
build:
@mkdir -p build
@mkdir -p bin
#THE UNIT TESTS
.PHONY: tests
tests: CFLAGS += $(TARGET)
tests: $(TESTS)
@sh ./tests/runtests.sh
print:
@echo($(CFLAGS))
#The Cleaner
clean:
rm -rf build $(OBJECTS) $(TESTS)
rm -f tests/tests.log |rm -f tests/tests.out
find . -name "*.gc*" -exec rm {} \;
rm -rf 'find . -name "*.dSYM" -print'
#the install
install: all
install -d $(DESTDIR)/$(PREFIX)/lib/
install $(TARGET) $(SESTDIR)/$(PREFIX)/lib/
#the checke
check:
@echo Files with potentially dangerous functions.
@egrep '[^_.>a-zA-Z0-9] (str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)' $(SOURCES) || true
如何?感觉到从普通难度升级为深渊了吗?第一次面对时总感觉有一句 “马买皮”一直憋在心口。
如果你也觉得如看天书,那就试试下面的绝招吧!
拉伸区磨灭大法:
让困难处在“跳一跳就能够的着”,稍微努努力就能解决的地步,这样就能让我们坚持前进的脚步!
Makefile定义:
首先让我们了解下makefile是什么有什么用吧?
Makefile是一个文本文件,它包含了用于构建软件项目的规则和命令。Makefile通常用于C/C++项目,但也可以用于其他编程语言。
Makefile的主要目的是定义项目的编译规则和依赖关系,使得当源代码发生变化时,只需要重新编译发生变化的文件,而不需要重新编译整个项目。这样可以提高构建的效率。
Makefile通常包含以下内容:
-
变量定义:用于存储编译器参数、库文件路径、安装目录等常用值,以方便在后续的规则中使用。
-
目标定义:每个目标对应一个构建任务,可以是编译源文件、链接可执行文件、生成静态库或共享库等。
-
依赖关系定义:定义每个目标的依赖关系,即该目标所依赖的其他目标或文件。当某个依赖发生变化时,该目标会重新构建。
-
规则定义:每个目标都有对应的构建规则,规定了如何通过命令行来构建目标。
通过运行make
命令,Makefile会按照定义的规则和依赖关系来构建项目。Make工具会自动分析文件的依赖关系,根据文件的修改时间来决定是否需要重新构建。在构建过程中,Make工具会执行指定的命令,如编译源文件、链接可执行文件、生成静态库或共享库等。
Makefile的优点是可以自动化构建过程,减少手动操作的繁琐程度,并提高构建的效率。同时,Makefile也具有可移植性,可以在不同的平台上使用相同的Makefile来构建项目。
所以说它是一个自动化构建项目工具,平台适应性强!
高级用法注释:
CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)
#编译器参数,通常包括调试选项、优化选项、警告选项等。
LIBS=-ldl $(OPTLIBS) #链接时需要的库文件
PREFIX?=/usr/local #是安装目录的前缀,默认为/usr/local
SOURCES=$(wildcard src/**/*.c src/*.c)#匹配所有的源文件
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))#将源文件的扩展名.c替换为.o,批量获得编译后的目标文件名称
TEST_SRC=$(wildcard tests/*_tests.c)#匹配所有的测试源文件
TESTS=$(patsubst %.c,%,$(TEST_SRC))#通过替换c为空,获得测试的可执行文件名称
PROGRAMS_SRC=$(wildcard bin/*.c)
PROGRAMS=$(patsubst %.c,%,$(PROGRAMS_SRC))
TARGET=build/libex29.a#静态库的名称就是为了方便修改目标,增加效率
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))#动态库的名称
#The Target Build下面是目标与依赖关系,正常人们互相之间的关系也不过如此吧!社畜表示很恐!
all: $(TARGET) $(SO_TARGET) tests $(PROGRAMS)
#目标all及依赖。所谓依赖不是命令,感觉就像抬轿子一样,四个人抬着all,all依赖四个人,四个人还要依赖他们的胳膊。如果只是其中一个人左胳膊酸了,他换右胳膊就行,别人不动。
dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all
#dev将环境变量改为调试模式重新构建all,同时说明依赖未必是在下面。
$(TESTS): $(TARGET) $(SO_TARGET)
$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
ar rcs $@ $(OBJECTS) #ar创建一个静态库
ranlib $@ #创建静态库的索引也就是一种指针,让系统能通过这个导入到程序中
$(SO_TARGET): $(TARGET) $(OBJECTS)
$(CC) -shared -o $@ $(OBJECTS)
#开始晕了,它为什么要依赖后面的两个呢?
#哦,它首先要有一个合适的环境间,其次需要那么多的中间文件,才能开始构建动态库
build:
@mkdir -p build
@mkdir -p bin
#THE UNIT TESTS
.PHONY: tests
tests: CFLAGS += $(TARGET)
tests: $(TESTS)
@sh ./tests/runtests.sh
print:
@echo($(CFLAGS))
#The Cleaner
clean:
rm -rf build $(OBJECTS) $(TESTS)
rm -f tests/tests.log |rm -f tests/tests.out
find . -name "*.gc*" -exec rm {} \;
rm -rf 'find . -name "*.dSYM" -print'
#the install
install: all
install -d $(DESTDIR)/$(PREFIX)/lib/
install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/
#the checke
check:
@echo Files with potentially dangerous functions.
@egrep '[^_.>a-zA-Z0-9] (str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)' $(SOURCES) || true
总结一下,以后要写依赖最好备注一下用途,不然最后很容易晕,就像踢皮球一样,踢到最后,究竟皮球最终在哪里了。哦,进了! 进了就好啊,进了就好,庆祝吧,欢呼吧,球进了!
关键词解释:
CFLAGS:
CFLAGS
是一个变量,用于指定编译器的编译选项。在Makefile中,CFLAGS
通常被用于设置一些通用的编译选项,如调试选项、优化级别、警告级别等
-g
:生成包含调试符号的可执行文件,方便调试。-O2
:进行优化,提高程序的执行效率。-Wall
:打开警告信息,会显示一些潜在的问题或不规范的代码。-Wextra
:打开额外的警告信息,可以检测更多的问题。-Isrc
:指定头文件搜索的路径,将src
目录添加到头文件搜索路径中。-rdynamic
:将所有符号(包括未使用的)添加到动态符号表中,方便后续动态链接。-DNDEBUG
:定义一个宏NDEBUG
,用于屏蔽一些调试相关的代码。如assert与dbg这些代码的输出- -fpic:
用于生成与位置无关的代码(Position Independent Code,PIC)。PIC是一种可以在内存中加载并重定位的代码,它可以在不同的进程之间共享,并且可以在内存地址改变时仍然可以正常工作。在动态链接库和共享对象中使用PIC是很常见的。
通过在编译目标文件时使用
-fPIC
选项,生成的目标文件可以用于构建动态链接库(.so
文件)。这样,通过链接这些目标文件,我们可以生成可在不同进程中加载和共享的共享对象。
OPTFLAGS与OPTLIBS:
OPTLIBS
和OPTFLAGS
是变量,用于指定编译或链接时的额外选项。OPTLIBS
通常用于指定需要链接的额外库文件,OPTFLAGS
通常用于指定编译器的额外选项。通过在命令行中设置这些变量,可以为编译或链接过程提供额外的选项。
PREFIX:
DESTDIR
是一个变量,用于指定安装目标文件的根目录。在执行make install
命令时,可以通过设置DESTDIR
变量来改变安装路径
.PHONY:
.PHONY
是一个特殊的目标标记。它告诉Make工具,目标不是一个真正的文件,而是一个虚拟的目标。在Makefile中,.PHONY
通常被用于定义一些不产生文件输出的目标,如clean
、install
、test
等。指定目标为.PHONY
可以避免与同名文件产生冲突,并确保目标被正确执行。
我倔脾气犯了,如果不用此标记会怎么样?
如果不用.PHONY
特殊标记,Make工具会默认将目标视为一个待生成的文件。这意味着如果存在一个与目标同名的实际文件,并且该文件的创建时间比目标要晚,Make工具将不会执行该目标对应的命令。
这可能会导致一些问题,特别是对于一些常用的目标,如clean
。如果没有使用.PHONY
标记定义clean
目标,当存在一个名为clean
的实际文件时,make clean
命令将不会执行清理操作,而是认为目标已经是最新的。
因此,使用.PHONY
标记可以确保目标被正确执行,而不依赖于同名文件的存在与否。它告诉Make工具这个目标不是一个实际的文件,需要始终执行对应的命令。这样可以避免一些潜在的问题,确保Makefile的正确性和可靠性。
前面的点号不可省略。
DESTDIR:
DESTDIR
是一个变量,用于指定安装目标文件的根目录。在执行make install
命令时,可以通过设置DESTDIR
变量来改变安装路径
wildcard:
wildcard
是Makefile中的一个函数,用于搜索指定模式的文件或目录。它的语法是$(wildcard pattern)
,其中pattern
是要搜索的文件或目录的模式。
在Makefile中,wildcard
通常用于获取文件列表,以方便后续的构建规则或操作。它可以帮助自动识别符合指定模式的文件,并将它们作为一个列表返回。
patsubst:
patsubst也
是Makefile中的一个函数,用于对字符串进行模式替换。它的语法是$(patsubst pattern,replacement,text)
,其中pattern
指定要被替换的模式,replacement
指定替换后的字符串,text
是要进行替换操作的字符串。
在Makefile中,patsubst
通常用于对文件名进行模式匹配和替换,以方便后续的构建规则或操作。它可以帮助自动化文件名的生成或转换。
install:
它不是预定义变量也不是预定义的函数,为啥还要单独介绍呢?因为未知,所以对于它的使用很好奇,了解下吧!
在给定的Makefile中,install
是一个目标,用于安装生成的目标文件。以下是install
目标的内容:
install: all
install -d $(DESTDIR)/$(PREFIX)/lib/
install $(TARGET) $(SESTDIR)/$(PREFIX)/lib/
install
目标的执行过程如下:
- 首先,
install
目标依赖于all
目标。这意味着在执行install
目标之前,需要先执行all
目标,以生成目标文件。 install
目标使用install
命令来安装生成的目标文件。具体地,它使用-d
选项创建目标目录的父目录,并使用$(DESTDIR)
和$(PREFIX)
变量指定目标目录的位置。然后,使用install
命令将生成的目标文件安装到目标目录中。
通过运行make install
命令,Make工具将自动执行install
目标,并将生成的目标文件安装到指定的目录中。
我现在一般生成可执行文件,只要输入“./ex15”就能执行程序了,所以不存在安装软件的感觉,所以为什么要安装,什么时候用到,安装的好处有哪些呢?
在这个Makefile中,install
目标的目的是将生成的目标文件安装到指定的目录中。安装的目的是为了将软件或程序的可执行文件、库文件或其他相关文件复制到系统的标准位置,以便可以方便地访问和使用这些文件。
安装软件的好处包括:
- 方便使用:安装软件后,可以通过系统的命令行或其他方式轻松地启动和运行软件。
- 标准化:将软件安装到系统的标准位置,符合系统管理的最佳实践和约定,使软件在不同系统上的安装和升级更加统一和可靠。
- 系统集成:通过将软件的库文件安装到系统的标准库目录中,其他程序可以使用和链接到这些库,实现更好的系统集成和共享资源的效果。
- 依赖管理:在安装过程中,可以检查和解决软件所依赖的其他库和组件,确保软件能够在目标系统上正常运行。
总之,安装软件可以使软件在系统中更加方便地使用和管理,提高软件的可用性和可靠性。
所以说,把install放到最后也表示这是一场大戏吧,压轴的。下面就是刷副本了吧?依靠make与shell探索数据结构和算法,进而可以搞项目解决问题!好期待啊!
后语
也许这是最后一篇此系列的学习笔记了,因为我深度学习的目的达到了。看到此篇文章的你们如果在自学C的路上,浮躁总想不求甚解。可以试试边学便写笔记的方法。