本文是介紹qmake的,不過要以cmake編譯嵌入python的C++程式開篇。
例子
簡簡單單,一個在C、C++中嵌入python的例子
#include "Python.h"int main(){ Py_Initialize(); PyRun_SimpleString("print \'Hello qmake!\'"); Py_Finalize(); return 0;}
這個例子嵌入一個python解譯器,執行一條python的print語句。程式運行結果:
"Hello qmake!"
如何編譯
g++ main.cpp -ID:\Python27\include D:\Python27\libs\python27.lib
讓人鬱悶的是,需要指定python的安裝路徑,而不同人安裝路徑和Python版本都可能不同,沒辦法直接寫成指令碼什麼的了
cmake
如果使用cmake的話,我們只需要這樣的工程檔案
cmake_minimum_required(VERSION 2.8)project(HELLO)find_package(PythonLibs REQUIRED)include_directories(${PYTHON_INCLUDE_DIRS})add_executable(hello main.cpp)
然後運行
cmake .make
就可以了,自始至終不需要我們指定python的路徑。很不錯哈
可是,我在用qmake而不是cmake啊,這可如何是好?
qmake
由於qmake提供了pkg_config支援,在linux下,我們很容易寫出下面的 xxx.pro 檔案。
CONFIG -= qtCONFIG += consoleunix{ CONFIG += link_pkgconfig PKGCONFIG += python}SOURCES += main.cpp
- 在 Windows 下,可沒有這麼幸運了,pkg_config 沒有市場。真要這麼寫麼
win32{ INCLUDEPATH += D:\\Python27\\include LIBS += D:\\Python27\\libs\python27.lib}
不甘心!!
Windows下如何找到Python
不妨考慮我們開始的那個例子:這個程式運行時,如何找到Python的那些模組的呢?
- 如果PYTHONHOME被設定,則使用
- 否則,按照可執行程式所在目錄向上搜尋 (lib/os.py)
- 否則,尋找註冊表
恩,對我們有用的1、3兩項,不過在Windows下,環境變數用的也不多啊。那就只剩查註冊表了
讀取註冊表?
翻遍qmake手冊,沒發現qmake提供操作註冊表的功能;看看qmake源碼,恩,還是沒有 ...
怎麼辦,看來只能用其他程式,或者寫個Windows預設支援的 .bat/.vbs/.js 指令碼?
還好,有個 reg.exe 的程式可以使用,命令列下運行看看:
C:\> reg query HKLM\SOFTWARE\Python\PythonCore\2.7\InstallPath /veHKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.7\InstallPath (預設) REG_SZ D:\Python27\
恩看起來還不錯,只要執行該命令,然後提取結果中REG_SZ後面的路徑就可了。
可是還是有個可惡的2.7啊?其他人可能在用Python2.6或2.5,額,都查一遍好了:
最終的代碼如下:
- 將可能的版本號碼存入列表 PY_VERSIONS
- 遍曆這個列表,檢查reg.exe的傳回值,非0則跳過
- 擷取reg.exe的標準輸出,用Regex提取路徑到 PY_HOME 中
- 判斷 Python.h 是否存在,不存在則跳過
- 將標頭檔路徑和庫檔案分別加入到 INCLUDEPATH 和 LIBS 中
- 注意,庫檔案分 PythonXX.lib 和 PythonXX_d.lib
- 結束
win32:{ PY_VERSIONS = 2.7 2.6 2.5 2.4 for(PY_VERSION, PY_VERSIONS){ system(reg query HKLM\\SOFTWARE\\Python\\PythonCore\\$$PY_VERSION\\InstallPath /ve) { PY_HOME = $$quote($$system(reg query HKLM\\SOFTWARE\\Python\\PythonCore\\$$PY_VERSION\\InstallPath /ve)) PY_HOME ~= s/.*(\\w:.*)/\\1 !exists($$PY_HOME\\include\\Python.h):next() INCLUDEPATH *= $$PY_HOME\\include PY_LIB_BASENAME = python$${PY_VERSION} PY_LIB_BASENAME ~= s/\\./ CONFIG(debug, debug|release):PY_LIB_BASENAME = $${PY_LIB_BASENAME}_d LIBS *= $$PY_HOME\\libs\\$${PY_LIB_BASENAME}.lib message(Python$$PY_VERSION found at $$PY_HOME) break() } }}