150 Linux C++ 通讯架构实战6 服务器程序目录规划,通过vs2017实现文件夹的创建,makefile编写

从无到有产生这套 通讯架构源代码【项目/工程】

一,服务器程序目录规划

一个完整的项目 肯定会有多个源文件,头文件,会分别存放到多个目录;

我们这里要规划项目的目录结构;

注意:不管是目录还是文件,文件名中一律不要带空格,一律不要用中文,最好的方式:字母,数字,下划线;

主目录名nginx
 

a) _include目录:专门存放各种头文件; 

b) app目录:放主应用程序.c(main()函数所在的文件)以及一些比较核心的文件;


       b.1)link_obj:临时目录:

                会存放临时的.o文件,这个目录不手工创建,后续用makefile脚本来创建


       b.2)dep:临时目录,

                会存放临时的.d开头的依赖文件,依赖文件能够告知系统哪些相关的文件发生变化,需要重新编译,后续用makefile脚本来创建


       b.3)nginx.c:主文件,

                main()入口函数就放到这里;


       b.4)ngx_conf.c  ,

                普通的源码文件,跟主文件关系密切,又不值得单独放在 一个目录;


c)misc目录:

        专门存放各种杂合性的不好归类的1到多个.c文件;暂时为空


d)net目录:

        专门存放和网络处理相关的1到多个.c文件,暂时为空


e)proc目录:

        专门存放和进程处理相关的1到多个.c文件,暂时为空


f)signal目录:

        专门用于存放和信号处理有关的 1到多个.c文件;
        //ngx_signal.c

linux上用tree看一下目录结构

二 如何在windows 上通过vs编写代码传递到linux上去,这里是通过vmware的设置操作

三。使用makefile 编译出最终的可执行文件

makefile 需要时utf-8格式的

原理图如下。使用每一个.c文件生成一个.o文件,最终这些.o文件链接在一起,生成最终可执行文件

当前的例子是这样的依赖关系

这里需要对老师写的makefile 理解,

makefile文件的编写
    //a)nginx根目录下我会放三个文件:
      //a.1)  makefile:是咱们编译项目的入口脚本,编译项目从这里开始,起总体控制作用;
      //a.2)config.mk:这是个配置脚本,被makefile文件包含;单独分离出来是为了应付一些可变的东西,所以,一般变动的东西都往这里搞;
      //a.3)common.mk:是最重要最核心的编译脚本,定义makefile的编译规则,依赖规则等,通用性很强的一个脚本,
         //并且各个子目录中都用到这个脚本来实现对应子目录的.c文件的编译;

    //b)每个子目录下(app,signal)都有一个叫做makefile的文件,每个这个makefile文件,都会包含根目录下的common.mk,
       //从而实现自己这个子目录下的.c文件的编译
    //现在的makefile不支持目录中套子目录,除非大家自己修改;
    //c)其他规划,上边讲过;
      //app/link_obj临时目录,存放.o目标文件
      //app/dep:存放.d开头的依赖关系文件;

    //(2.3)makefile脚本用法介绍
    //a)编译项目,生成可执行文件
    //make
    //make clean

    //(2.4)makefile脚本具体实现讲解
    //从common.mk讲起
    //将来增加新目录时:
    //a)修改根目录下的config.mk来增加该目录
    //b)在对应的目录下放入makefile文件,内容参考signal目录ixa的makefile文件即可
 

 //a.1) 最外层makefile是咱们编译项目的入口脚本,编译项目从这里开始,起总体控制作用;

内容


include config.mk   #自己的注释1,include 表示导入 config.mk

#-C是指定目录
 #自己的注释2, Makefile中-C是递归调用子目录中的Makefile,-C选项后跟目录,表示到子目录下执行子目录的Makefile,
 #自己的注释2,顶层Makefile中的export的变量还有make默认的变量是可以传递给子目录中的Makefile的
#make -C signal  

#可执行文件应该放最后
#make -C app      

#自己的注释3 for循环,从 BUILD_DIR中拿出来每一条信息,然后赋值给dir,为什么要给dir 前面加上两个$$呢?
#自己的注释3 我们知道Makefile引用变量只需要一个$符号就够了,为什么这里要两个呢?
#自己的注释3 因为变量i是在shell的for循环中定义的,是属于shell中的变量。
#自己的注释3 make在读到$$dir的时候会把它扩展成$dir(有点转义字符的意味),然后交给shell的时候,shell才能从中拿出来真正的dir中的值
#自己的注释3 makefile的 all表示 此makefile的终极目标,从文中看,终极目标就是对于 BUILD_DIR 中的进行循环,然后对BUILD_DIR中的makefile进行调用
#用shell命令for搞,shell里边的变量用两个$

all:
	@for dir in $(BUILD_DIR); \
	do \
		make -C $$dir; \
	done


