构建工程化:多种不同的工程体系如何编写MakeFile

时间:2024-07-07 07:00:19

源码分析

核心MakeFile

这个 Makefile 是一个复杂的构建脚本,用于管理和构建一个大型项目。它包括多个目标、条件判断和递归调用 make 命令来处理多个子项目和子目录。让我们逐部分进行详细解析。

伪目标和变量定义

.PHONY: all clean install build test init
  • .PHONY 目标声明了 allcleaninstallbuildtestinit 是伪目标,不对应实际文件。
PLATDIRS = newbuild so fantom share party www update var SIP-INSTALLROOT linux
CLEANDIRS = $(PLATDIRS) apps
  • PLATDIRSCLEANDIRS 是目录列表变量,用于后续的构建和清理操作。

模式规则

%.all :
	cd $* && $(MAKE)
%.clean :
	cd $* && $(MAKE) clean
%.build :
	cd $* && $(MAKE) build
  • 这三条规则定义了通用目标规则,对于目录列表中的每个目录,会进入该目录并执行相应的 make 目标(allcleanbuild)。

clean 目标

clean:
	rm -rf $(BUILD)/fantom
	make $(patsubst %, %.clean, $(CLEANDIRS))
  • clean 目标删除 $(BUILD)/fantom 目录,并调用 make 命令清理 CLEANDIRS 列表中的每个目录。

平台预构建目标

build_plat_pre:
	@chmod +x $(ROOT)/update/setbaseversion	
	@if [ ! -d $(BUILDROOT) ]; then mkdir -p $(BUILDROOT) ; fi
	@if [ ! -d $(BUILDROOT)/newbuild ]; then mkdir -p $(BUILDROOT)/newbuild ; fi
	@if [ ! -d $(BUILDROOT)/apps ]; then mkdir -p $(BUILDROOT)/apps ; fi
	@if [ ! -d $(BUILDROOT)/bin ]; then mkdir -p $(BUILDROOT)/bin ; fi
	@if [ ! -d $(BUILDROOT)/party ]; then mkdir -p $(BUILDROOT)/party ; fi
	@if [ ! -d $(BUILDROOT)/share/lib ]; then mkdir -p $(BUILDROOT)/share/lib ; fi
	@if [ ! -d $(BUILDROOT)/share/Python-2.7/lib ]; then mkdir -p $(BUILDROOT)/share/Python-2.7/lib ; fi
  • build_plat_pre 目标进行了一些预构建任务,包括设置文件权限和创建所需目录。

应用列表和目标

sis_apps_list = accessskeleton coengine dnsdetect ngfw saas secvisual server_anomaly super \
			wskiller update secdetect dgadetect httpdetector scandetect beforehanddetect \
			cloud_report webshelldetect wxscandetect brutedetect dosdetect netflow_forensic smbdetect \
			mailector secevent sec_dnsdetect sec_httpdetect sec_maildetect sec_netdetect sec_secdetect \
			sec_smbdetect sec_tidnsdetect sec_tihttpdetect sec_tiipdetect sec_thirdparty infoshare monitor \
			sec_filedetect sec_threatdetect documents agent
ifndef NO_SECLIB
sis_apps_list += seclib
endif
SISAPPS = $(patsubst %, apps/%, $(sis_apps_list))
  • sis_apps_list 定义了一个应用列表,包含多个应用名。
  • 如果未定义 NO_SECLIB,则将 seclib 添加到应用列表中。
  • SISAPPSsis_apps_list 转换为带有 apps/ 前缀的格式。

平台打包前置任务

plat_sis_pre:
	@python $(ROOT)/apps/super/plat/plat.py $(ROOT)/apps/super/plat/sis/plugins.conf
  • plat_sis_pre 目标运行一个 Python 脚本,用于配置或预处理。

平台打包

plat_sis: plat_sis_pre plat_fantom $(SISAPPS:=.build)
  • plat_sis 目标依赖于 plat_sis_preplat_fantom 和所有 SISAPPS.build 目标。

云平台应用列表和目标

cloud_apps_list = super cloud_report
CLOUDAPPS = $(patsubst %, apps/%, $(cloud_apps_list))
  • cloud_apps_list 定义了云平台的应用列表。
  • CLOUDAPPScloud_apps_list 转换为带有 apps/ 前缀的格式。
