動(dòng)態(tài)庫的創(chuàng)建、引用及工程間相互依賴關(guān)系的pro文件編寫方法
采用一個(gè)非常簡單的Qt程序作為例子,通過pro文件的合理編寫,使得我們的程序在使用動(dòng)態(tài)庫的時(shí)候,幾乎可以忽略掉動(dòng)態(tài)庫的存在。它包括3部分:
生成動(dòng)態(tài)庫使用動(dòng)態(tài)庫生成與使用的自動(dòng)化
測試環(huán)境:
ubuntu 12.04 + Qt 5.9.4windows vista + Qt 5.9.4(MinGW)引子
一個(gè)非常非常簡單的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(); }
這個(gè)程序是如此的簡單,我們都能很輕易地寫出需要的pro文件
HEADERS?+=?widget.h SOURCES?+=?main.cpp?widget.cpp
然后qmake,make即可得到結(jié)果。
可是,你想過么:如果不想讓我們的程序鐵板一塊,分成幾個(gè)動(dòng)態(tài)庫(共享庫)會(huì)怎么樣呢,pro文件又該如何寫?
如何做?(一)源碼分開放置
既然要準(zhǔn)備用動(dòng)態(tài)庫了,庫的源碼和程序的源碼還是分開放置吧?
將源文件放到不同的路徑下src/main.cpplibwidget/widget.hlibwidget/widget.cpp
我們知道qmake不如cmake那么強(qiáng)大,它的每個(gè)project只能有一個(gè)目標(biāo),要么是庫,要么是可執(zhí)行程序。當(dāng)目標(biāo)多于一個(gè)時(shí),只能用 subdirs 這個(gè)TEMPLATE,于是,
我們需要3個(gè)xxx.pro文件project.prosrc/src.prolibwidget/libwidet.pro
可以確定,project.pro 文件沒有什么懸念:
project.pro (本文件內(nèi)容不變)
TEMPLATE=subdirs CONFIG?+=?ordered SUBDIRS?+=?libwidget?src
如何做?(二)生成動(dòng)態(tài)庫
使用動(dòng)態(tài)庫,當(dāng)務(wù)之急是生成動(dòng)態(tài)庫。
如果我們不在windows下使用,一切都會(huì)比較簡單,源代碼也不需要改動(dòng)。
在windows下,動(dòng)態(tài)庫導(dǎo)出的東西需要使用?__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
這樣一來,確實(shí)可以生成動(dòng)態(tài)庫了??墒强傆X得不太好:
首先,windows下debug和release的動(dòng)態(tài)庫是不兼容的,取同一個(gè)名字(TARGET=widget)會(huì)不會(huì)有潛在的問題?其次,生成的庫放到那個(gè)路徑下呢?程序鏈接和運(yùn)行時(shí)如何找到它?
暫且存疑,我們先看看其他
如何做?(三)使用動(dòng)態(tài)庫
看看可執(zhí)行程序的生成,它要使用我們前面的庫,那么:
編譯預(yù)處理時(shí)需要找到頭文件連接時(shí)需要找到庫文件(庫文件在那個(gè)目錄下,叫什么名字)運(yùn)行時(shí)能夠找到動(dòng)態(tài)庫
src/src.pro 文件可以就寫成這個(gè)樣子了:
TEMPLATE=app INCLUDEPATH?+=?../libwidget LIBS?+=?-LThePathWePutLib?-lwidget SOURCES?+=?main.cpp
先不考慮運(yùn)行時(shí)的情況。頭文件和庫文件都和前面的libwidget直接相關(guān),怎么構(gòu)建自動(dòng)化呢?比如:庫文件的名字改動(dòng)了?庫文件的存放目錄變了?...
如何做?(四)構(gòu)建自動(dòng)化
我們構(gòu)建動(dòng)態(tài)庫的時(shí)候,可以控制動(dòng)態(tài)庫的名字,可以控制存放目錄,那么,我在講動(dòng)態(tài)庫的這部分設(shè)置獨(dú)立出來不就行了:恩,使用一個(gè) libwidget/libwidget.pri 文件。l由于src/src.pro和libwidget/libwidget.pro共用這個(gè)文件,還需要一個(gè)開關(guān)來進(jì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 }
注意:這兒庫目錄用一個(gè)變量PROJECT_LIBDIR表示(你這兒可以直接換成存放庫的目錄),具體稍后解釋。這兒的庫的名字使用qtLibraryTarget進(jìn)行生成(這樣可以確保windows下debug模式生成的動(dòng)態(tài)庫可以自動(dòng)加個(gè)d),fakelib是用來哄騙qtibraryarget的,不然它只在TEMPLATE為lib是生效。
?
這樣,可執(zhí)行程序的生成時(shí),它要使用我們前面的庫,只需要包括進(jìn)來libwidget.pri,于是:
src/src.pro 文件可以就寫成這個(gè)樣子了:
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
如何做?(五)運(yùn)行自動(dòng)化
現(xiàn)在似乎一切都比較正常了,可是有一點(diǎn),我們要將生成的庫文件放到什么地方呢?才能使得運(yùn)行時(shí)都能被找到(就像沒使用動(dòng)態(tài)庫一樣,點(diǎn)擊IDE中的run或者去目錄下雙擊即可運(yùn)行)
我們需要:
將庫文件放到 lib目錄下將可執(zhí)行文件放到 bin目錄下windows下將 xxx.dll 也放到bin目錄下
恩,這兩個(gè)目錄對整個(gè)工程比較通用,我們可以考慮建立一個(gè) 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
注意:這兒我們指定了庫文件的目錄,并會(huì)將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,使得程序運(yùn)行時(shí)不許設(shè)置可以即可找到動(dòng)態(tài)庫