Terminology
The first hurdle to take was terminology. Apparently I was the only one in the world building DLLs on Linux as Google didn't give me any hope. But are Linux applications all statically built? No. I was wrong to make the assumption that terminology was the same across the OSs.
While in Windows world it's called DLLs, in Unix/Linux world the same thing is called 'shared libraries' and they usually have the .so
file extension.
That was an important breakthrough as that really helped me finding useful results on Google.
Note
In Windows we have DLLs, in Linux/Unix we have shared libraries. It's the same!
Creating the projects
Step 1: Using the wizard
We'll need at least two projects in Qt Creator; one will be the shared library, the other one the main()
and maybe some other classes.
This, of course, depends on the model you're trying to put life to.
- Create a new project.
- Choose 'Other Project' on the left and select C++ Libary.
- A wizard will open and select 'Shared Library' as the type. Fill in the name of your library and the (base) path to create it in.
- Select 'QtCore' as only required Qt module. (Don't know if this is really needed.)
- Create your first class from the wizard.
- Finish the wizard.
- Open the
.pro
file with the editor and notice the following:TEMPLATE
(should beLIB
).
Build (don't run) the project.
Now you should get .dll
along with .a
files on Windows (G++/MinGW setup) or .so
files on Linux (with a lot of symlinks) in the build directory (probably some shadow build folder—this is fine).
Step 2: Moving the shared libraries to a common folder
It's quite common to build libraries yourself and actually use them in another project at the same time.
One would need to make install
all of the libraries in order to use them.
For developing your libraries and their uses at the same time, this is unwanted.
In order to move the shared libraries (and the needed header files to interface with it) we'll use some QMake magic.
Open the .pro
file and append on the end of the file the following (example, customize to your own needs):
# Set the destination directory of the shared libraries
# MYDLLDIR is a name something I made up myself, DESTDIR is a QMake variable.
# $$IN_PWD expands to the directory your .pro file is in.
# This example copies the final build output to ../dlls (one level above your project).
MYDLLDIR = $$IN_PWD/../dlls
# We need quotes around the path in order to support whitespaces in the path
# for Windows. e.g. (''C:\Users\Gert van Dijk\...'').
# For some strange reason $$quote(...) does not seem to work for Windows here.
DESTDIR = \"$$MYDLLDIR\"
#
# Here's some magic to move all of the project's header files to the same DLL directory.
#
DDIR = \"$$MYDLLDIR/\"
SDIR = \"$$IN_PWD/\"
# Replace slashes in paths with backslashes for Windows
win32:file ~= s,/,\\,g
win32:DDIR ~= s,/,\\,g
win32:SDIR ~= s,/,\\,g
# For-loop to copy all header files to DDIR.
for(file, HEADERS) {
QMAKE_POST_LINK += $$QMAKE_COPY $$quote($${SDIR}$${file}) $$quote($$DDIR) $$escape_expand(\\n\\t)
}
In the directory dlls
(located one level above your project) you should now find the library along with the header files.
Hint
If you don't want to expose all of the HEADERS
, just specify your on list of headers and let the for-loop run on that.
This can be useful in cases that you want your internal library method calls to be completely closed and only expose some classes to interface with others.
Note
This whole magic could also be done using the 'Build Settings' and specifying custom build steps of your project, but this ends up in the .pro.user
file and this is not desiged to be shared with other developers.
Step 3: Creating a project using the shared library
Now that we output the library and their headers to some common folder, we can use that as the path for QMake to depend on when linking against the libraries in there.
You can leave the project of the shared library open or close it, it doesn't matter.
- Create a new project.
- Choose 'Other Project' on the left and select 'Qt Console application' (just an example).
- Fill in the name of your library and the (base) path to create it in.
- Use default build set up (both Release and Debug, enable shadow building).
- Create your first class from the wizard.
- Finish the wizard.
- Open the
.pro
file with the editor and notice the following:TEMPLATE
(should beapp
).
Now in order to make it dependent on the library, append the following to the .pro
file:
MYDLLDIR = $$IN_PWD/../dlls
# As our header files are in the same directory, we can make Qt Creator find it
# by specifying it as INCLUDEPATH.
INCLUDEPATH += $$MYDLLDIR
# Dependency to library domain (libdomain.so for Unices or domain.dll on Win32)
# Repeat this for more libraries if needed.
win32:LIBS += $$quote($$MYDLLDIR/domain.dll)
unix:LIBS += $$quote(-L$$MYDLLDIR) -ldomain
Now you should be able to build (not yet run) your project.
Note
To create libraries depending on other libraries, you simply specify the QMake LIBS
also in you shared library project.
If you fail to do so, you'll get runtime errors.
Step 4: Set up runtime linker paths
While it now builds, it needs to be able to find your shared libraries at runtime.
To make it search in the specified directory we will need to append it to the PATH
environment variable (Windows) or LD_LIBRARY_PATH
environment variable (Unices).
- Click on 'Projects' in the left toolbar in Qt Creator.
- Select the leaf project (the one that builds to the executable to run) at the top.
- Click on 'Run Settings'
- Somewhat below, you'll find 'Run Environment'. Expand it (click on 'Details').
- For Unices:
- Click on the button 'Add' to the right.
- Enter
LD_LIBRARY_PATH
as 'Variable' and../dlls
as Value.
- For Windows:
- Find
PATH
In the list of Variables. - Click 'Edit'.
- Add our path relative by appending
;..\dlls
.
- Find
Now you should be able to build and run your leaf project depending on a library!
Warning
Build order does matter. While you can specify build dependencies in Qt Creator in order to just hit "Build All", this didn't work out for me. Just build the libraries in the order it works for their dependencies, and your leaf project as the last one.