plat_cloud_pre:
	@python $(ROOT)/apps/super/plat/plat.py $(ROOT)/apps/super/plat/cloud/plugins.conf
  • plat_cloud_pre 目标运行一个 Python 脚本,用于云平台的预处理。
plat_cloud: plat_cloud_pre plat_fantom $(CLOUDAPPS:=.build)
  • plat_cloud 目标依赖于 plat_cloud_preplat_fantom 和所有 CLOUDAPPS.build 目标。

平台基础内容打包

plat_fantom: build_plat_pre $(PLATDIRS:=.build)
	@cd $(BUILD) && tar -czf fantom.tar.gz fantom
  • plat_fantom 目标依赖于 build_plat_pre 和所有 PLATDIRS.build 目标。
  • 它会进入 $(BUILD) 目录并将 fantom 目录打包成 fantom.tar.gz

应用打包预处理

build_apps_pre:
	@if [ ! -d "$(BUILDROOT)/apps" ]; then mkdir -p "$(BUILDROOT)/apps" ; fi
  • build_apps_pre 目标确保 $(BUILDROOT)/apps 目录存在。

应用打包

plat_apps: build_apps_pre
ifdef NAME
	@if [ -d $(ROOT)/apps/$(NAME) ]; then \
		cd "$(ROOT)/apps/$(NAME)" && make build; 	\
		cd "$(ROOT)/update" && make build; 	\
		cd $(BUILD)/fantom/apps/ && tar -czf $(NAME).tar.gz $(NAME);	\
		mv $(NAME).tar.gz $(BUILD);\
	else \
		echo "App $(NAME) not exists";	\
	fi
else
endif
  • plat_apps 目标依赖于 build_apps_pre

  • 如果定义了 NAME 变量并且 $(ROOT)/apps/$(NAME) 目录存在,则

    • 进入 $(ROOT)/apps/$(NAME) 并执行 make build
    • 进入 $(ROOT)/update 并执行 make build
    • $(BUILD)/fantom/apps/$(NAME) 目录打包成 $(NAME).tar.gz,并移动到 $(BUILD) 目录。
  • 如果 $(ROOT)/apps/$(NAME) 目录不存在,则输出错误信息。

总结

这个 Makefile 用于构建和打包一个复杂的项目,包含多个应用和平台组件。它通过定义变量和模式规则来管理不同的构建任务,并使用条件判断和递归调用 make 命令来处理各个子目录和应用的构建过程。

php

TYPE = dir

ifdef NO_SECLIB
dirs := $(shell find . -maxdepth 1 -path ./seclib -prune -o -type d -print)
else
dirs := $(shell find . -maxdepth 1 -type d)
endif

dirs := $(basename $(patsubst ./%,%,$(dirs)))

SUBDIRS = $(dirs)

include $(MAKEFILEDIR)/Makefile.$(TYPE)

这个 Makefile 代码片段通过使用条件和 shell 命令来动态生成子目录列表,并根据一个特定的类型包含相应的 Makefile。让我们逐行解析这个 Makefile 片段

代码解析

TYPE = dir
  • 这行定义了一个变量 TYPE,其值为 dir。这个变量在后续的 include 语句中被使用。
ifdef NO_SECLIB
dirs := $(shell find . -maxdepth 1 -path ./seclib -prune -o -type d -print)
else
dirs := $(shell find . -maxdepth 1 -type d)
endif
  • 这里使用了条件判断 ifdef 来检测是否定义了 NO_SECLIB 变量。

  • 如果定义了 NO_SECLIB,则执行第一条命令

    dirs := $(shell find . -maxdepth 1 -path ./seclib -prune -o -type d -print)
    
    • 这条命令使用 find 命令查找当前目录(不包括子目录)的所有目录,但排除了 ./seclib 目录。
    • -maxdepth 1 限制查找深度为当前目录。
    • -path ./seclib -prune 用于排除 ./seclib 目录。
    • -o -type d -print 查找类型为目录并打印路径。
  • 否则(如果未定义 NO_SECLIB),则执行第二条命令

    dirs := $(shell find . -maxdepth 1 -type d)
    
    • 这条命令使用 find 命令查找当前目录(不包括子目录)的所有目录。
