動態(tài)庫的創(chuàng)建、引用及工程間相互依賴關(guān)系的pro文件編寫方法
采用一個非常簡單的Qt程序作為例子,通過pro文件的合理編寫,使得我們的程序在使用動態(tài)庫的時候,幾乎可以忽略掉動態(tài)庫的存在。它包括3部分:
生成動態(tài)庫使用動態(tài)庫生成與使用的自動化
測試環(huán)境:
ubuntu 12.04 + Qt 5.9.4windows vista + Qt 5.9.4(MinGW)引子
一個非常非常簡單的Qt的小程序,是吧?
widget.h
#ifndef?WIDGET_H #define?WIDGET_H #includeclass?Widget:public?QWidget { ????Q_OBJECT public: ????Widget(QWidget?*?parent=0); }; #endif?//?WIDGET_H
widget.cpp (本文件內(nèi)容不變)
#include?"widget.h" Widget::Widget(QWidget?*parent) ????:QWidget(parent) { }
main.cpp (本文件內(nèi)容不變)
#include#include?"widget.h" int?main(int?argc,?char?**argv) { ????QApplication?app(argc,?argv); ????Widget?w; ????w.show(); ????app.exec(); }
這個程序是如此的簡單,我們都能很輕易地寫出需要的pro文件
HEADERS?+=?widget.h SOURCES?+=?main.cpp?widget.cpp
然后qmake,make即可得到結(jié)果。
可是,你想過么:如果不想讓我們的程序鐵板一塊,分成幾個動態(tài)庫(共享庫)會怎么樣呢,pro文件又該如何寫?
如何做?(一)源碼分開放置
既然要準備用動態(tài)庫了,庫的源碼和程序的源碼還是分開放置吧?
將源文件放到不同的路徑下src/main.cpplibwidget/widget.hlibwidget/widget.cpp
我們知道qmake不如cmake那么強大,它的每個project只能有一個目標,要么是庫,要么是可執(zhí)行程序。當目標多于一個時,只能用 subdirs 這個TEMPLATE,于是,
我們需要3個xxx.pro文件project.prosrc/src.prolibwidget/libwidet.pro
可以確定,project.pro 文件沒有什么懸念:
project.pro (本文件內(nèi)容不變)
TEMPLATE=subdirs CONFIG?+=?ordered SUBDIRS?+=?libwidget?src
如何做?(二)生成動態(tài)庫
使用動態(tài)庫,當務(wù)之急是生成動態(tài)庫。
如果我們不在windows下使用,一切都會比較簡單,源代碼也不需要改動。
在windows下,動態(tài)庫導出的東西需要使用?__declspec(dllexport)。
我們需要兼顧不同的平臺,幸好Qt有解決方案,改造后的widget.h文件如下:
widget.h (本文件內(nèi)容后續(xù)不再改變)
#ifndef?WIDGET_H #define?WIDGET_H #include#if?defined(LIBWIDGET_BUILD) #??define?WIDGET_API?Q_DECL_EXPORT #else #??define?WIDGET_API?Q_DECL_IMPORT #endif class?WIDGET_API?Widget:public?QWidget { ????Q_OBJECT public: ????Widget(QWidget?*?parent=0); }; #endif?//?WIDGET_H
然后寫寫 libwidget.pro 文件:
TEMPLATE?=?lib TARGET?=?widget DEFINES?+=?LIBWIDGET_BUILD SOURCES?+=?widget.cpp HEADERS?+=?widget.h
這樣一來,確實可以生成動態(tài)庫了??墒强傆X得不太好:
首先,windows下debug和release的動態(tài)庫是不兼容的,取同一個名字(TARGET=widget)會不會有潛在的問題?其次,生成的庫放到那個路徑下呢?程序鏈接和運行時如何找到它?
暫且存疑,我們先看看其他
如何做?(三)使用動態(tài)庫
看看可執(zhí)行程序的生成,它要使用我們前面的庫,那么:
編譯預(yù)處理時需要找到頭文件連接時需要找到庫文件(庫文件在那個目錄下,叫什么名字)運行時能夠找到動態(tài)庫
src/src.pro 文件可以就寫成這個樣子了:
TEMPLATE=app INCLUDEPATH?+=?../libwidget LIBS?+=?-LThePathWePutLib?-lwidget SOURCES?+=?main.cpp
先不考慮運行時的情況。頭文件和庫文件都和前面的libwidget直接相關(guān),怎么構(gòu)建自動化呢?比如:庫文件的名字改動了?庫文件的存放目錄變了?...
如何做?(四)構(gòu)建自動化
我們構(gòu)建動態(tài)庫的時候,可以控制動態(tài)庫的名字,可以控制存放目錄,那么,我在講動態(tài)庫的這部分設(shè)置獨立出來不就行了:恩,使用一個 libwidget/libwidget.pri 文件。l由于src/src.pro和libwidget/libwidget.pro共用這個文件,還需要一個開關(guān)來進行區(qū)分(這就是widget-buildlib):
INCLUDEPATH?+=?$$PWD TEMPLATE?+=?fakelib LIBWIDGET_NAME?=?$$qtLibraryTarget(widget) TEMPLATE?-=?fakelib !widget-buildlib{ ????LIBS?+=?-L$$PROJECT_LIBDIR?-l$$LIBWIDGET_NAME }else{ ????SOURCES?+=?widget.cpp ????HEADERS?+=?widget.h }
注意:這兒庫目錄用一個變量PROJECT_LIBDIR表示(你這兒可以直接換成存放庫的目錄),具體稍后解釋。這兒的庫的名字使用qtLibraryTarget進行生成(這樣可以確保windows下debug模式生成的動態(tài)庫可以自動加個d),fakelib是用來哄騙qtibraryarget的,不然它只在TEMPLATE為lib是生效。
?
這樣,可執(zhí)行程序的生成時,它要使用我們前面的庫,只需要包括進來libwidget.pri,于是:
src/src.pro 文件可以就寫成這個樣子了:
TEMPLATE=app include(../libwidget/libwidget.pri) SOURCES?+=?main.cpp
相應(yīng)地,libwidget/libwidget.pro 可以修改如下:
TEMPLATE?=?lib CONFIG?+=?widget-buildlib include(libwidget.pri) TARGET?=?$$LIBWIDGET_NAME CONFIG?+=?debug_and_release?build_all DEFINES?+=?LIBWIDGET_BUILD
如何做?(五)運行自動化
現(xiàn)在似乎一切都比較正常了,可是有一點,我們要將生成的庫文件放到什么地方呢?才能使得運行時都能被找到(就像沒使用動態(tài)庫一樣,點擊IDE中的run或者去目錄下雙擊即可運行)
我們需要:
將庫文件放到 lib目錄下將可執(zhí)行文件放到 bin目錄下windows下將 xxx.dll 也放到bin目錄下
恩,這兩個目錄對整個工程比較通用,我們可以考慮建立一個 common.pri 文件:
common.pri 內(nèi)容 (本文件內(nèi)容后續(xù)不再改變)
PROJECT_BINDIR?=?$$PWD/bin PROJECT_LIBDIR?=?$$PWD/lib
然后libwidget/libwidget.pri 包含該common.pri 文件
libwidget/libwidget.pri (本文件內(nèi)容后續(xù)不再改變)
INCLUDEPATH?+=?$$PWD DEPENDPATH?+=?$$PWD TEMPLATE?+=?fakelib LIBWIDGET_NAME?=?$$qtLibraryTarget(widget) TEMPLATE?-=?fakelib include(../common.pri) !widget-buildlib{ ????LIBS?+=?-L$$PROJECT_LIBDIR?-l$$LIBWIDGET_NAME }else{ ????SOURCES?+=?widget.cpp ????HEADERS?+=?widget.h }
完整版的 libwidget/libwidget.pro 文件 (本文件內(nèi)容后續(xù)不再改變)
TEMPLATE?=?lib CONFIG?+=?widget-buildlib include(libwidget.pri) TARGET?=?$$LIBWIDGET_NAME DESTDIR?=?$$PROJECT_LIBDIR win32{ ????DLLDESTDIR?=?$$PROJECT_BINDIR ????QMAKE_DISTCLEAN?+=?$$PROJECT_BINDIR/$${LIBWIDGET_NAME}.dll } CONFIG?+=?debug_and_release?build_all DEFINES?+=?LIBWIDGET_BUILD
注意:這兒我們指定了庫文件的目錄,并會將dll拷貝到了PROJECT_BINDIR目錄
完整版的 src/src.pro 文件 (本文件內(nèi)容后續(xù)不再改變)
TEMPLATE=app include(../libwidget/libwidget.pri) DESTDIR?=?$$PROJECT_BINDIR unix:QMAKE_RPATHDIR+=$$PROJECT_LIBDIR SOURCES?+=?main.cpp
注意:這兒我們對unix下,指定了rpath,使得程序運行時不許設(shè)置可以即可找到動態(tài)庫