本文使用 Zhihu On VSCode 创作并发布
在我们使用cmake构建c++项目,仍然会遇到各种各样的问题,比如设置编译器路径,生成配置,install的配置等等。这是仍然需要一些细碎的cmake指令。这时,就可以使用python脚本来执行构建命令。通过python脚本生成一列的cmake的指令,控制cmake生成规则,从而控制程序的生成。下面就讲述python脚本控制cmake的方法。
具体代码,见这里
设置项目路径
给出当前脚本路径变量
scriptDir = os.path.dirname(os.path.realpath(__file__)) # 给出脚本的路径
给出项目路径
# 给出项目的路径,所以,脚本和项目的相对位置要设置好
projRoot = os.path.abspath(os.path.join(scriptDir))
# projRoot = os.path.abspath(os.path.join(scriptDir, "..", "..", ".."))
可以利用脚本路径相对于项目根目录的位置设置项目根目录
解析脚本执行时给出的变量
parser = argparse.ArgumentParser() # 解析命令
#positional argument
parser.add_argument('type', help='specify build type as Release or Debug')
#optional argument, solver solver has just 1 component
parser.add_argument('projecName', help='specify the component to build')
args = parser.parse_args()
需要给出两个变量:
根据传入的type类型,确定build的类型
# 确定build的类型,默认为Release
buildType="Release"
if (args.type == "Release") or (args.type == "release"):
buildType = "Release"
elif (args.type == "Debug") or (args.type == "debug"):
buildType = "Debug"
elif (args.type == "Coverage") or (args.type == "coverage"):
buildType = "Coverage"
elif (args.type == "Coverage_Release") or (args.type == "coverage_release"):
buildType = "Coverage_Release"
else:
print("Error: ")
print(" Unrecognized build type " + args.type)
sys.exit("Aborted!")
使用subprocess.call命令调用脚本输出cmake版本
try:
# 这个方法的作用是执行一些命令行的命令,例如 sh xxx.sh , java -jar xxx.jar 等,(相当于执行shell命令?)
# 会开启一个子进程去执行,并且等待子进程结束才继续执行其他的,使用起来非常方便,就是需要注意一些小细节
# subprocess 模块允许我们启动一个新进程,并连接到它们的输入/输出/错误管道,从而获取返回值。
subprocess.call(['cmake', '--version'])
except OSError:
print("")
print("Error: ")
print(" Cannot find cmake")
sys.exit("Aborted!")
根据不同系统执行不同命令
通过if os.name == "posix": # posix指的是linux/MAC来判断系统,然后执行不同的命令
根据创建类型给出目录名称
installSubDir = "linux64" + suffix # 给出install目录
solverBuildDir = solverBuildDir + suffix # 给出build的目录,根据suffix的不同,会有不同的文件夹
buildInstallCMD = "make -j4 install" # 给出build的脚本命令
buildTestCMD = "make tests" # 给出test的命令
cmakeSLTN = "Unix Makefiles" # cmake使用的generator类型
确定编译器位置,lib库位置等
#compilers
#compilerPath = "/usr/local" #给出gcc的目录
compilerPath = "/usr/gcc/9.3.0" #gcc 9.3 #给出gcc路径
cCompiler = compilerPath + "/bin/gcc" # gcc位置
cxxCompiler = compilerPath + "/bin/g++"# g++位置
fCompiler = compilerPath + "/bin/gfortran" # gfortran位置
stdlibPath = compilerPath + "/lib64" # lib位置
makeCmd = "/usr/local/bin/make" # make的位置
if not os.path.isfile(makeCmd):# 如果make不在‘makeCmd’中的话
makeCmd = "/usr/bin/make" # 认为其在此处
check_file(makeCmd) # 判断其是不是file
# LIB的路径,为给出的gcc路径‘stdlibPath’和系统的lib路径,将`gcc`lib路径添加到系统`lib`路径中
os.environ["LD_LIBRARY_PATH"] = stdlibPath + ":" + os.environ["LD_LIBRARY_PATH"]
其他命令和目录等
cmakeSLTN = """ + cmakeSLTN + """ + archSLTN # 添加双引号
installDir = os.path.join(projRoot, "install") # 添加install目录
buildDir = os.path.join(projRoot, "build") # 添加builds目录
sourceDir = os.path.join(projRoot) # 确定CMakeLists.txt文件目录
ProjectSourceDir = os.path.join(sourceDir)
生成目录
os.makedirs(buildDir) # 创建builds的子目录
检查目录
# 检查目录,这些目录需要提前创建好
check_dir(buildDir)
check_dir(installDir)
check_dir(sourceDir)
print("")
print("----------------------------------------------")
print("project root dir: ", projRoot)
print("build type : ", buildType)
print("install dir : ", installDir)
开始组装最后的cmake命令
actualInstall = os.path.join(installDir, installSubDir)
configCMD = "cmake -D CMAKE_INSTALL_PREFIX:PATH=" + actualInstall
if os.name == "posix":
configCMD = configCMD + " -D CMAKE_BUILD_TYPE:STRING=" + buildType # build类型
configCMD = configCMD + " -D CMAKE_C_COMPILER:PATH=" + cCompiler # c编译器位置
configCMD = configCMD + " -D CMAKE_CXX_COMPILER:PATH=" + cxxCompiler # c++编译器位置
开始执行build过程
将当前目录转到builds下的某个项目目录下:
# 判断是否存在mfe的目录
if not (os.path.exists(mfeSourceDir)):
sys.exit("multife source folder does not exist!")
if os.path.exists(mfeBuildDir):
print()
print("Warning: folder " + mfeBuildDir + " exists, deleting")
print()
shutil.rmtree(mfeBuildDir)
os.makedirs(mfeBuildDir) # 创建builds的子目录
os.chdir(mfeBuildDir) # os.chdir() !!!方法用于改变当前工作目录到指定的路径。
添加额外的build命令
# 结合cmake的-D指令给出build的额外指令
mfeCMD = configCMD # 给出build的命令
mfeCMD = mfeCMD + " -G " + cmakeSLTN
mfeCMD = mfeCMD + " " + ProjectSourceDir # 给出source的目录
print("------------")
mfeCMD1="cmake ../ "
subprocess.call(mfeCMD, shell=True) #启动shell脚本,执行mfeCMD指令,即‘cmake xxx‘指令
print("------------")
subprocess.call(buildInstallCMD, shell=True) # 即'make xxx'指令
执行设定好的mfeCMD脚本命令
print("------------")
subprocess.call(mfeCMD, shell=True) #启动shell脚本,执行mfeCMD指令,即‘cmake xxx‘指令
print("------------")
subprocess.call(buildInstallCMD, shell=True) # 即'make xxx'指令
最后形成的cmake指令
上面的所有一切python脚本需要gdb模块python脚本需要gdb模块,最终结果都体现在最后的cmake命令上:
'cmake
-D CMAKE_INSTALL_PREFIX:PATH=/media/linux/cplus/cmake-tutorial-bilibili/buildScriptExample_blog/install/linux64_dbg # 生成的install路径
-D CMAKE_BUILD_TYPE:STRING=Debug #生成项目build类型
-D CMAKE_C_COMPILER:PATH=/usr/bin/gcc #找到的gcc编译器位置
-D CMAKE_CXX_COMPILER:PATH=/usr/bin/g++ #找到的g++编译器位置
-G "Unix Makefiles" /media/cplus/cmake-tutorial-bilibili/buildScriptExample_blog' #generator类型
这样就把命令交给cmake去执行了,完成了python控制编译的任务。
总结使用python脚本生成cmake的指令从而控制cmake的编译最终,我们的设置都会变成cmake的指令去交给cmake执行