undocumented qmake

9
Index Software Undocumented Qmake Last Update: 20060829 In July 2006, I changed jobs, and no longer use Qt at work. I intend to dabble a bit with Qt, but I won't be spending anything like as much time with it as previously. So I don't expect to be adding much to this page from now on. If you're using Qt, then the chances are you use qmake. It's possible to integrate Qt into other make systems, but for most people, it's a lot of hard work for little gain. Personally, I find qmake quite good, but it has one big problem. The documentation is incomplete. In addition to the documentation available in assistant, I've found it invaluable to search through the qmake source code to find out how things are done, and also to discover undocumented features. This web page is intended to document some of these features. The current version of Qt at the time of writing was Qt 3.3.4. My experience of Qt 4 is quite limited. However, I do have the impression that qmake has been substantially rewritten, and that the documentation is more exhaustive The other thing that I've found invaluable is to study the qmake.conf files in $QTDIR/mkspecs attentively. From studying these files, you can see how qmake does things by default, and you can adapt them to you needs if required. I've tested most of these things on Solaris, Linux and HPUX. I just about got most things to work, with some difficulty, on Windows 2000 using cygwin. Using $$ORIGIN Most unices allow you to specify the RPATH for applications. This is part of the path that will be searched for dynamic libraries. In addition, you can specify a relative path with the $ORIGIN variable. This is not an environment variable, it is part of the application's RPATH. For instance, say the application is installed in /opt/foo/bin/fooapp and there are dynamic libraries in /opt/foo/lib. You could specify the absolute path (/opt/foo/lib) in the application. Or alternatively you could specify the relative path with $ORIGIN/../lib. Enough background. How do you actually do this? On Solaris with Sun Studio, it would be achieved by adding R$ORIGIN/../lib to the link command. (g++ uses Wl,rpath, instead of R). The qmake variable for this is, naturally, QMAKE_RPATH. So what is the problem? Well, the makefile needs to contain 'R$$ORIGIN/../lib' (two dollars so that it doesn't take $ORIGIN for a make MACRO). If you are using Qt 3.3.8 (and I guess 4.x, but I haven't checked), then you can simply use \$$ORIGIN. But if you are using an older version of Qt (I've checked this with 3.1.1), then you have a problem. $$ORIGIN looks like a variable to qmake. I've tried all sorts of things to escape the dollars, ($$$$ORIGIN, \$\$ORIGIN, single quotes, double quotes ...). But always either the $$ORIGIN is treated as a variable and disappears, or else the escaping is too efficient, and the escape characters end up in the generated makefile. One solution is to set an ORIGIN environment variable: export ORIGIN=\$\$ORIGIN

Upload: testabc

Post on 16-Nov-2015

24 views

Category:

Documents


3 download

DESCRIPTION

ryjhtyjut

