|
|
(18 intermediate revisions by 5 users not shown) |
Line 1: |
Line 1: |
| {{Template:I18n/Language Navigation Bar|Development/Tutorials/Creating Libraries}} | | {{Moved To Community | Guidelines_and_HOWTOs/CMake/Library}} |
| | |
| ==Abstract==
| |
| | |
| 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.
| |
| | |
| ==Separating the 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 <tt>myshare</tt>, 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.
| |
| | |
| | |
| ==Adding the library to the buildsystem==
| |
| | |
| You also need a file CMakeLists.txt in the same directory as the source files:
| |
| <code>
| |
| set( myshared_LIB_SRCS
| |
| myaclass.cpp
| |
| mybclass.cpp
| |
| myinternalcclass.cpp
| |
| myinternaldclass.cpp
| |
| )
| |
| | |
| kde4_add_library( myshared SHARED ${myshared_LIB_SRCS} )
| |
| | |
| target_link_libraries( myshared
| |
| ${KDE4_KDECORE_LIBS}
| |
| )
| |
| set_target_properties( myshared
| |
| PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION}
| |
| )
| |
| | |
| install( TARGETS myshared ${INSTALL_TARGETS_DEFAULT_ARGS} )
| |
| </code>
| |
| | |
| The instructions are similar to the ones for a program. But instead of calling <tt>kde4_add_executable()</tt> you use <tt>kde4_add_library()</tt> to register the library <tt>myshared</tt>. The parameter <tt>SHARED</tt> 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 <tt>target_link_libraries()</tt>.
| |
| | |
| The version number of the library is set by <tt>set_target_properties()</tt>.
| |
| | |
| And <tt>install()</tt> moves the library to a place where it can be loaded.
| |
| | |
| | |
| ==Declaring exported classes and methods==
| |
| | |
| 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:
| |
| | |
| <code cpp>
| |
| #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
| |
| </code>
| |
| | |
| This macro code defines the tag <tt>MYSHARED_EXPORT</tt> 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:
| |
| <code cppqt>
| |
| #include "myshared_export.h"
| |
| | |
| class MYSHARED_EXPORT MyAClass {
| |
| // class declaration as usual
| |
| };
| |
| </code>
| |
| | |
| And the same with the other class:
| |
| <code cppqt>
| |
| #include "myshared_export.h"
| |
| | |
| class MYSHARED_EXPORT MyBClass {
| |
| // class declaration as usual
| |
| };
| |
| </code>
| |
| | |
| {{warning|
| |
| Beware that header-only classes, even additionally based on templates, must not have an export tag. Their methods do not end as any symbol in the library. Instead all machine code is created on demand in the external calling software, if compiled.}}
| |
| | |
| | |
| ==Enabling others to compile using your library==
| |
| | |
| You need also tell the buildsystem to install the header files. Without this external code can not build, as it can not include the files and thus the class and methods declarations of your library. You do this by adding to the library's CMakeLists.txt:
| |
| | |
| <code>
| |
| set( myshared_LIB_HDRS
| |
| myshared_export.h
| |
| myaclass.h
| |
| mybclass.h
| |
| )
| |
| install( FILES ${myshared_LIB_HDRS}
| |
| DESTINATION ${INCLUDE_INSTALL_DIR}/myshared
| |
| COMPONENT Devel
| |
| )
| |
| </code>
| |
| | |
| Of course also myshared_export.h is installed, as it gets included by the headers of both classes.
| |
| | |
| External KDE code which should use the library's classes and functions now can include the headers by the lines:
| |
| | |
| <code cppqt>
| |
| #include <myshared/myaclass.h>
| |
| #include <myshared/mybclass.h>
| |
| </code>
| |
| | |
| ==Getting the version numbers right==
| |
| | |
| By installing the header files you are releasing your library officially. External code will rely on the interfaces declared by the headers. So you need to care for binary and behaviour compatibility if developing your library further. Any changes in the next release should be reflected in the version number, so external code gets compiled, linked and run with the matching version of your library.
| |
| | |
| As you already saw above the version of your library is defined by <tt>set_target_properties()</tt>:
| |
| <code>
| |
| set_target_properties( myshared
| |
| PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION}
| |
| )
| |
| </code>
| |
| | |
| So there are two different kind of versions.
| |
| | |
| === Defining the development version ===
| |
| The value of <tt>VERSION</tt> defines the development state of your library. It is good practice to use a pattern "major.minor.patch-level".
| |
| For each release you need to update or reset the different parts:
| |
| * Only bug fixes added: increase the patch-level, e.g. 2.3.10 -> 2.3.11
| |
| * New functionality added, while keeping the old functionality: increase the minor version, reset the patch-level, e.g. 2.3.10 -> 2.4.0
| |
| * Binary or behaviour changes of old functionality, regardless of new functionality: increase the major version number, e.g. 2.3.10 -> 3.0.0
| |
| This version ends in the name of the resulting library file, e.g. on Linux "libmyshared.so.2.3.10" or "libmyshared.2.3.10.so".
| |
| | |
| === Defining the ABI version ===
| |
| The value of <tt>SOVERSION</tt> defines the ABI variant of your library. It is a single always increasing integer, numbering the versions with non-backward-compatibel changes. So if you release a new version with some binary or behaviour changes you need to increase this version number by one, e.g. 2->3.
| |
| | |
| {{tip|
| |
| If you follow the rules given above for the <tt>VERSION</tt> version, the value of <tt>SOVERSION</tt> will of course match the major number of it. So with a <tt>VERSION</tt> of 2.3.10 the <tt>SOVERSION</tt> value would be 2. But this is no necessity.}}
| |
| | |
| This version ends in a reference with ABI versioning to the resulting library file, e.g. on Linux in the symbolic link "libmyshared.so.2", which points to the real library file.
| |
| | |
| ===Using the generic versions===
| |
| If you are developing your library inside a normal KDE module, e.g. kdeutils, and all new versions of your library are only released together with the usual KDE releases, of course following the policies regarding ABI as demanded, you do not need to take care of the versioning yourself. Just use the generic variables <tt>${GENERIC_LIB_VERSION}</tt> and <tt>${GENERIC_LIB_SOVERSION}</tt> as shown above. Using the generic version numbers also helps to identify a version of your library by the general KDE version.
| |
| | |
| E.g. for KDE 4.1 these variables are defined in kdelibs/cmake/modules/KDE4Defaults.cmake with
| |
| <code>
| |
| set(GENERIC_LIB_VERSION "4.1.0")
| |
| set(GENERIC_LIB_SOVERSION "4")
| |
| </code>
| |