摘要

If a part of your code could be used by more than one software module, like other programs or plugins, you should put that part in a shared library. This tutorial tells how to add the library to the buildsystem and how to prepare the source code.

分离代码

It is good practise to put all source files for a library in a separate directory.

This tutorial assumes you want to create a shared library named myshare, which contains the classes MyAClass and MyBClass. Both classes use internally the classes MyInternalCClass and MyInternalDClass, but these are not found in their public interfaces.

The classes are declared and defined in the files myaclass.h, myaclass.cpp, mybclass.h, mybclass.cpp, myinternalcclass.h, myinternalcclass.cpp, myinternaldclass.h, and myinternaldclass.cpp.

想构建系统中添加库

You also need a file CMakeLists.txt in the same directory as the source files:
include_directories(

The instructions are similar to the ones for a program. But instead of calling kde4_add_executable() you use kde4_add_library() to register the library myshared. The parameter SHARED is needed as this declares the library is a shared one, not a static.

Like with a program, other libraries that are used have to be defined with target_link_libraries().

The version number of the library is set by set_target_properties().

And install() moves the library to a place where it can be loaded.

声明导出的类和方法

If a library gets compiled, all functions or class methods it offers get listed as so called symbols in a table. Each symbol points to the offset in the library where the corresponding code can be found.

Adding also functions or class methods to the table which are not intended to be called from the outside is of no use. It just bloats the table and asks others to make calls to them against your will. For the library myshared you do not want to add the methods of the classes MyInternalCClass and MyInternalDClass.

In the KDE buildsystem you need to put explicitly a tag to all the classes and functions which the library should provide externally. This tag has to have a different name for each library.

For this add a file myshared_export.h to the directory of the library, containing:

ifndef MYSHARED_EXPORT_H

define MYSHARED_EXPORT_H

// needed for KDE_EXPORT and KDE_IMPORT macros

include <kdemacros.h>

ifndef MYSHARED_EXPORT

if defined(MAKE_MYSHARED_LIB)

// We are building this library

define MYSHARED_EXPORT KDE_EXPORT

else

// We are using this library

define MYSHARED_EXPORT KDE_IMPORT

endif

endif

ifndef MYSHARED_EXPORT_DEPRECATED

define MYSHARED_EXPORT_DEPRECATED KDE_DEPRECATED MYSHARED_EXPORT

endif

endif

This macro code defines the tag MYSHARED_EXPORT for the library.

Now you prepare all classes by adding a include for the header with the library's export tag and putting the export tag defined above in front of the class name, like this:

#include "myshared_export.h"classMYSHARED_EXPORTMyAClass{// class declaration as usual};</code>Andthesamewiththeotherclass:<syntaxhighlightlang="cpp-qt">#include "myshared_export.h"classMYSHARED_EXPORTMyBClass{// class declaration as usual};</code>{{warning|Bewarethatheader-onlyclasses,evenadditionallybasedontemplates,mustnothaveanexporttag.Theirmethodsdonotendasanysymbolinthelibrary.Insteadallmachinecodeiscreatedondemandintheexternalcallingsoftware,ifcompiled.}}==使其他程序能使用你的库==Youneedalsotellthebuildsystemtoinstalltheheaderfiles.Withoutthisexternalcodecannotbuild,asitcannotincludethefilesandthustheclassandmethodsdeclarationsofyourlibrary.Youdothisbyaddingtothelibrary'sCMakeLists.txt:<code>set(myshared_LIB_HDRSmyshared_export.hmyaclass.hmybclass.h)install(FILES${myshared_LIB_HDRS}DESTINATION${INCLUDE_INSTALL_DIR}/mysharedCOMPONENTDevel)</code>Ofcoursealsomyshared_export.hisinstalled,asitgetsincludedbytheheadersofbothclasses.ExternalKDEcodewhichshouldusethelibrary'sclassesandfunctionsnowcanincludetheheadersbythelines:<syntaxhighlightlang="cpp-qt">#include <myshared/myaclass.h>#include <myshared/mybclass.h></code>==获得正确的版本号==Byinstallingtheheaderfilesyouarereleasingyourlibraryofficially.Externalcodewillrelyontheinterfacesdeclaredbytheheaders.Soyouneedtocareforbinaryandbehaviourcompatibilityifdevelopingyourlibraryfurther.Anychangesinthenextreleaseshouldbereflectedintheversionnumber,soexternalcodegetscompiled,linkedandrunwiththematchingversionofyourlibrary.Asyoualreadysawabovetheversionofyourlibraryisdefinedby<tt>set_target_properties()</tt>:<code>set_target_properties(mysharedPROPERTIESVERSION${GENERIC_LIB_VERSION}SOVERSION${GENERIC_LIB_SOVERSION})</code>Sotherearetwodifferentkindofversions,thedevelopmentversion<tt>VERSION</tt>andtheABIversion<tt>SOVERSION</tt>.Bothwillbeexplainednow.===定义开发版本===Thevalueof<tt>VERSION</tt>definesthedevelopmentstateofyourlibrary.Itisgoodpracticetouseapattern"major.minor.patch-level".Foreachreleaseyouneedtoupdateorresetthedifferentparts:*Onlybugfixesadded:increasethepatch-level,e.g.2.3.10->2.3.11*Newfunctionalityadded,whilekeepingtheoldfunctionality:increasetheminornumber,resetthepatch-level,e.g.2.3.10->2.4.0*Binaryorbehaviourchangesofoldfunctionality,regardlessofanynewfunctionality:increasethemajornumber,resettheminornumberandthepatch-level,e.g.2.3.10->3.0.0Thisversionendsinthenameoftheresultinglibraryfile,e.g.onLinux"libmyshared.so.2.3.10"or"libmyshared.2.3.10.so".===定义ABI版本===Thevalueof<tt>SOVERSION</tt>definestheABIvariantofyourlibrary.Itisasinglealwaysincreasinginteger,numberingtheversionswithnon-backward-compatibelchanges.Soifyoureleaseanewversionwithsomebinaryorbehaviourchangesyouneedtoincreasethisversionnumberbyone,e.g.2->3,otherwiseyoukeepit.{{tip|Ifyoufollowtherulesgivenaboveforthe<tt>VERSION</tt>version,thevalueof<tt>SOVERSION</tt>willofcoursematchthemajornumberofit.Sowitha<tt>VERSION</tt>of2.3.10the<tt>SOVERSION</tt>valuewouldbe2.Butthisisnonecessity.}}ThisversionendsinareferencewithABIversioningtotheresultinglibraryfile,e.g.onLinuxinthesymboliclink"libmyshared.so.2",whichpointstothereallibraryfile.TheseversionedreferencesareusedbythedynamiclinkertoloadtheversionofthelibrarywiththematchingABIvarianttoarunningexecutable.ExecutableswhicharelinkedtoasharedlibraryhaveanotewhichABIvariantoftheselibrayisneeded.{{warning|ThisconceptofABIversioningbreaksiftheexecutablegotlinkedtoanewerversionofthelibrary(e.g.2.4.0)whichisbackward-compatibletoolderversions(all2.0-3.*)butadditionallyoffersnewfunctionalitythatalsogetsusedbytheexecutable.BecauseconformingtotheconcepttheolderversionshavethesameABIversionlikethenewerone(e.g.2).Soifonlyanolderversionofthelibraryisinstalled(e.g.2.1.8)theexecutableisstillstartedasthetestedprerequisites(ABIversion)arematched,butitwillcrashduetothemissingfunctionalityifthelibrarygetsfinallyloaded.Inacompilation-from-sourcecode-basedsystemthisjustshouldnothappen,unlessyoudowngradealibraryagain.Andinapackage-basedsystemdependencychecksonde-/installationsofpackageswhichalsocareforthe<tt>VERSION</tt>versionshouldpreventthis.Soyoumightalsoforgetaboutthisproblem.}}===使用通用的版本===IfyouaredevelopingyourlibraryinsideanormalKDEmodule,e.g.kdeutils,andallnewversionsofyourlibraryareonlyreleasedtogetherwiththeusualKDEreleases,ofcoursefollowingthepoliciesregardingABIasdemanded,youdonotneedtotakecareoftheversioningyourself.Justusethegenericvariables<tt>${GENERIC_LIB_VERSION}</tt>and<tt>${GENERIC_LIB_SOVERSION}</tt>asshownabove.UsingthegenericversionnumbersalsohelpstoidentifyaversionofyourlibrarybythegeneralKDEversion.E.g.forKDE4.1.0thesevariablesaredefinedinkdelibs/cmake/modules/KDE4Defaults.cmakewith<code>set(GENERIC_LIB_VERSION"4.1.0")set(GENERIC_LIB_SOVERSION"4")</code>==本地化工作==Ifyoursharedlibrarycontainstranslationsyouhavetohelpalittlesoaprogramwhichusesyourlibrarycanmakeuseofthem.Youmarkthestringstobetranslated[[Development/Tutorials/Localization/i18n|asusual]].And[[Development/Tutorials/Localization/i18nBuildSystems|likeyoudowithprograms]]youhavetoaddashellscriptnamed<tt>Messages.sh</tt>inthetopleveldirectoryofyoursharedlibrarytocollectallthestrings,e.g.:<code>#!bin/sh# fetch all strings in files that are not source code$EXTRACTRC`find.-name\*.rc-o-name\*.ui-o-name\*.kcfg`>>rc.cpp# fetch all strings in source code files$XGETTEXT`find.-name\*.cc-o-name\*.cpp-o-name\*.h`-o$podir/libmyshare.pot</code>Soallstringswillendinacatalognamed<tt>libmyshare</tt>.Whileforprogramsandmostpluginsthetranslationsareloadedautomatically,forsharedlibrariesyouhavetodoitmanually.Youdosobyaddingintheprogramafterthecreationofthe<tt>KApplication</tt>or<tt>KCoreApplication</tt>instancetheline:<codemode=cppqt>KGlobal::locale()->insertCatalog("libmyshare");</code>Themethod<tt>insertCatalog()</tt>willloadthetranslatedstringsinthecurrentlanguagefromthecatalognamed<tt>libmyshare</tt>.==用静态库组织你的程序代码==Sharingcodewithothersisonereasontoseparatecodeintolibraries.Anotherreasonistheneedtoorganizealargecodebase.Ausefulapproachtogiveastructuretocodeistoseparatethesourcefilesindifferentsubdirectories,eachsubdirectorycontainingonlycodeforacertaindomain,e.g.asubdirectory"utils"forallutilityclassesor"gui"forallguirelatedcode.InsteadofonebigcentralCMakeLists.txtfileyoualsouseanownCMakeLists.txtfilepersubdirectory,whichisalsoplacedinthatsubdirectory.IneachoftheseCMakeLists.txtfilesyouadvisethebuildsystemtoconstructaseparatepackageofthecompiledsourcesinthegivendirectory,asocalledconveniencelibraryor,intechnicalterms,staticlibrary.E.g.forthesubdirectory"myutils""myutils/CMakeLists.txt"lookslike:<code>include_directories(# only as needed for the sources of this directory/library)set(myutils_LIB_SRCSmyutilaclass.cppmyutilbclass.cppmyinternalutilcclass.cppmyinternalutildclass.cpp)kde4_add_library(myutilsSTATIC${myutils_LIB_SRCS})</code>Theinstructionsaresimilartotheonesforasharedlibrary.Inthecall<tt>kde4_add_library()</tt>youjustusetheparameter<tt>STATIC</tt>insteadtodeclarethelibraryisastaticone.Thislibraryisonlyusedasanintermediateproductinthetotalbuildprocess.Soyouneitherneedtosetaversionnumberwith<tt>set_target_properties()</tt>ordeclareatthispointotherlibrariestolinkagainstwith<tt>target_link_libraries()</tt>,likeyoudowithasharedlibrary.Andyoudonotinstallanything,neithertheliboranyheaderfiles.Youalsodonotmarkexportedclassesorfunctions,allwillbeavailabletoexternalcode.Youdeclareanydynamiclibrariesusedbythelibraryatanotherplace,seebelow.With<tt>include_directories()</tt>youalsohavetolisttheincludedirectoriesneededforthesourcefilesinthedirectory,andonlythese.SoyougetmorecontrolwithregardtodependenciesthanwithonebigcentralCMakeLists.txtfile.TohaveallthelocalCMakeLists.txtfilesgetusedbythebuildsystemyouneedtoaddeachsubdirectorytotheCMakeLists.txtfileintheupperdirectory,withthecall<tt>add_subdirectory()</tt>,e.g.<code>add_subdirectory(myutils)add_subdirectory(mygui)</code>Thisinclusioncanbedonerecursively,soinasubdirectoryyoucanaddagainthesubsubdirectory.Finallyyouneedtotellthebuildsystemtoalsousethestaticlibrariestoconstructthecompleteprogram.Youdosobyincludingtheirnamesinthe<tt>target_link_libraries()</tt>callfortheprogram.Thereyoualsoaddanynotalreadylistedsharedlibrarywhichisusedbycodeinyourstaticlibraries,astoldabove.SotoconstructtheprogrammyprogramusingthestaticlibrariesmyguiandmyutilsyouwritetheCMakeLists.txtlikee.g.:<code>target_link_libraries(myprogrammyguimyutils# and all needed dynamic libraries, like the KDE ones and else,# also the ones needed by your static libraries mygui and myutils)</code>Makesurethatyoulistthoselibraries,staticorshared,whicharealsousedbyotherlibrariesinthelistbelowthese,e.g.ifmyguiusesmyutils,firstlistmygui,thenmyutils,asintheexampleabove.{{warning|ThebuildsystemuptoKDE4.1supportstheuseofstaticlibrariesonlyforprograms,notforsharedlibraries.Sousethisstructuringmethodonlyforprograms.Thisrestrictionisduetothefactthatthelinkerutilitiesonmostplatformssupportedonlyimportthosepartsofstaticlibrariesintotheendproductwhicharereferencedbytheothercodeintheproduct,notsimplyeverything.Whichisuseful,iftheproductisaprogram.Butitisn'tusefulfortheproductdynamiclibrary.Ifafunction"foo()"fromthestaticlibraryisnotusedbytheothercodeinthesharedlibrarythisfunctionwillnotbefoundinresultingsharedlibraryproduct.Yetthirdpartycodelinkingtothissharedlibrarymightexpectthefunctionifitisdefinedasexported.Whichcreatesaproblem.}}Itisnotuncommonforastaticlibrarytobecomeashareddynamiclibrarylaterindevelopment,ifthecodeinthelibraryprovestobecompleteandusefulforothers.