TRANSCRIPT

  • 3/26/2015 UndocumentedQmake

    http://paulf.free.fr/undocumented_qmake.html 1/9

    Index Software

    UndocumentedQmakeLastUpdate:20060829

    InJuly2006,Ichangedjobs,andnolongeruseQtatwork.IintendtodabbleabitwithQt,butIwon'tbespendinganythinglikeasmuchtimewithitaspreviously.SoIdon'texpecttobeaddingmuchtothispagefromnowon.

    Ifyou'reusingQt,thenthechancesareyouuseqmake.It'spossibletointegrateQtintoothermakesystems,butformostpeople,it'salotofhardworkforlittlegain.Personally,Ifindqmakequitegood,butithasonebigproblem.Thedocumentationisincomplete.Inadditiontothedocumentationavailableinassistant,I'vefounditinvaluabletosearchthroughtheqmakesourcecodetofindouthowthingsaredone,andalsotodiscoverundocumentedfeatures.Thiswebpageisintendedtodocumentsomeofthesefeatures.ThecurrentversionofQtatthetimeofwritingwasQt3.3.4.

    MyexperienceofQt4isquitelimited.However,Idohavetheimpressionthatqmakehasbeensubstantiallyrewritten,andthatthedocumentationismoreexhaustive

    TheotherthingthatI'vefoundinvaluableistostudytheqmake.conffilesin$QTDIR/mkspecsattentively.Fromstudyingthesefiles,youcanseehowqmakedoesthingsbydefault,andyoucanadaptthemtoyouneedsifrequired.

    I'vetestedmostofthesethingsonSolaris,LinuxandHPUX.Ijustaboutgotmostthingstowork,withsomedifficulty,onWindows2000usingcygwin.

    Using$$ORIGIN

    MostunicesallowyoutospecifytheRPATHforapplications.Thisispartofthepaththatwillbesearchedfordynamiclibraries.Inaddition,youcanspecifyarelativepathwiththe$ORIGINvariable.Thisisnotanenvironmentvariable,itispartoftheapplication'sRPATH.Forinstance,saytheapplicationisinstalledin/opt/foo/bin/fooappandtherearedynamiclibrariesin/opt/foo/lib.Youcouldspecifytheabsolutepath(/opt/foo/lib)intheapplication.Oralternativelyyoucouldspecifytherelativepathwith$ORIGIN/../lib.Enoughbackground.Howdoyouactuallydothis?OnSolariswithSunStudio,itwouldbeachievedbyaddingR$ORIGIN/../libtothelinkcommand.(g++usesWl,rpath,insteadofR).Theqmakevariableforthisis,naturally,QMAKE_RPATH.Sowhatistheproblem?Well,themakefileneedstocontain'R$$ORIGIN/../lib'(twodollarssothatitdoesn'ttake$ORIGINforamakeMACRO).

    IfyouareusingQt3.3.8(andIguess4.x,butIhaven'tchecked),thenyoucansimplyuse\$$ORIGIN.ButifyouareusinganolderversionofQt(I'vecheckedthiswith3.1.1),thenyouhaveaproblem.$$ORIGINlookslikeavariabletoqmake.I'vetriedallsortsofthingstoescapethedollars,($$$$ORIGIN,\$\$ORIGIN,singlequotes,doublequotes...).Butalwayseitherthe$$ORIGINistreatedasavariableanddisappears,orelsetheescapingistooefficient,andtheescapecharactersendupinthegeneratedmakefile.OnesolutionistosetanORIGINenvironmentvariable:

    exportORIGIN=\$\$ORIGIN

    http://paulf.free.fr/index.htmlhttp://paulf.free.fr/software.html

  • 3/26/2015 UndocumentedQmake

    http://paulf.free.fr/undocumented_qmake.html 2/9

    andthenusethatintheprojectfile:

    QMAKE_LFLAGS=L/bar/build/libR$$(ORIGIN)/../liblibrary=stlport4

    Anotherpossibility,abitcleaner,istobeattheqmakevariablesattheirowngame:

    DOLLAR=$QMAKE_LFLAGS=L/bar/build/libR$${DOLLAR}$${DOLLAR}ORIGIN/../liblibrary=stlport4

    Thefirstcurlybracesaren'tstrictlynecessary.Justimagineexplainingthatbytelephonesupport,"minusrdollardollardollardollardollardollar".Hmm!

    QMAKE_EXTRA_WIN_TARGETS,QMAKE_EXTRA_WIN_COMPILERS

    ThedocumentationcoversQMAKE_EXTRA_UNIX_TARGETSandQMAKE_EXTRA_UNIX_COMPILERS,butnottheirWINcounterparts.ThesecertainlydoexistforWindowsaswell.

    No.inheaderincludepath

    Add"no_include_pwd"toCONFIGandI"."willnolongerbeaddedtoINCPATH.Youmightwanttodothis,forinstance,ifyouhaveafilelikestring.hinyourproject,butyouwanttoincludethesystemversionbydefault.

    NoeliminationofduplicateentriesinLIBS

    Add"no_smart_library_merge"toCONFIG.

    Escaping'{'and'}'inaprojectfile

    Fromqtinterestmailinglist

    First,echo${0##*/}isthecorrectthingtouse.

    GettingpathsforMSSDKandDDK

    Fromqtinterestmailinglist

    Ithinkthatthisisalsoonan'undocumentedQt'wiki.

    #FindtheSDKSYSTEM_CMD=$$quote(regqueryHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MicrosoftSDK\Directories/v"InstallDir")SYSTEM_RET=$$system($${SYSTEM_CMD})FORLOOP=8910111213141516for(i,FORLOOP):SDKPATH+=$$member(SYSTEM_RET,$${i})SDKPATH=$$quote($${SDKPATH})

  • 3/26/2015 UndocumentedQmake

    http://paulf.free.fr/undocumented_qmake.html 3/9

    #FindthenewestDDKcontainingheaders/libsfor$${DDK_TARGET}DDK_TARGET=w2kSYSTEM_CMD=$$quote(regqueryHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WINDDK)SYSTEM_RET=$$system($${SYSTEM_CMD})FORLOOP=12111098765for(i,FORLOOP){REG_CURENTRY=$$member(SYSTEM_RET,$${i})count(REG_CURENTRY,1){SYSTEM_CMD2=$$quote(regquery$${REG_CURENTRY}/v"LFNDirectory")SYSTEM_RET2=$$system($${SYSTEM_CMD2})FORLOOP2=789101112131415CURDDKPATH=for(j,FORLOOP2){CURDDKPATH+=$$member(SYSTEM_RET2,$${j})}CURDDKPATH=$$quote($${CURDDKPATH})exists($${CURDDKPATH}/inc/$${DDK_TARGET}):DDKPATH=$$quote($${CURDDKPATH})}}

    Settingsystemdependentoptions

    Theexistingqmakesupportissufficientformostneeds,inparticular,thevalueoftheQMAKESPECenvironmentvariable.Thereare,however,afewcaseswherethismightnotbeenough,e.g.,ifyouneedtodistinguishbetweenLinuxonx86andPPC,orbetweenSolarisonSPARCandx86.Asanexample,onUnixsystems(possiblyincludingWindowswithcygwin),thenyoucoulduse

    SYSTEM=$$system(unamesp|sed's//_/g')

    Analternative[Qt3.3andlater]istouseproperties.Foreachsystemthatyouuse,youwouldissueacommandlike

    qmakesetSYSTEMLinuxPPC

    Youcouldthenuseinyourprojectfilesomethinglike

    CONFIG+=$$[SYSTEM]

    LinuxPPC{message(LinuxonPPC)}

    Theoneslightdrawbackofthisapproachisthatitrequireseachusertoperformthe'qmakeset'steptocorrectlyconfiguretheirenvironment.Usuallythiswouldfallslightlyoutofthescopeofasourcemanagementsystem.Thusyouwouldnotbeabletodosomethinglike"checkout&make".Ifyoudotakethissortofapproach,thenyoucanprotectyourselfagainsterrorslikethepropertynotbeingsetlikethis:

    isEmpty($$[FOO]){

  • 3/26/2015 UndocumentedQmake

    http://paulf.free.fr/undocumented_qmake.html 4/9

    error(YoumustrunqmakesetFOOwhateverbeforerunningqmake)}

    AddingtoINCPATHafter$QTDIR/include

    Inyour.profile,add

    QMAKE_INCDIR_QT+=my/path

    NotlinkingwiththeoutputofQMAKE_EXTRA_UNIX_COMPILERS

    qmakeassumesthattheoutputoftheextracompilersisdestinedtobelinkedintothefinallibraryorapplication.Ifthisisn'tthecase,thenyouneedtodosomethingtostopit.

    If"mycompiler"isthenameofyourcompilercompoundvariable,thenifyouhaveamember.CONFIGsettono_link,thentheoutputwon'tgetlinked.Example.

    mycompiler.output=${QMAKE_FILE_BASE}.outmycompiler.input=MYCOMPILER_INPUT_FILESmycompiler.commands=foo${QMAKE_FILE_NAME}mycompiler.CONFIG=no_linkQMAKE_EXTRA_UNIX_COMPILERS+=my_compiler

    Exampleswhenyoumightwanttodothisareforthingslikegeneratingdocumentation.

    Usingflexandbison

    qmakehasbuiltinsupportforlexandyacc.Unfortunately,itwon'tworkwithflexandbison.

    lex

    lexisquiteeasy,justset

    QMAKE_LEX=flex

    bison

    bisonisanotherstory.Withabitofeffort,though,itcanbecoerced.Thefirststepiseasy,likelex.

    QMAKE_YACC=bison

    Afterthat,we'reintrouble.yaccgeneratesfiles"y.tab.h"and"y.tab.c".Qmakeaddstothemakefileruleacommandthatmovesthesefilesto"_yacc.h"and"_yacc.c".

    bisongenerates".Y.tab.c"and".Y.tab.h"sothemovestagefails,andalsoevidentlysodoesthecompile.

    SoIusedQMAKE_YACCFLAGS_MANGLE,whicharen'tdocumented.Ithinkthatitissupposedtoallowyoutochangethewaythatvariablesarenames(defaultprefixis"yy_",defaultqmakeprefixis"".I'vekeptthisprefix,butforcedbisontogenerate"y.tab.h"and"y.tab.c",whichiswhatqmake

  • 3/26/2015 UndocumentedQmake

    http://paulf.free.fr/undocumented_qmake.html 5/9

    needsforthesubsequentrenameandthencompile.Phew!

    MysolutiononSolarisdidn'tworkonLinux.Thistime,Iusedthesamebisonoptiontocontroltheoutputfilename,butIalsoneededdtogeneratetheheaderfile.IalsohadtoclearQMAKE_YACC_HEADER/QMAKE_YACC_SOURCE.

    OnSolaris

    Usingthesfwversionofbison,1.35

    solariscc{QMAKE_YACCFLAGS_MANGLE=oy.tab.cp$base}

    OnLinux

    linuxg++{QMAKE_YACCFLAGS=doy.tab.cQMAKE_YACC_HEADER=QMAKE_YACC_SOURCE=}

    Addingadoxygentarget

    Fromqtinterestmailinglist

    Youcanatleastmake"customtargets",suchasfor'makestrip'.Seedocsaboutqmake.Notsurehowtorunthemautomaticallyafterlinkingthough.Example(forrunningdoxygenoverthesourceswith'makedoc'):

    #customtarget'doc'in*.profiledox.target=docdox.commands=doxygenDoxyfile\testddoxydoc/html/images||mkdirdoxydoc/html/images\cpdocumentation/images/*doxydoc/html/imagesdox.depends=

    ...#somewhereelseinthe*.profileQMAKE_EXTRA_UNIX_TARGETS+=dox

    Strippingexecutables

    Fromqtinterestmailinglist

    Youcanusesomethinglikethisinthe.profile(untested):

    unix:QMAKE_POST_LINK=strip$(TARGET)!unix:QMAKE_POST_LINK=xxx$(TARGET)

  • 3/26/2015 UndocumentedQmake

    http://paulf.free.fr/undocumented_qmake.html 6/9

    ReplacexxxwiththesamelinebutwithwhateverthestripcommandiscalledonWindows.

    Buildinginsubdirectories

    Perhapsthesimplestissomethinglike

    system(cdcontrib/sshaskpass&&$${MAKECMD})

    WhereyouwouldhavepreviouslysettheappropriateMAKECMD.

    Preinstallheaders

    Ihadalengthybattleatmyplaceofworkgettingsomethinglike"makeinstall_headers"towork(orevenbetter,tobedoneasaprerequisitebeforeanyothertargets,sojusttypingmakewillpreinstallanyexportedheaders).Why?Well,wehavealotofsourcethatincludesheadersinthecommonunixwayas#include,inparticularwhentheheadersaregoingtobeexported.Thisposesnoproblemforalibrarythathasallsuchheadersinadirectory"basedir/include/library",youcanjustaddINCLUDEPATH+=include,andallwillbewell.

    Whenthefilesaren'torganizedinthisway,therearethreepossibilities.

    1. Editthefiles.Notanoptionforme.2. Changethedirectorystructure(orfakeitwithlinks).Thiswouldworkinmostcases,butIhavea

    fewlibrariesthatdon'tconformtothepatternofonesourcedirectoryforone#includedirectory.3. Fightitoutwithqmake.

    Asyoumayhavegathered,it'snumberthreethatI'mgoingtopursuehere.I'vehadthreegoesatsolvingthisproblem,allwithvaryingdegreesofsuccess.Sadlynoneofthemseemstobe100%reliableonallplatforms.Iusuallyfindthatsolutions2and3takeafewiterationsofqmakeandmakebeforeeverythingsettlesdown(i.e.,theintermediatemocanduigeneratedfileshavebeengenerated).

    1.UsetheINSTALLSqmakevariable

    Thissoundslikeitfitsthebill,butunfortunately,thereareacoupleofgotchas,whichI'llcometoshortly.

    Here'showitworks.Youdefineacompoundvariable(Iusuallycallit"headers".Ifyouneedmorethanonedestination,you'llneedmorethanonecompoundvariable.Withinyourvariable,youdefinethepath(wherethefileswillgo)andthefiles(whichfilestocopy).

    Example.Thisassumesthatyou'vealreadydefinedINSTALLBASE,TARGET,VERSIONandHEADERS.

    headers.path=$$INSTALLBASE/include/$${TARGET}_$${VERSION}/$${TARGET}headers.files=$$HEADERSINSTALLS+=headersPRE_TARGETDEPS+=install_headers

    Nowforthegotchas.Firstofall,bydefaultqmakewillgenerateamakefilethatusesthebasicOScopyfunctiontoinstallthefiles(e.g.,cponUnixandcopyonWindowsdon'tknowforMac).Thiswillcausethedestinationfiletohaveitstimestampsettothetimethatitwascopied.Obviously,thisisacalamity

  • 3/26/2015 UndocumentedQmake

    http://paulf.free.fr/undocumented_qmake.html 7/9

    foraninterdependentsetoflibrariesbuiltwithmake.Theupdatedtimestampswillcausealotofunnecessarycompiles.Thereisasolution.QMAKE_COPYtotherescue!Simplysetthistosomethinglike"cpfp"andallwillbewell.

    OnWindows,Ihadabitofahardertime,andIendedupcreatingalittleshellscripttoovercomethefactthatthoughIwasrunningnmakewithincygwin,withinnmake,pathsallusebackslashes,whichcygwincopydoesn'tunderstand.Thescriptbasicallydoes

    cpfp`cygpathu"$@"`

    Nowforthesecondgotcha.Thisonehasnorealsolution.Myaimwastohave

    HEADERS=...INSTALLS=...asabove...PRE_TARGETDEPS+=install_headers

    AlliswelluntilyouwanttoaddanyuicormocgeneratedheaderstoINSTALLS.Theneverythinggoespearshaped.There'snoproblemputtingthegeneratedheadersbeforeinstall_headersinPRE_TARGETDEPS.Thereisaproblemwithacirculardependencythough.qmakeadds'all'tothedependenciesforINSTALLS,buttheuic/mocgeneratedheadersdependonthe.ui/.hfiles.Someversionsofmakewilljustcomplain,otherswillterminatewhentheyseeacirculardependency.

    Ithinkthatthebestsolutionwouldbetofixqmaketoallowustoturnoffthedependencyon'all'.Forexample

    headers.path=$$INSTALLBASE/include/$${TARGET}_$${VERSION}/$${TARGET}headers.files=$$HEADERSheaders.CONFIG+=no_allINSTALLS+=headersPRE_TARGETDEPS+=install_headers

    Note:youmaywanttohavedifferentvariablesforexportedandprivateheaders,e.g.,

    PRIVATE_HEADERS=...EXPORTED_HEADERS=...HEADERS=$$PRIVATE_HEADERS$$EXPORTED_HEADERS...headers.files=$$EXPORTED_HEADERS

    We'llhavetowaitandseeifTrolltechtakeitup(oranyoneelseforthatmatter).

    Becauseofthisproblem,IgaveupwiththissolutionwhereverIneedtoinstallgeneratedheaders.

    But,holdthepresses!

    1.aUseINSTALLSandpatchqmake

    Itookupmyownchallengeandmadeasmall(andasitturnsout,quitesimple)modtoqmake($QTDIR/qmake/generators/makefile.cp).Hereisthepatch.BeforeImadethispatch,IwasusingINSTALLSforprojectswhereuigeneratedheaderswerenotrequiredtobepublic.Iwasusingthemethodoutlinedinpoint2belowforthoselibrarieswhereIneededtoexportgeneratedheaders.NowI

    http://paulf.free.fr/makefile.cpp.diff

  • 3/26/2015 UndocumentedQmake

    http://paulf.free.fr/undocumented_qmake.html 8/9

    useINSTALLSforeverything.

    2.UseQMAKE_EXTRA_UNIX_TARGETS

    BasicallytheideaistoreinventtheINSTALLSmechanism,butwithoutthecirculardependencies.IwrotealittleTclscriptthattakesthedestinationpath,thenameofa.profiletogenerateandalistofheaders.Inmy.profileIput

    HEADERS_PATH=$$INSTALLBASE/include/$${TARGET}_$${VERSION}/$${TARGET}system(preinstallheaders.tcl$$HEADERS_PATHpih.pro$$HEADERS)include(pih.pro)

    Andthiswouldgenerate"pih.pro"containing

    mkdir.commands=$$QMAKE_MKDIR[pathtoinstallto]mkdir.target=[pathtoinstallto]iheader0.target=[pathtoinstallto]/header.hiheader0.commands=$$QMAKE_COPYsrc/header.h[pathtoinstallto]iheader0.depends=src/header.h...iheaderX...unix:QMAKE_EXTRA_UNIX_TARGETS+=mkdiriheader0...iheaderXPRE_TARGETDEPS+=[pathtoinstallto][pathtoinstallto]/header.h...

    Notverypretty,butitgotthejobdone.Iputthe"pih.pro"fileintheworkingdirectorywheretherewouldbesomeriskofoverwritingifIbuiltontwoplatformssimultaneously.Onesolutiontothiswouldbetoputthefileinaplatformspecificdirectory.Ontopofthat,whentheprojectfileincludespih.pro,themakefilewillcontainadependencybetweenpih.proanditself.Thismeansthatifyougoandbuildonadifferentplatform,makewillcauseqmaketoregeneratethemakefile(andpih.pro),whichisunnecessary.

    AnotherthingthatIneverbotheredtosolvewasifthefilesneedtobeinstalledtomorethanonedirectory,ifImadetwocallstopreinstallheaders.tcl,theywouldbothcreateextratargetsstartingwithiheader0,sothesecondonewouldhidethethird.Thisshouldbeeasyenoughtosolve,butasitonlyaffectedonelibraryforme,itwasn'tworththeeffort.

    Allthesame,Ithoughttomyselfthattheremustbesomethingprettier.So...

    3.UseQMAKE_EXTRA_UNIX_COMPILERS

    It'sprehapsnotthemostobviousthingtodo,usea'compiler'to'install'aheader.Butthereyougo.Here'sanexample.

    install_headers.output=[pathtoinstallto]/${QMAKE_FILE_BASE}.hinstall_headers.input=INSTALL_HEADERSinstall_headers.commands=$$QMAKE_COPY${QMAKE_FILE_NAME}$$make_dir.targetinstall_headers.CONFIG=no_link

    QMAKE_EXTRA_UNIX_TARGETS+=make_dir

  • 3/26/2015 UndocumentedQmake

    http://paulf.free.fr/undocumented_qmake.html 9/9

    QMAKE_EXTRA_UNIX_COMPILERS+=install_headers

    INSTALL_HEADERS=$$HEADERS$$GENERATED_HEADERS

    PRE_TARGETDEPS+=$$make_dir.target$$UIDIR$$GENERATED_HEADERS$$INST_HDRS$$INST_GEN_HDRS

    Afewcommentsareinorder.Firstly,'make_dir'isthesamethingasinpoint2above.It'stheretoensurethatthedestinationdirectoryexistsbeforeanyattemptatinstallingfiles.There'snoneedfora.dependsfield.Iuse${QMAKE_FILE_BASE}.hfortheoutputsince${QMAKE_FILE_NAME}mayincludeanundesirablerelativepath.make_dirisatarget,justlikemkdirinthepih.profileinpart2above.$$UIDIRneedstobeinthePRE_TARGETDEPSbefore$$GENERATED_HEADERS,sincethatiswheretheygetput.$$GENERATED_HEADERSneedstobebefore$$INST_GEN_HDRSsincetheheadersneedtobegeneratedbeforetheycanbeinstalled!

    CopyrightPaulJohnFloyd20052007,2009

    http://validator.w3.org/check/referer