dirs := $(basename $(patsubst ./%,%,$(dirs)))
  • 这行代码使用了 patsubstbasename 函数对 dirs 变量进行处理

    • $(patsubst ./%,%,$(dirs)) 移除每个目录路径前面的 ./ 前缀。
    • $(basename $(patsubst ./%,%,$(dirs))) 进一步处理每个目录路径,只保留目录名称。
SUBDIRS = $(dirs)
  • 将处理后的目录列表赋值给变量 SUBDIRS
include $(MAKEFILEDIR)/Makefile.$(TYPE)
  • 包含另一个 Makefile 文件。文件的路径和名称由 $(MAKEFILEDIR)/Makefile.$(TYPE) 决定。
    • $(MAKEFILEDIR) 是另一个变量,通常在其他地方定义,用于指定 Makefile 文件所在的目录。
    • $(TYPE) 变量的值为 dir,所以这行等价于 include $(MAKEFILEDIR)/Makefile.dir

总结

这个 Makefile 片段的目的是

  • 动态生成当前目录下的子目录列表,并处理排除某些特定目录(如 seclib)。
  • 处理目录路径,将它们标准化为不带 ./ 前缀的格式。
  • 将处理后的目录列表赋值给 SUBDIRS 变量。
  • 根据 TYPE 变量的值,包含另一个 Makefile 文件,便于分层和模块化管理构建过程。

通过这种方式,可以根据不同的条件动态调整构建过程,灵活应对各种需求和依赖关系。

python

TYPE = app

SUBDIRS =

.PHONY: build clean

appversion=`date "+%Y%m%d/%H:%M:%S"`

build:
	@find ./appserver -name .svn | xargs rm -rf
	@find ./bin -name .svn | xargs rm -rf
	@find ./default -name .svn | xargs rm -rf
	@find ./lib -name .svn | xargs rm -rf
	@find ./local -name .svn | xargs rm -rf
	@find ./var -name .svn | xargs rm -rf
	@find ./meta-info -name .svn | xargs rm -rf
	
	@rwini -w -s info -k version -f libinfo -v $(appversion)
	
	@mkdir -p ./patch0/$(notdir $(CURDIR))
	@mv -f ./appserver ./patch0/$(notdir $(CURDIR))/appserver
	@mv -f ./bin ./patch0/$(notdir $(CURDIR))/bin
	@mv -f ./default ./patch0/$(notdir $(CURDIR))/default
	@mv -f ./lib ./patch0/$(notdir $(CURDIR))/lib
	@mv -f ./local ./patch0/$(notdir $(CURDIR))/local
	@mv -f ./var ./patch0/$(notdir $(CURDIR))/var
	@cp -rf ./libinfo ./patch0/$(notdir $(CURDIR))/libinfo
	@cp -rf ./meta-info/update ./patch0
	@cp -rf ./meta-info ./patch0/$(notdir $(CURDIR))/meta-info
	
	@zip -r -q $(notdir $(CURDIR)).zip patch0 libinfo meta-info
	@rm -rf ./libinfo
	@rm -rf ./meta-info
	@rm -rf ./patch0
	
	@if [ ! -d "$(BUILDROOT)/apps/$(notdir $(CURDIR))" ]; then mkdir -p "$(BUILDROOT)/apps/$(notdir $(CURDIR))" ; fi
	@rsync -r . "$(BUILDROOT)/apps/$(notdir $(CURDIR))" --exclude="Makefile" --exclude='.svn/'
clean:
	@rm -f $(notdir $(CURDIR)).zip

这个 Makefile 定义了两个主要目标
buildclean。它主要用于清理项目目录中的 .svn 文件夹、更新版本信息、组织项目文件,并将其打包成压缩文件。下面是对每一部分的详细解析

变量和伪目标

TYPE = app
SUBDIRS =
.PHONY: build clean
  • TYPE = app
    定义了一个变量 TYPE,其值为 app。虽然在这个 Makefile 中没有进一步使用,但它可以用来在包含的其他 Makefile 中使用。
  • SUBDIRS =
    定义了一个空的 SUBDIRS 变量。在这个 Makefile 中没有实际使用。
  • .PHONY: build clean
    声明了两个伪目标 buildclean,这表示这些目标不是实际的文件名,而是任务名称。

