一个自动生成 makefile 依赖的 bash 脚本 - 哆啦比猫's Blog - I'm an ArchLinuxer

一个自动生成 makefile 依赖的 bash 脚本

哆啦比猫 posted @ 2011年8月10日 16:42 in Shell with tags make makefile bash shell dependence generate auto , 3523 阅读

很多初学 Linux 编程的人抱怨 makefile 怎么那么麻烦,干嘛不能自动生成依赖关系;我有时也觉得写 makefile 比较麻烦,每次代码里增加文件都要去改 makefile,注意力无法集中于代码上,后来干脆尽量不增加文件,把代码都尽可能的塞进已有的文件。

Makefile 的这种设计自有它的优点,但这确实给人们带来太多麻烦。我也见过一些可以解决此麻烦的方法,但是得另外装软件。于是乎,一个念头闪过,何不用 bash 写一个自动生成 makefile 依赖的脚本呢?顺便还能练一下 bash 呢。

经过 2 天间断性的努力,终于完成了。

#!/bin/bash

# Automatic Dependencies Solving Makefile Generating Configure Script
# Written by eXerigumo Clanjor

# CUSTOMIZE
MAIN="main"
LIBS="-lglut -lGL -lGLU"

# GLOBAL VARS
CXXFLAGS="-s"
CXXOPTIMIZE="-O2"
DEPS=()
OBJS=()

# -- void main(argv)
main()
{
	# parse arguments
	while ! [ -z "$1" ]; do
		case $1 in
			"--help" | "-h")	show_help
								;;
			"--enable-debug")	CXXFLAGS="-g -DDEBUG"
								CXXOPTIMIZE=""	# No optimization
								shift ;;
			"-Ofast")	CXXOPTIMIZE="-Ofast"
						shift ;;
			"--clean")	make cleanall
						rm -f makefile
						exit 0
						;;
			*)	error "unknown argument: $1"
				;;
		esac
	done

	generate_makefile
}

# -- void generate_makefile(void)
generate_makefile()
{
	echo -e "# Generated by ADSMGCS." > makefile
	echo >> makefile
	echo -e "CXXFLAGS = -Wall $CXXFLAGS $CXXOPTIMIZE $LIBS" >> makefile
	echo >> makefile

	echo -e "all: $MAIN" >> makefile
	echo -e "clean:" >> makefile
	echo -e "\trm -f *.o" >> makefile
	echo -e "cleanall: clean" >> makefile
	echo -e "\trm -f $MAIN" >> makefile
	echo -e "rebuild: clean all" >> makefile
	echo -e "debug: all" >> makefile
	echo -e "\t./$MAIN" >> makefile
	echo >> makefile

	generate_dep "${MAIN}.cc"

	echo -e "$MAIN:" ${OBJS[*]} >> makefile
	echo -e '\t$(CXX) $(CXXFLAGS) -o $@ $^' >> makefile
	cat .makefile >> makefile
	echo >> makefile
	rm -f .makefile
}

# -- void show_help(void)
show_help()
{
	echo
	echo -e "OPTIONS:"
	echo -e "\t--help, -h"
	echo -e "\t\tShow this help"
	echo -e "\t--enable-debug"
	echo -e "\t\tEnable debug mode"
	echo -e "\t-Ofast"
	echo -e "\t\tUse -Ofast instead of -O2"
	echo

	exit 0
}

# -- void error(string err)
error()
{
	echo -e "\e[1;31merror:\e[m $1"
	exit 1
}

# -- void generate_dep(string file)
generate_dep()
{
	echo -e "Solving dependencies of $1..."

	if ! g++ -MM "$1" > .dep; then
		error "unexpected failure of g++."
	fi
	cat .dep >> .makefile
	echo -e '\t$(CXX) $(CXXFLAGS) -c -o $@ $<' >> .makefile

	local dep="`cat .dep|awk '{sub(/\\\\/, ""); printf $0}' \
			|sed 's/[^ ]\+: [^ ]\+ \?//g'`"
	local obj="`cat .dep|head -n 1|awk -F: '{printf $1}'`"
	rm -f .dep

	OBJS[${#OBJS[*]}]="$obj"
	
	local d
	for d in $dep; do
		if need_recursive_dep "$d"; then
			[[ "$d" =~ (.*)\.h$ ]] && d="${BASH_REMATCH[1]}.cc"
			[ -f "$d" ] && generate_dep "$d"
		fi
	done
}

# -- bool need_recursive_dep(string file)
need_recursive_dep()
{
	local d
	for d in ${DEPS[*]}; do
		[[ "$d" == "$1" ]] && return 1
	done

	DEPS[${#DEPS[*]}]="$1"
	return 0
}

main $*

将此脚本命名为 configure(以假乱真,哈哈),然后就可以两部曲了——./configure && make

目前仅支持 C++,但理论上只要是 GCC 支持的语言都可以用,只不过要改里面关于后缀名替换的部分。

主文件名由全局变量 MAIN 指定(MAIN="main" 代表主文件名为 main.cc,生成的可执行文件名为 main)


凡未特殊声明(转载/翻译),所有文章均为原创。
by Giumo Xavier Clanjor (哆啦比猫/兰威举), 2010, 2011, 2012, 2013, 2014, 2015-2016 and 2017.
知识共享许可协议本作品采用知识共享署名·非商业性使用·相同方式共享 3.0 中国大陆许可协议进行许可。
文中凡未特殊声明且未声明为引用的代码均以 MIT 协议授权。

blog comments powered by Disqus
© 2010, 2011, 2012, 2013, 2014, 2015-2016 and 2017 Giumo Xavier Clanjor (哆啦比猫/兰威举).
© 2013, 2014, 2015-2016 and 2017 The Dark Colorscheme Designed by Giumo Xavier Clanjor (哆啦比猫/兰威举).
知识共享署名·非商业性使用·相同方式共享 3.0 中国大陆许可协议
| © 2007 LinuxGem | Design by Matthew "Agent Spork" McGee