Linking against our sample library
Now, let's make an application that depends on our library. Our application will call the factorial function in the library, statically linking to the library in order to access the factorial function. To accomplish this, you need to perform the following steps:
- Select Close All Projects and Editors from the File menu.
- Choose New File or Project… from the File menu and create a new Qt console application called
MathFunctionsTest
using the wizard. - Right-click on MathFunctionsTest in the Project pane and click on Add Library.... You can now choose a library in your build tree, a library outside your build tree, an external library on your system such as the Unix math library,
fftmpeg
, or another library that you've created. Select External Library and click on Next. - Browse the library file that was built in the previous section by clicking on Browse, next to the line labelled Library file. It'll be in a folder named something such as
build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug
in your project's folder. Select theMathFunctions
library in either therelease
ordebug
folders—it doesn't matter which. The dialog box should look something similar to the following screenshot: - Browse the include files for your library by clicking on Browse next to Include path; this is the directory where you put the headers for your library.
- Choose static linking.
- Leave the other values set to their default values, click on Next, and then click on Finish.
Qt Creator will work its magic with your .pro
file, adding a LIBS
variable that includes the output of your library's build and an include path to your library's header files.
We can now call our factorial function. Edit main.cpp
to read the following code:
#include <QCoreApplication>
#include "mathfunctions.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug("6! is %lu", MathFunctions::factorial(6));
return a.exec();
}
This code first includes our library header file. Note that if you compile the application after adding just the #include
declaration, you'll get autosuggest help for every element of the MathFunctions
library. This code uses qDebug
instead of the C standard library to process its console output.
Tip
qDebug()
actually has a stream-savvy implementation too. I could have written the qDebug
line as follows:
qDebug() << "6! is" << MathFunctions::factorial(6);
The code would have generated the same output. To do this, you'll need to be sure to include the line #include <QDebug>
.
Build and run the application now in the Debug mode; you should see a console window with the text 6! is 720. Now, try to build and run the library in the Release mode… wait, why is the debugging output from qDebug
still there?
qDebug
isn't really a debugging log; it's an output stream for debugging information regardless of build levels. If you want to turn off its output in release builds, you'll need to edit the .pro
file. Double-click on your .pro
file and add the following line:
CONFIG(release, debug|release): DEFINES += QT_NO_DEBUG_OUTPUT
This is another scope; it says that if your build configuration is release
, add the QT_NO_DEBUG_OUTPUT
preprocessor definition to the list of preprocessor definitions for the project.
Now, if you rebuild (you don't just choose build, but actually choose rebuild because you want a clean build through the entire system) and run in the release mode, you won't see any output.
Tip
Qt actually defines four output streams, one for debugging messages and one for bonafide warnings. Use qDebug
for regular logging and qWarning
to output messages of a higher priority. There's also qCritical
and qFatal
for high-priority log messages that will indicate critical failures or failures that cause the application to terminate. You can also turn off warnings in release builds in the same way; simply add the following to your .pro
file:
CONFIG(release, debug|release): DEFINES += QT_NO_WARNING_OUTPUT
What will you do if you want to add files to your project? You can either do this by manually editing the .pro
file—which can be faster if you're a good typist, but it is also error-prone and can result in weird build problems if you mess up—or by right-clicking on your project and selecting either Add New… or Add Existing Files…. The Add New… option opens up a short wizard with choices such as these:
- C++ header and source files
- Qt Designer forms, which we'll talk about in the next chapter
- Qt resource files, which we'll talk about in the next chapter
- Qt Meta-object Language (QML) files
- JavaScript files (which can contain the code implementing the logic of a Qt Quick application)
- OpenGL shaders for fragments or vertices in either full OpenGL or OpenGL/ES
- Text files (such as a
Readme
file for your project) or a scratch file to use as a place to stash temporary clipboard items until you're done with an editing session
Before we move on to the important topic of debugging, let's take a look at one more .pro
file: the .pro
file for our application:
#------------------------------------------------- # # Project created by QtCreator 2013-07-23T20:43:19 # #------------------------------------------------- QT += core QT -= gui CONFIG(release, debug|release): DEFINES += QT_NO_DEBUG_OUTPUT TARGET = MathFunctionsTest CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/release/ -lMathFunctions else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/debug/ -lMathFunctions else:unix: LIBS += -L$$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/ -lMathFunctions INCLUDEPATH += $$PWD/../MathFunctions DEPENDPATH += $$PWD/../MathFunctions win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/release/libMathFunctions.a else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/debug/libMathFunctions.a else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/release/MathFunctions.lib else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/debug/MathFunctions.lib else:unix: PRE_TARGETDEPS += $$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/libMathFunctions.a
Phew! That's pretty dense. Let's see if we can unravel it. It begins by telling the build system that we use QtCore, but not QtGui. Next up is the instruction to disable the qDebug
messages in release builds, which won't happen by default. The TARGET
, CONFIG
, and TEMPLATE
options together say that we're building a console application with the name MathFunctionsTest
. The next line indicates that we have one source file, main.cpp
.
The next set of scopes indicate the path to our library and handle the fact that our libraries are in different directories on Windows for release and debug—this is different from Unix systems, where there is only one build variant of the library. After this come the INCLUDEPATH
and DEPENDPATH
variables, which indicate that there are library headers in the MathFunctions
directory and that the application depends on those headers; so, if the timestamps on the headers change, the binary should be rebuilt.
The final scope specifies the same dependency on the output library itself; if the library changes, the application executables have to be rebuilt. This is especially important because that way, we can run multiple copies of Qt Creator, edit our library and application files separately, and rebuild the bits we need either after they change, as well as all the dependencies get figured out and the right bits get built automatically.