版本信息

appversion=`date "+%Y%m%d/%H:%M:%S"`
  • appversion 变量用来保存当前的日期和时间,格式为 YYYYMMDD/HH:MM:SS。这个变量会用于更新版本信息。

build 目标

build:
	@find ./appserver -name .svn | xargs rm -rf
	@find ./bin -name .svn | xargs rm -rf
	@find ./default -name .svn | xargs rm -rf
	@find ./lib -name .svn | xargs rm -rf
	@find ./local -name .svn | xargs rm -rf
	@find ./var -name .svn | xargs rm -rf
	@find ./meta-info -name .svn | xargs rm -rf
	@rwini -w -s info -k version -f libinfo -v $(appversion)
	@mkdir -p ./patch0/$(notdir $(CURDIR))
	@mv -f ./appserver ./patch0/$(notdir $(CURDIR))/appserver
	@mv -f ./bin ./patch0/$(notdir $(CURDIR))/bin
	@mv -f ./default ./patch0/$(notdir $(CURDIR))/default
	@mv -f ./lib ./patch0/$(notdir $(CURDIR))/lib
	@mv -f ./local ./patch0/$(notdir $(CURDIR))/local
	@mv -f ./var ./patch0/$(notdir $(CURDIR))/var
	@cp -rf ./libinfo ./patch0/$(notdir $(CURDIR))/libinfo
	@cp -rf ./meta-info/update ./patch0
	@cp -rf ./meta-info ./patch0/$(notdir $(CURDIR))/meta-info
	@zip -r -q $(notdir $(CURDIR)).zip patch0 libinfo meta-info
	@rm -rf ./libinfo
	@rm -rf ./meta-info
	@rm -rf ./patch0
	@if [ ! -d "$(BUILDROOT)/apps/$(notdir $(CURDIR))" ]; then mkdir -p "$(BUILDROOT)/apps/$(notdir $(CURDIR))" ; fi
	@rsync -r . "$(BUILDROOT)/apps/$(notdir $(CURDIR))" --exclude="Makefile" --exclude='.svn/'
清理 .svn 文件夹
  • 使用 find 命令查找并删除各个目录下的 .svn 文件夹,这些文件夹是 Subversion (SVN) 版本控制系统的目录,不需要打包到最终的发布版本中。
更新版本信息
  • 使用 rwini 命令更新 libinfo 文件中的版本信息,rwini 是一个假设的工具,用于写入 INI 文件格式中的值。具体操作是设置 [info] 节点中的 version 键为当前时间。
组织项目文件
  • 创建一个名为 patch0 的目录,并将各个子目录(如 appserverbindefaultliblocalvar)移动到 patch0/$(notdir $(CURDIR)) 中。
    • $(notdir $(CURDIR)) 获取当前目录的名称(不包括路径),例如,如果当前目录是 /home/user/project,则 $(notdir $(CURDIR)) 会得到 project
复制和压缩文件
  • libinfometa-info 目录复制到 patch0 下的相应位置。
  • 使用 zip 命令将 patch0libinfometa-info 压缩成一个 zip 文件,名称为当前目录名称加 .zip(例如 project.zip)。
  • 删除 libinfometa-infopatch0 目录。
同步文件
  • 如果 $(BUILDROOT)/apps/$(notdir $(CURDIR)) 目录不存在,则创建它。
  • 使用 rsync 命令将当前目录的内容(除了 Makefile.svn 文件夹)同步到 $(BUILDROOT)/apps/$(notdir $(CURDIR)) 目录。

clean 目标

clean:
	@rm -f $(notdir $(CURDIR)).zip
  • 删除以当前目录名称命名的 zip 文件(例如 project.zip)。

总结

  • 这个 Makefile 主要用于清理项目目录中的 .svn 文件夹,更新版本信息,组织和打包项目文件,并将其同步到一个特定的构建目录。
  • build 目标执行了所有这些步骤,而 clean 目标仅删除生成的 zip 文件。
  • 使用了 shell 命令和 rwini 工具来处理文件和目录,动态生成版本信息,并打包和同步项目文件。