用python写makefile

2020-01-03 16:32:33 浏览数 (1)

温馨提示:阅读本文的同学最好能了解makefile和python的编写规则。不懂的同学可以先保存在收藏夹,以便日后查看。

其实之前我一直很懒,我不想了解makefile规则,因为在linux下开发我一直使用Qt creator。(很多时候正是一些“懒人”的创造力,解放了我们的双手,显然现在我们还需要用双手写makefile)。Qt creator是一个很好的IDE,而且可以跨平台开发。但是相比VS,显然还是不够优秀。因此很多开发者都会选择在Windows下开发C/C 程序,然后部署在Linux下执行。当然我也不例外。所以最近花了几个晚上了解makefile的编写规则。

开始的时候,我参照网上一些makefile的例子,写了一个初版的makefile。然而这个makefile在编译我的工程的时候报错。

主要出错体现在:

%.o: %.cpp

    $(CXX) -fpic -c $(INCPATH) $< -o $@

当然可以写成

$(objdir)/%.o:$(srcdir)/%.cpp

    $(CXX) -fpic -c $(INCPATH) $< -o $@

原因在于:

1、.o文件与.cpp文件处于不同的目录下。

2、不同的.o文件或不同的.cpp文件处于不同的目录下。

这时我找到两种解决方法:

1、就是用VPATH这个特殊变量,但是我不可能将所有要包含的目录都一一手动包含进来,于是我放弃。

2、就是把所有的编译规则列举出来。

我最终选择第二种解决方法。因为之前遇到这个困难时,我特意去了解Qt生成的makefile(其实这个makefile是依据.pro工程文件生成的)。而这个makefile正是将所有的编译规则都列举出来。于是就有下面这个python脚本。其实开始的时候我想用shell来做这一步工作的,但是我看到sed和awk,我头都晕,之前还一直抵触学习sed和awk。因此最后选择了python。

代码语言:javascript复制
#encoding: utf-8

import os
import os.path
import sys

#sys.exit(0);
#########################################################################################################################
#
#	本脚本的作用是:通过配置必要的信息,用python来生成makefile。(技术支持:www.guimigame.com)
#	@FILENAME 		执行脚本输出makefile文件名
#	@BIN 			生成可执行文件名
#	@SUFFIX 		源文件后缀
#	@ROOTPATH		“根”目录路径(脚本工作目录的上一层)
#	@PWD			当前工作目录
#	@WD 			工作目录,如果程序有多个工作目录请一一用append加上
#	@BINDIR			可执行文件输出目录
#	@OBJDIR			中间文件输出目录
#	@INCROOTPATH	头文件包含路径的“根路径”,方便INCPATH的编写
#	@LIBROOTPATH 	包含库的“根”路径,方便LIBS的编写
#	@INCPATH 		头文件包含路径
#	@SYSLIBS 		包含的系统库
#	@LIBS			编译程序需要包含的库
# 	@CXX			一般填写gcc/g  
#	@FLAGS			gcc/g  的编译标志
#
#########################################################################################################################
FILENAME 	= 'makefile';
BIN			= "DatabaseServer";
SUFFIX 		= ".cpp";

ROOTPATH 	= os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
PWD 		= os.getcwd();

WD			= [];
WD.append(PWD);
WD.append(ROOTPATH   "/common");


BINDIR		= PWD   "/Bin/";
OBJDIR		= BINDIR   "obj/";

INCROOTPATH	= "-I "   ROOTPATH;
LIBROOTPATH = "-L "   ROOTPATH;

INCPATH 	= INCROOTPATH   "/common"   " "   INCROOTPATH   "/lib/include";
SYSLIBS		= " -lmysqlclient -lpthread"
LIBS		= LIBROOTPATH   "/lib/linux "   "-lTimeManager -lServerConfig -lGameSocket -lCommon -lTinyxml"   SYSLIBS;
CXX			= "g  ";
FLAGS		= '-g -Wall';

#########################################################################################################################
#
#	以下不需要再配置
#
#########################################################################################################################

OBJFILE		= '';
OBJ2SRC		= [];

SOURCES		= "";

def SearchFiles(path):
	global OBJFILE;
	global OBJ2SRC;
	global SOURCES;
	global SUFFIX;
	listFile = os.listdir(path);
	for file in listFile:
		if os.path.isdir(os.path.join(path, file)):
			SearchFiles(os.path.join(path, file));
		elif file.endswith(SUFFIX) > 0:
			OBJFILE = file;
			OBJFILE = OBJFILE.replace(SUFFIX,'.o');
			OBJ2SRC.append([OBJDIR   OBJFILE,path   "/"   file]);
			SOURCES  = path   "/"   file   " ";
		
for dir in WD:
	SearchFiles(dir);

if os.path.exists(FILENAME):
    os.remove(FILENAME);

f = open(FILENAME,'w');

f.write("PWD 				= "   PWD   "n");
f.write("CXX           		= "   CXX   "n");
f.write("INCPATH			= "   INCPATH   "n");
f.write("LIBS 				= "   LIBS   "n");
f.write("BINDIR				= "   BINDIR  "n");
f.write("OBJDIR				= "   OBJDIR   "n");
f.write("BIN 				= "   BIN   "n");
f.write("SOURCES			= "   SOURCES  "n");
f.write("SOURCEFILES		= $(notdir $(SOURCES))n");
f.write("OBJECTS			= $(addprefix $(OBJDIR), $(patsubst %.cpp,%.o,$(SOURCEFILES)))n");
f.write("FLAGS				= "   FLAGS   "n");
f.write("n");

f.write("all:makedir $(OBJECTS)n");
f.write("	$(CXX) $(FLAGS) $(INCPATH) -o $(BIN) $(OBJECTS) $(LIBS);n");
f.write("n");

f.write("makedir:n");
f.write('	$(shell if [ -n "$(OBJDIR)" -a ! -e "$(OBJDIR)" ];then mkdir -p $(OBJDIR); fi)n');
f.write('	$(shell if [ -n "$(BINDIR)" -a ! -e "$(BINDIR)" ];then mkdir -p $(BINDIR); fi)n');
f.write("n");	

for val in OBJ2SRC:
	f.write(val[0]   ":"   val[1]  "n");
	f.write("	rm -f $@n");
	f.write("	$(CXX) -fpic -c $(INCPATH) $< -o $@n");
	f.write("n");

f.close();

os.system("make");
os.system("mv "   BIN   " "   BINDIR);
os.system("cd "   OBJDIR   ";rm -f *.o");

如何编写makefile和python,这里不作说明。因为这篇文章不是makefile和python的教程。以下要说明的是SearchFiles函数。

通过遍历之前设定的工程工作目录,调用SearchFiles遍历该目录下所有的源文件(.cpp),及设定目标文件(.o)的绝对路径,最终是tuple的形式保存到OBJ2SRC数组中;还有的是将所有源文件保存在SOURCES中。当然这个过程中会递归遍历所有子目录,查找到所有的源文件。最终在for val in OBJ2SRC:遍历所有的数据;列出所有的源文件(.cpp)生成所对应的目标文件(.o),将编译规则写进makefile。

这是我要编译的工程,当然截图只是其中一部分。这个工程需要包含的文件除了在DatabaseServer下,还要包含在../common当中(脚本中代码WD.append(ROOTPATH "/common");)。我截图是为了证明,这个脚本是可行的。有人可能会说为什么不写一个测试例子。其实我想说,很多时候要弄懂一些技术,动手去做也许是最好的方法。如果你有什么问题,欢迎与我讨论!

0 人点赞