gcc与makefile

前言

自接触C语言以来,小demo直接gcc,大项目的Makefile都是框架里自带的,一般都是按需修修补补,具体的规则总是摸不清搞不懂!当自己搭个小项目,手搓一个Makefile真是费劲,根本写不出来,因此书此博客,以后来ctrl+c
本文不会详细展开如何编写一个Makefile。如想了解种种细节,请参考下面这个非常详细的教程,包含几乎GNU make的Makefile的所有细节:

跟我一起写Makefile

而本文包含以下内容:

  • makefile小模板
  • gcc指令

Makefile小模板

适用于纯 C 语言

# 指令编译器和选项
CC=gcc
CFLAGS=-Wall -std=gnu99
 
# 目标文件
TARGET=main
SRCS = main1.c \
   			main2.c  \
   			main3.c  
INC = -I./
OBJS = $(SRCS:.c=.o)

$(TARGET):$(OBJS)
	$(CC) -o $@ $^
 
clean:
	rm -rf $(TARGET) $(OBJS)
 
%.o:%.c
	$(CC) $(CFLAGS) $(INC) -o $@ -c $<

注意:Makefile有个规则就是命令行是以tab键开头,4个空格或其他则会报错:
Makefile:2: *** missing separator。stop

  • 相比于单个文件和多个文件的makefile,通过变量INC制定了头文件路径。头文件路径之间通过空格隔开。
  • 编译规则%.o:%.c中加入了头文件参数$(CC) $(CFLAGS) $(INC) -o $@ -c $<
  • 单个文件和多个文件的makefile相比增加了头文件路径参数。
  • SRCS变量中,文件较多时可通过“\”符号续行。
  • $@ --代表目标文件
  • $^ --代表所有的依赖文件
  • $< --代表第一个依赖文件(最左边的那个)。

适用于 C/C++ 混合编译

目录结构如下:

httpserver
│   main.cpp
│   Makefile  
└─────inc
│      │   mongoose.h
│      │   http_server.h
│   
──────src
│       │   http_server.cpp
│       │   mongoose.c
│       │   ...

Makefile 如下:

CC=gcc
CXX=g++

# 编译器在编译时的参数设置,包含头文件路径设置
CFLAGS:=-Wall -O2 -g 
CFLAGS+=-I $(shell pwd)/inc
CXXFLAGS:=-Wall -O2 -g -std=c++11
CXXFLAGS+=-I $(shell pwd)/inc

# 库文件添加
LDFLAGS:=
LDFLAGS+=

# 指定源程序存放位置
SRCDIRS:=.
SRCDIRS+=src

# 设置程序中使用文件类型
SRCEXTS:=.c .cpp

# 设置运行程序名
PROGRAM:=httpserver

SOURCES=$(foreach d,$(SRCDIRS),$(wildcard $(addprefix $(d)/*,$(SRCEXTS))))
OBJS=$(foreach x,$(SRCEXTS),$(patsubst %$(x),%.o,$(filter %$(x),$(SOURCES))))

.PHONY: all clean distclean install

%.o: %.c
	$(CC) -c $(CFLAGS) -o $@ $<
	
%.o: %.cxx
	$(CXX) -c $(CXXFLAGS) -o $@ $<


$(PROGRAM): $(OBJS)
ifeq ($(strip $(SRCEXTS)),.c)
	$(CC) -o $(PROGRAM) $(OBJS) $(LDFLAGS)
else
	$(CXX) -o $(PROGRAM) $(OBJS) $(LDFLAGS)
endif


install:
	install -m 755 -D -p $(PROGRAM) ./bin

clean:
	rm -f $(shell find -name "*.o")
	rm -f $(PROGRAM)

distclean:
	rm -f $(shell find -name "*.o")
	rm -f $(shell find -name "*.d")
	rm -f $(PROGRAM)

all:
	@echo $(OBJS)

gcc指令

一步到位

gcc main.c -o main

多个程序文件的编译

gcc main1.c main2.c -o main

预处理

gcc -E main.c -o main.i

gcc -E main.c
gcc的-E选项,可以让编译器在预处理后停止,并输出预处理结果。

编译为汇编代码

预处理之后,可直接对生成的test.i文件编译,生成汇编代码:
gcc -S main.i -o main.s
gcc的-S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。

汇编

对于上文中生成的汇编代码文件test.s,gas汇编器负责将其编译为目标文件,如下:
gcc -c main.s -o main.o

连接

gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。
对于上一小节中生成的main.o,将其与C标准输入输出库进行连接,最终生成可执行程序main。

检错

参数-Wall,使用它能够使GCC产生尽可能多的警告信息。
gcc -Wall main.c -o main
在编译程序时带上-Werror选项,那么GCC会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改,如下:
gcc -Werrormain.c -o main

创建动态链接库

生成生成o文件
gcc -c -fPIC add.c //这里一定要加上-fPIC选项,目的使库不必关心文件内函数位置
再编译
gcc -shared -fPIC -o libadd.so add.o

库文件连接

开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助许多函数库的支持才能够完成相应的功能。从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(so、或lib、dll)的集合。虽然Linux下的大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下;但也有的时候,我们要用的库不在这些目录下,所以GCC在编译时必须用自己的办法来查找所需要的头文件和库文件。
额外补充:Linux需要连接so库文件(带软连接),可以完完整整的复制到/usr/include//usr/lib/目录下,使用 cp -d * /usr/lib/ 命令,然后别忘记再运行 ldconfig

其中inclulde文件夹的路径是/home/test/include,lib文件夹是/home/test/lib,lib文件夹中里面包含二进制so文件libtest.so
首先要进行编译main.c为目标文件,这个时候需要执行:
gcc –c –I /home/test/include main.c –o main.o
最后把所有目标文件链接成可执行文件:
gcc –L /home/test/lib -ltest main.o –o main

默认情况下, GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,如果需要的话可以在编译时加上-static选项,强制使用静态链接库。
gcc –L /home/test/lib -static -ltest main.o –o main

静态库链接时搜索路径顺序:

  1. ld会去找GCC命令中的参数-L
  2. 再找gcc的环境变量LIBRARY_PATH
  3. 再找内定目录 /lib/usr/lib/usr/local/lib 这是当初compile gcc时写在程序内的

动态链接时、执行时搜索路径顺序:

  1. 编译目标代码时指定的动态库搜索路径
  2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
  3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
  4. 默认的动态库搜索路径/lib
  5. 默认的动态库搜索路径/usr/lib

相关环境变量:
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径

你可能感兴趣的:(Makefile)