clean:
#-rf:删除文件夹,强制删除
	rm -rf app/link_obj app/dep nginx
	rm -rf signal/*.gch app/*.gch

最外层的makefile引用到了config.mk,下来看config.mk的写法

      //a.2)config.mk:这是个配置脚本,被makefile文件包含;单独分离出来是为了应付一些可变的东西,所以,一般变动的东西都往这里搞;
 

写法:

注意的是BUILD_DIR是有顺序的, app始终要放在最后一个,这是因为app下的子目录中的makefile是我们最终要编译出来的可执行文件,这个可执行文件一定是依赖于前面各个模块build出来的.o文件生成的。


#定义项目编译的根目录,通过export把某个变量声明为全局的[其他文件中可以用],这里获取当前这个文件所在的路径作为根目录;
#BUILD_ROOT = /mnt/hgfs/linux/nginx
export BUILD_ROOT = $(shell pwd)

#定义头文件的路径变量
export INCLUDE_PATH = $(BUILD_ROOT)/_include

#定义我们要编译的目录
BUILD_DIR = $(BUILD_ROOT)/signal/ \
			$(BUILD_ROOT)/app/ 

#编译时是否生成调试信息。GNU调试器可以利用该信息
export DEBUG = true

从最外层的makefile的目标是对于BUILD_DIR下面signal 和 app子目录的

    //b)每个子目录下(app,signal)都有一个叫做makefile的文件,每个这个makefile文件,都会包含根目录下的common.mk,

那么先看哪一个呢?因为我们的设计是app中放置的代码是最终会build出来的可执行的文件,

那么app下面的makefile是最后看的。

如果除了app外,其他目录都有信息,那么我们先看哪一个呢?从没有依赖的那个看起,这也决定了我们在写最外层config.mk时的顺序

我们看到 signal下的makefile 的BIN 是没有值的,这个BIN是我们定义变量,在外层的common.mk中要使用到。从common.mk中的使用,可以知道,这个BIN代表的意思是:当前目录下的.c文件是否需要build出来可执行文件。因此设置为空,这个在后面的common.mk中会有详细的说明。

再来看下一行,由include了外层的common.mk,那么我们下面就要再来看外层的common.mk

#只生成 .d,.o即可 
BIN = 
include $(BUILD_ROOT)/common.mk

 //a.3)common.mk:是最重要最核心的编译脚本,定义makefile的编译规则,依赖规则等,通用性很强的一个脚本,

写法:

#自己的注释6:  .PHONY: clean ALL 这句话的意思是:即使当前文件夹下有 clean的文件,或者有ALL的文件,会认为clean文件和ALL文件是伪目标。
#自己的注释6:  不会执行真正的文件,而是执行make 命令参数。例如:make clean
#自己的注释6:  但是不知道为啥老师讲这块注释了

#.PHONY:all clean 

ifeq ($(DEBUG),true)
#-g是生成调试信息。GNU调试器可以利用该信息
CC = gcc -g
VERSION = debug
else
CC = gcc
VERSION = release
endif

#CC = gcc

#自己的注释7:  wildcard是百搭牌的意思,也就是当 SRCS = $(wildcard *.c) 执行完毕后,SRCS中的数据就变成了当前文件下的所有的.c文件的集合
# $(wildcard *.c)表示扫描当前目录下所有.c文件
#SRCS = nginx.c ngx_conf.c
SRCS = $(wildcard *.c)

#OBJS = nginx.o ngx_conf.o  这么一个一个增加.o太麻烦,下行换一种写法:把字符串中的.c替换为.o
OBJS = $(SRCS:.c=.o)

#把字符串中的.c替换为.d
#DEPS = nginx.d ngx_conf.d
DEPS = $(SRCS:.c=.d)

#自己的注释8: 在singal文件夹下的makefile,BIN的值是NULL,因此对于singal目录下,就没有文件夹
#自己的注释8: addprefix是函数,意思是将逗号后面的文件,放在逗号前面的目录下
#可以指定BIN文件的位置,addprefix是增加前缀函数
#BIN = /mnt/hgfs/linux/nginx
BIN := $(addprefix $(BUILD_ROOT)/,$(BIN))

#定义存放ojb文件的目录,目录统一到一个位置才方便后续链接,不然整到各个子目录去,不好链接
#注意下边这个字符串,末尾不要有空格等否则会语法错误 
LINK_OBJ_DIR = $(BUILD_ROOT)/app/link_obj
DEP_DIR = $(BUILD_ROOT)/app/dep

#-p是递归创建目录,没有就创建,有就不需要创建了
$(shell mkdir -p $(LINK_OBJ_DIR))
$(shell mkdir -p $(DEP_DIR))

#我们要把目标文件生成到上述目标文件目录去,利用函数addprefix增加个前缀
#处理后形如 /mnt/hgfs/linux/nginx/app/link_obj/ngx_signal2.o /mnt/hgfs/linux/nginx/app/link_obj/ngx_signal.o
# := 在解析阶段直接赋值常量字符串【立即展开】,而 = 在运行阶段,实际使用变量时再进行求值【延迟展开】
# /mnt/hgfs/linux/nginx/app/link_obj/nginx.o   /mnt/hgfs/linux/nginx/app/link_obj/ngx_conf.o 
OBJS := $(addprefix $(LINK_OBJ_DIR)/,$(OBJS))
DEPS := $(addprefix $(DEP_DIR)/,$(DEPS))

#找到目录中的所有.o文件(编译出来的)
LINK_OBJ = $(wildcard $(LINK_OBJ_DIR)/*.o)
#因为构建依赖关系时app目录下这个.o文件还没构建出来,所以LINK_OBJ是缺少这个.o的,我们 要把这个.o文件加进来
LINK_OBJ += $(OBJS)

#-------------------------------------------------------------------------------------------------------
#make找第一个目标开始执行[每个目标[就是我们要生成的东西],其实都是定义一种依赖关系],目标的格式为:
#目标:目标依赖【可以省略】
#	要执行的命令【可以省略】
#如下这行会是开始执行的入口,执行就找到依赖项$(BIN)去执行了,同时,这里也依赖了$(DEPS),这样就会生成很多.d文件了
all:$(DEPS) $(OBJS) $(BIN)

#这里是诸多.d文件被包含进来,每个.d文件里都记录着一个.o文件所依赖哪些.c和.h文件。内容诸如 nginx.o: nginx.c ngx_func.h
#我们做这个的最终目的说白了是,即便.h被修改了,也要让make重新编译我们的工程,否则,你修改了.h,make不会重新编译,那不行的
#有必要先判断这些文件是否存在,不然make可能会报一些.d文件找不到
ifneq ("$(wildcard $(DEPS))","")   #如果不为空,$(wildcard)是函数【获取匹配模式文件名】,这里 用于比较是否为""
include $(DEPS)  
endif

#----------------------------------------------------------------1begin------------------
#自己的注释9 这里用到了 makefile 的 模式规则
#$(BIN):$(OBJS)
$(BIN):$(LINK_OBJ)
	@echo "------------------------build $(VERSION) mode--------------------------------!!!"

#一些变量:$@:目标,     $^:所有目标依赖
# gcc -o 是生成可执行文件
	$(CC) -o $@ $^

#----------------------------------------------------------------1end-------------------


#----------------------------------------------------------------2begin-----------------
#%.o:%.c
$(LINK_OBJ_DIR)/%.o:%.c
# gcc -c是生成.o目标文件   -I可以指定头文件的路径
#如下不排除有其他字符串,所以从其中专门把.c过滤出来 
#$(CC) -o $@ -c $^
	$(CC) -I$(INCLUDE_PATH) -o $@ -c $(filter %.c,$^)
#----------------------------------------------------------------2end-------------------



#----------------------------------------------------------------3begin-----------------
#我们现在希望当修改一个.h时,也能够让make自动重新编译我们的项目,所以,我们需要指明让.o依赖于.h文件
#那一个.o依赖于哪些.h文件,我们可以用“gcc -MM c程序文件名” 来获得这些依赖信息并重定向保存到.d文件中
#.d文件中的内容可能形如:nginx.o: nginx.c ngx_func.h
#%.d:%.c
$(DEP_DIR)/%.d:%.c
#gcc -MM $^ > $@
#.d文件中的内容形如:nginx.o: nginx.c ngx_func.h ../signal/ngx_signal.h,但现在的问题是我们的.o文件已经放到了专门的目录
# 所以我们要正确指明.o文件路径这样,对应的.h,.c修改后,make时才能发现,这里需要用到sed文本处理工具和一些正则表达式语法,不必深究
#gcc -MM $^ | sed 's,\(.*\)\.o[ :]*,$(LINK_OBJ_DIR)/\1.o:,g' > $@
#echo 中 -n表示后续追加不换行
	echo -n $(LINK_OBJ_DIR)/ > $@
#	gcc -MM $^ | sed 's/^/$(LINK_OBJ_DIR)&/g' > $@
#  >>表示追加
	gcc -I$(INCLUDE_PATH) -MM $^ >> $@

#上行处理后,.d文件中内容应该就如:/mnt/hgfs/linux/nginx/app/link_obj/nginx.o: nginx.c ngx_func.h ../signal/ngx_signal.h

#----------------------------------------------------------------4begin-----------------



#----------------------------------------------------------------nbegin-----------------
#clean:			
#rm 的-f参数是不提示强制删除
#可能gcc会产生.gch这个优化编译速度文件
#	rm -f $(BIN) $(OBJS) $(DEPS) *.gch
#----------------------------------------------------------------nend------------------





  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值