diff options
author | Ivan Komissarov <abbapoh@gmail.com> | 2023-07-20 17:17:43 +0300 |
---|---|---|
committer | Ivan Komissarov <ABBAPOH@gmail.com> | 2024-01-30 09:47:48 +0000 |
commit | 271e6c9365f61ec9463861a4f2fb1c4d7c6c2502 (patch) | |
tree | d9360cfd3e0a1acd65d95ebcd89733af052261d6 | |
parent | b38fad7c10d68ec76856b26ca520bd6746ddda20 (diff) |
Tutorial. Part 1
This change adds a step-by step tutorial that helps
to explain Qbs concepts and best practices for new users.
Change-Id: I5c669f8fa0f89b8300f241bb8e4ed7cd4b3bb4c6
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
35 files changed, 821 insertions, 2 deletions
diff --git a/doc/doc.qbs b/doc/doc.qbs index 03c82e18c..0dfc5f474 100644 --- a/doc/doc.qbs +++ b/doc/doc.qbs @@ -30,6 +30,7 @@ Project { "fixnavi.pl", "howtos.qdoc", "qbs.qdoc", + "tutorial.qdoc", "qbs-online.qdocconf", "config/*.qdocconf", "config/style/qt5-sidebar.html", diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc index 9ea88763e..76bbf2a61 100644 --- a/doc/qbs.qdoc +++ b/doc/qbs.qdoc @@ -70,6 +70,13 @@ \li \l{Special Property Values} \li \l{Module Providers} \endlist + \li \l{Tutorial} + \list + \li \l{tutorial-1.html}{Application} + \li \l{tutorial-2.html}{Static Library} + \li \l{tutorial-3.html}{Dynamic Library} + \li \l{tutorial-4.html}{Convenience Items} + \endlist \li \l{How-tos} \li \l{Reference} \list @@ -1068,7 +1075,6 @@ */ - /*! \previouspage usage.html \page language-introduction.html @@ -1881,7 +1887,7 @@ /*! \previouspage special-property-values.html \page module-providers.html - \nextpage howtos.html + \nextpage tutorial.html \title Module Providers diff --git a/doc/tutorial.qdoc b/doc/tutorial.qdoc new file mode 100644 index 000000000..9f8dc5a44 --- /dev/null +++ b/doc/tutorial.qdoc @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \previouspage module-providers.html + \page tutorial.html + \nextpage howtos.html + + \title Tutorial + + The tutorial describes the process of creating a typical \QBS project and explains core + concepts. + + \list + \li \l{tutorial-1.html}{Application} + \li \l{tutorial-2.html}{Static Library} + \li \l{tutorial-3.html}{Dynamic Library} + \li \l{tutorial-4.html}{Convenience Items} + \endlist +*/ + +/*! + \previouspage tutorial.html + \page tutorial-1.html + \nextpage tutorial-2.html + + \title Application + + Let's start with a mandatory Hello World example. There is a Hello World example in the + \l{overview.html#well-defined-language}{overview section}, but this time we will create a + real-world project. + + We will start with a simple \QBS project file: + \snippet ../tutorial/chapter-1/myproject.qbs 0 + + Here, we set the \l{Project::name}{name} of the project to \c "My Project" - it is mainly + used for IDEs but can also be used to reference a project when setting properties from + command-line. We also set the \l{Project::minimumQbsVersion}{minimumQbsVersion} - this property + can be useful if the project depends on features that are not present in earlier \QBS + versions. + + The \l{Project::references}{references} property contains the path to a file + that describes our application. This file is located in a separate \c app directory - + typically, projects tend to have quite a complicated structure but \QBS does not enforce any + specific layout, so we will simply put each product in a separate directory. + + The application file sets several properties: + \snippet ../tutorial/chapter-1/app/app.qbs 0 + + The \l{Product::name}{name} property identifies the product. + The \l{Product::targetName}{targetName} property sets the name of the resulting binary + (without the \c .exe extension on Windows, which is appended automatically). By default, + \l{Product::targetName}{targetName} defaults to \l{Product::name}{name}. The + \l{Product::files}{files} property contains a single \c main.c file which is a trivial + \e{hello world} application: + \snippet ../tutorial/chapter-1/app/main.c 0 + + We set \l{Product::consoleApplication}{consoleApplication} to \c true to indicate that our + application is going to be used from a terminal. For example, on Windows, this will spawn a + new console window if you double-click the resulting binary, while on macOS it will create a + standalone binary file instead of an + \l{https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html}{application bundle}. + + By default, the \l{Application::install}{CppApplication.install} property is \c false and thus + \QBS does not install the binary. If the \l{Application::install}{install} property is + \c true, when building a project, \QBS will also install it into an \e{installation root} + folder which by default is named \c install-root and located under the build directory. This + folder will contain only resulting artifacts without the build leftovers and files will have + the same layout as in the target system. The \l{Application::install}{install} property should + be set to \c true for all products that are to be distributed. + See the \l{installing-files.html}{Installing Files} section for more details. + + We can now build and run our application from the project directory: + \code + chapter-1 $ qbs build + ... + Building for configuration default + compiling main.c [My Application] + ... + linking myapp [My Application] + ... + Build done for configuration default. + + chapter-1 $ qbs run + ... + Starting target. Full command line: .../default/install-root/usr/local/bin/myapp + Hello, world + \endcode + + The \QBS output to console and default installation location may vary between different + platforms. + + In most cases, \QBS should be able to find the default compiler (for example, GCC or + Clang on Linux, Visual Studio on Windows, or Xcode on macOS), but if that's not the + case, see the \l{configuring.html}{configuring} section. + + In the following chapters, we will continue to improve our project: we will add a library and + make it configurable. +*/ + +/*! + \previouspage tutorial-1.html + \page tutorial-2.html + \nextpage tutorial-3.html + + \title Static Library + + Let's add a static library to our project so we can reuse some code. Analogous to what we did + for the application, we put the file into the \c{lib} directory and add it to the + \l{Project::references}{references} property in our project. The modified project may look + like this: + + \snippet ../tutorial/chapter-2/myproject.qbs 0 + + Let's take a look at the the library file now: + + \snippet ../tutorial/chapter-2/lib/lib.qbs 0 + It contains the \l{StaticLibrary} item which sets the \l{Product::type}{type} of the product + to \c "staticlibrary" and sets some defaults like where to install that library. + As before, we set the \l{Product::files}{files} property with a header: + \snippet ../tutorial/chapter-2/lib/lib.h 0 + And we set the implementation file of our library: + \snippet ../tutorial/chapter-2/lib/lib.c 0 + + We will keep our library really simple - it only contains one function, which we will later use + in our application. The source file requires a \c "CRUCIAL_DEFINE" to be + passed to a preprocessor. That is why we set the \l{cpp::defines}{cpp.defines} property: + \snippet ../tutorial/chapter-2/lib/lib.qbs 1 + + Note that unlike the \l{CppApplication} item, the \l{StaticLibrary} does not pull in the + dependency on the \l{cpp} module automatically - that is why we have to pull it in manually + using the \l{Depends} item. Without the \l{cpp} module, \QBS would not know how to turn a + \c{.c} file into an object file and the object file into a library. See + \l{Rules and Product Types} for details. + + Next, we need to tell \QBS where to look for public headers of our library when building + products that depend on it. By default, \QBS knows nothing about the layout of our + library, so we tell it to look for headers in the library's source directory using the + \l{Export} item: + + \snippet ../tutorial/chapter-2/lib/lib.qbs 2 + You can export any \l{Module} property within the \l{Export} item - it will be merged in the + depending product with other properties. For example, you can export + \l{cpp::defines}{cpp.defines} or specific \l{cpp::commonCompilerFlags}{compiler flags} that + are required to use this product. + + We depend on the \l{cpp} module twice - once within the \l{StaticLibrary} + item and once in the \l{Export} item. This is because by default \QBS does not export anything + when depending on this product and the dependencies in this item (as well as + properties set in this item) are private to this product while dependencies and properties + set in the \l{Export} item are for export only. + + Finally, we have some Apple-specific settings. You can skip this part of the tutorial if you + are using some other platform. We depend on the \l{bundle} module and set the + \l{bundle::isBundle}{bundle.isBundle} to false: + \snippet ../tutorial/chapter-2/lib/lib.qbs 3 + By default, \QBS builds static and dynamic libraries as + \l{https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WhatAreFrameworks.html}{Frameworks} + on macOS. So, to keep things simple, we disable the framework build and build a plain old + static library file here. +*/ + +/*! + \previouspage tutorial-2.html + \page tutorial-3.html + \nextpage tutorial-4.html + + \title Dynamic Library + + Let's now turn our static library into a dynamic library. It is a bit trickier with dynamic + libraries since several things should be tweaked. First, we need to be able to mark methods + (or classes) in our library as exported. Second, we need to tell our application where to look + for our library at run time using \l{https://en.wikipedia.org/wiki/Rpath}{rpaths}. + + Some compilers, like MSVC, require us to mark which symbols we want to export or import. The + \l{https://stackoverflow.com/a/6840659}{canonical} way would be to define some helper macros. + Let's start with a header that defines those helper macros: + \snippet ../tutorial/chapter-3/lib/lib_global.h 0 + + This header defines the \c MYLIB_EXPORT macro that expands either to an "export" or to an + "import" directive based on the presence of the \c MYLIB_LIBRARY macro. We can use this macro + to mark a function as follows: + \snippet ../tutorial/chapter-3/lib/lib.h 0 + + The modified library product may look like this: + + \snippet ../tutorial/chapter-3/lib/lib.qbs 0 + + The most important change is that we changed the item type from \l{StaticLibrary} to + \l{DynamicLibrary}. We also added the \c MYLIB_LIBRARY to the list of + \l{cpp::defines}{defines}. We do this only when building the library but we are not exporting + it - that way the users of our library will have methods marked for import rather than export. + + Finally, we set \l{cpp::sonamePrefix}{cpp.sonamePrefix} to \c "@rpath". This is required only + for Apple platforms, see + \l{https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/RunpathDependentLibraries.html}{Run-Path Dependent Libraries} + for details. + + It is also required to set \l{cpp::rpaths}{cpp.rpaths} in our application file. Since the + library is installed to the \c{lib} directory and the application is installed to the \c{bin} + directory, we need to tell the loader to look in the \c{lib} directory. The + \l{jsextension-fileinfo.html#relativePath}{FileInfo.relativePath} method can help us: + \snippet ../tutorial/chapter-3/app/app.qbs 0 + + On Linux, this expression would be equivalent to this: \c{cpp.rpaths: ["$ORIGIN/../lib"]}. + Don't forget to \c{import qbs.FileInfo} in order to be able to use the + \l{jsextension-fileinfo.html} extension. + + To make the example complete, here's how the full \c app/app.qbs file should look like: + \snippet ../tutorial/chapter-3/app/app.qbs 1 +*/ + +/*! + \previouspage tutorial-3.html + \page tutorial-4.html + \nextpage howtos.html + + \title Convenience Items + + As you might have noticed, we are repeating ourselves when setting the same properties in our + products - we set \l{Product::version}{version}, \l{Application::install}{install}, + \l{cpp::rpaths}{cpp.rpaths}, and so on. For a single application and a library, that is not a + big deal, but what if we have a dozen libraries? Luckily, this can be achieved using item + \l{language-introduction.html#reusing-project-file-code}{inheritance} - we move the common code + to the base item and in the real product we will only set what is specific to that product (for + example, the list of \l{Product::files}{files}). + + First, we need to tell \QBS where to look for our new base items. This can be achieved using + the \l{Project::qbsSearchPaths}{qbsSearchPaths} property. We set this property to \c "qbs" so + that \QBS will search our items in the \c{qbs} directory located in the project directory: + \snippet ../tutorial/chapter-4/myproject.qbs 0 + + \note This directory has a pre-defined structure: base items should be located under the + \c{imports} subdirectory. See \l{Custom Modules and Items} for details. + + Let's create a base item for all our applications and move common code there: + \snippet ../tutorial/chapter-4/qbs/imports/MyApplication.qbs 0 + + As you see, we managed to extract most of the code here, and our application file now only + contains what's relevant to it: + \snippet ../tutorial/chapter-4/app/app.qbs 0 + + Now let's do the same for our library: + \snippet ../tutorial/chapter-4/qbs/imports/MyLibrary.qbs 0 + + Here, we introduce a helper property, \c libraryMacro, with a default value calculated based + on the capitalized product name. Since the name of out library product is \c "mylib", this + property will expand to \c "MYLIB_LIBRARY". We can also override the default value + for the macro in products that inherit our item like this: + \code + MyLibrary { + libraryMacro: "SOME_OTHER_LIBRARY_MACRO" + } + \endcode + + Let's take a look at the refactored library file: + \snippet ../tutorial/chapter-4/lib/lib.qbs 0 + + We managed to extract the reusable parts to common base items leaving the actual products clean + and simple. + + Unfortunately, item inheritance comes with a price - when both parent and child items set the + same property (\l{cpp::defines}{cpp.defines} in our case), the value in the child item wins. + To work around this, the special \l{special-property-values.html#base}{base} value + exists - it gives access to the base item's value of the current property and makes it possible + to extend its value rather than override it. Here, we concatenate the list of defines from the + base item \c{["MYLIB_LIBRARY"]} with a new list, specific to this product (namely, + \c{['CRUCIAL_DEFINE']}). +*/ @@ -12,6 +12,7 @@ Project { "examples/examples.qbs", "share/share.qbs", "scripts/scripts.qbs", + "tutorial/tutorial.qbs", ] SubProject { diff --git a/tests/auto/auto.qbs b/tests/auto/auto.qbs index bf6b3d219..b042d180a 100644 --- a/tests/auto/auto.qbs +++ b/tests/auto/auto.qbs @@ -11,6 +11,7 @@ Project { "blackbox/blackbox-joblimits.qbs", "blackbox/blackbox-providers.qbs", "blackbox/blackbox-qt.qbs", + "blackbox/blackbox-tutorial.qbs", "blackbox/blackbox-windows.qbs", "blackbox/blackbox.qbs", "buildgraph/buildgraph.qbs", diff --git a/tests/auto/blackbox/CMakeLists.txt b/tests/auto/blackbox/CMakeLists.txt index 1d90ec95b..88e19acdf 100644 --- a/tests/auto/blackbox/CMakeLists.txt +++ b/tests/auto/blackbox/CMakeLists.txt @@ -80,6 +80,15 @@ add_qbs_test(blackbox-qt tst_blackboxqt.h ) +add_qbs_test(blackbox-tutorial + SOURCES + ../shared.h + tst_blackboxbase.cpp + tst_blackboxbase.h + tst_blackboxtutorial.h + tst_blackboxtutorial.cpp + ) + add_qbs_test(blackbox-windows SOURCES ../shared.h diff --git a/tests/auto/blackbox/blackbox-tutorial.qbs b/tests/auto/blackbox/blackbox-tutorial.qbs new file mode 100644 index 000000000..174821ffa --- /dev/null +++ b/tests/auto/blackbox/blackbox-tutorial.qbs @@ -0,0 +1,21 @@ +import qbs.Utilities + +QbsAutotest { + testName: "blackbox-tutorial" + Depends { name: "qbs_app" } + Depends { name: "qbs-setup-toolchains" } + Group { + name: "testdata" + prefix: "../../../tutorial/" + files: ["**/*"] + fileTags: [] + } + files: [ + "../shared.h", + "tst_blackboxtutorial.cpp", + "tst_blackboxtutorial.h", + "tst_blackboxbase.cpp", + "tst_blackboxbase.h", + ] + cpp.defines: base.concat(["SRCDIR=" + Utilities.cStringQuote(path)]) +} diff --git a/tests/auto/blackbox/tst_blackboxtutorial.cpp b/tests/auto/blackbox/tst_blackboxtutorial.cpp new file mode 100644 index 000000000..49de448a1 --- /dev/null +++ b/tests/auto/blackbox/tst_blackboxtutorial.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "tst_blackboxtutorial.h" + +#include <QtCore/qdir.h> +#include <QtCore/qdiriterator.h> + +static QStringList collectProjects(const QString &dirPath) +{ + QStringList result; + QDir dir(dirPath); + const auto subDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); + for (const auto &subDir : subDirs) { + const auto path = dir.filePath(subDir); + if (!QFileInfo::exists(path + "/myproject.qbs")) + continue; + result.append(dir.relativeFilePath(path)); + } + return result; +} + +TestBlackboxTutorial::TestBlackboxTutorial() + : TestBlackboxBase(SRCDIR "/../../../tutorial/", "blackbox-tutorial") +{} + +void TestBlackboxTutorial::tutorial_data() +{ + QTest::addColumn<QString>("project"); + + const auto projects = collectProjects(testDataDir); + for (const auto &project : projects) { + QTest::newRow(project.toUtf8().data()) << project; + } +} + +void TestBlackboxTutorial::tutorial() +{ + QFETCH(QString, project); + + QVERIFY(QDir::setCurrent(testDataDir + "/" + project)); + QCOMPARE(runQbs(), 0); +} + +QTEST_MAIN(TestBlackboxTutorial) diff --git a/tests/auto/blackbox/tst_blackboxtutorial.h b/tests/auto/blackbox/tst_blackboxtutorial.h new file mode 100644 index 000000000..2e84d6a96 --- /dev/null +++ b/tests/auto/blackbox/tst_blackboxtutorial.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef TST_BLACKBOXTUTORIAL_H +#define TST_BLACKBOXTUTORIAL_H + +#include "tst_blackboxbase.h" + +class TestBlackboxTutorial : public TestBlackboxBase +{ + Q_OBJECT + +public: + TestBlackboxTutorial(); + +private slots: + void tutorial_data(); + void tutorial(); +}; + +#endif // TST_BLACKBOXTUTORIAL_H diff --git a/tutorial/chapter-1/app/app.qbs b/tutorial/chapter-1/app/app.qbs new file mode 100644 index 000000000..8d0831dc1 --- /dev/null +++ b/tutorial/chapter-1/app/app.qbs @@ -0,0 +1,12 @@ +//! [0] +CppApplication { + name: "My Application" + targetName: "myapp" + files: "main.c" + version: "1.0.0" + + consoleApplication: true + install: true + installDebugInformation: true +} +//! [0] diff --git a/tutorial/chapter-1/app/main.c b/tutorial/chapter-1/app/main.c new file mode 100644 index 000000000..3f3560d25 --- /dev/null +++ b/tutorial/chapter-1/app/main.c @@ -0,0 +1,9 @@ +//! [0] +#include <stdio.h> + +int main() +{ + printf("Hello, world\n"); + return 0; +} +//! [0] diff --git a/tutorial/chapter-1/myproject.qbs b/tutorial/chapter-1/myproject.qbs new file mode 100644 index 000000000..b936f3738 --- /dev/null +++ b/tutorial/chapter-1/myproject.qbs @@ -0,0 +1,9 @@ +//! [0] +Project { + name: "My Project" + minimumQbsVersion: "2.0" + references: [ + "app/app.qbs" + ] +} +//! [0] diff --git a/tutorial/chapter-2/app/app.qbs b/tutorial/chapter-2/app/app.qbs new file mode 100644 index 000000000..a3ee757ab --- /dev/null +++ b/tutorial/chapter-2/app/app.qbs @@ -0,0 +1,11 @@ +CppApplication { + Depends { name: "mylib" } + name: "My Application" + targetName: "myapp" + files: "main.c" + version: "1.0.0" + + consoleApplication: true + install: true + installDebugInformation: true +} diff --git a/tutorial/chapter-2/app/main.c b/tutorial/chapter-2/app/main.c new file mode 100644 index 000000000..a560d1ba1 --- /dev/null +++ b/tutorial/chapter-2/app/main.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +#include "lib.h" + +int main() +{ + printf("Hello, world\n"); + printf("%s\n", get_string()); + return 0; +} diff --git a/tutorial/chapter-2/lib/lib.c b/tutorial/chapter-2/lib/lib.c new file mode 100644 index 000000000..9fe020ba2 --- /dev/null +++ b/tutorial/chapter-2/lib/lib.c @@ -0,0 +1,15 @@ +//! [0] + +// lib/lib.cpp +#include "lib.h" + +#ifndef CRUCIAL_DEFINE +# error CRUCIAL_DEFINE not defined +#endif + +const char *get_string() +{ + return "Hello from library"; +} + +//! [0] diff --git a/tutorial/chapter-2/lib/lib.h b/tutorial/chapter-2/lib/lib.h new file mode 100644 index 000000000..efc56ae0b --- /dev/null +++ b/tutorial/chapter-2/lib/lib.h @@ -0,0 +1,11 @@ +//! [0] + +// lib/lib.h +#ifndef LIB_H +#define LIB_H + +const char *get_string(); + +#endif // LIB_H + +//! [0] diff --git a/tutorial/chapter-2/lib/lib.qbs b/tutorial/chapter-2/lib/lib.qbs new file mode 100644 index 000000000..378d855a6 --- /dev/null +++ b/tutorial/chapter-2/lib/lib.qbs @@ -0,0 +1,28 @@ +//! [0] +StaticLibrary { + name: "mylib" + files: [ + "lib.c", + "lib.h", + ] + version: "1.0.0" + install: true + + //! [1] + Depends { name: 'cpp' } + cpp.defines: ['CRUCIAL_DEFINE'] + //! [1] + + //! [2] + Export { + Depends { name: "cpp" } + cpp.includePaths: [exportingProduct.sourceDirectory] + } + //! [2] + + //! [3] + Depends { name: 'bundle' } + bundle.isBundle: false + //! [3] +} +//! [0] diff --git a/tutorial/chapter-2/myproject.qbs b/tutorial/chapter-2/myproject.qbs new file mode 100644 index 000000000..17ef04ac9 --- /dev/null +++ b/tutorial/chapter-2/myproject.qbs @@ -0,0 +1,10 @@ +//! [0] +Project { + name: "My Project" + minimumQbsVersion: "2.0" + references: [ + "app/app.qbs", + "lib/lib.qbs" + ] +} +//! [0] diff --git a/tutorial/chapter-3/app/app.qbs b/tutorial/chapter-3/app/app.qbs new file mode 100644 index 000000000..bb7108581 --- /dev/null +++ b/tutorial/chapter-3/app/app.qbs @@ -0,0 +1,29 @@ +//! [1] +// app/app.qbs +import qbs.FileInfo + +CppApplication { + Depends { name: "mylib" } + name: "My Application" + targetName: "myapp" + files: "main.c" + version: "1.0.0" + + consoleApplication: true + install: true + + //! [0] + cpp.rpaths: { + if (!cpp.rpathOrigin) + return []; + return [ + FileInfo.joinPaths( + cpp.rpathOrigin, + FileInfo.relativePath( + FileInfo.joinPaths("/", product.installDir), + FileInfo.joinPaths("/", "lib"))) + ]; + } + //! [0] +} +//! [1] diff --git a/tutorial/chapter-3/app/main.c b/tutorial/chapter-3/app/main.c new file mode 100644 index 000000000..a560d1ba1 --- /dev/null +++ b/tutorial/chapter-3/app/main.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +#include "lib.h" + +int main() +{ + printf("Hello, world\n"); + printf("%s\n", get_string()); + return 0; +} diff --git a/tutorial/chapter-3/lib/lib.c b/tutorial/chapter-3/lib/lib.c new file mode 100644 index 000000000..1cbc5ef21 --- /dev/null +++ b/tutorial/chapter-3/lib/lib.c @@ -0,0 +1,16 @@ + +//! [0] + +// lib/lib.cpp +#include "lib.h" + +#ifndef CRUCIAL_DEFINE +# error CRUCIAL_DEFINE not defined +#endif + +const char *get_string() +{ + return "Hello from library"; +} + +//! [0] diff --git a/tutorial/chapter-3/lib/lib.h b/tutorial/chapter-3/lib/lib.h new file mode 100644 index 000000000..772e1d82b --- /dev/null +++ b/tutorial/chapter-3/lib/lib.h @@ -0,0 +1,11 @@ +#ifndef LIB_H +#define LIB_H + +//! [0] +// lib/lib.h +#include "lib_global.h" + +MYLIB_EXPORT const char *get_string(); +//! [0] + +#endif // LIB_H diff --git a/tutorial/chapter-3/lib/lib.qbs b/tutorial/chapter-3/lib/lib.qbs new file mode 100644 index 000000000..a52c29d97 --- /dev/null +++ b/tutorial/chapter-3/lib/lib.qbs @@ -0,0 +1,26 @@ +//! [0] +// lib/lib.qbs + +DynamicLibrary { + name: "mylib" + files: [ + "lib.c", + "lib.h", + "lib_global.h", + ] + version: "1.0.0" + install: true + + Depends { name: "cpp" } + cpp.defines: ["MYLIB_LIBRARY", "CRUCIAL_DEFINE"] + cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined + + Export { + Depends { name: "cpp" } + cpp.includePaths: [exportingProduct.sourceDirectory] + } + + Depends { name: "bundle" } + bundle.isBundle: false +} +//! [0] diff --git a/tutorial/chapter-3/lib/lib_global.h b/tutorial/chapter-3/lib/lib_global.h new file mode 100644 index 000000000..07b7978ef --- /dev/null +++ b/tutorial/chapter-3/lib/lib_global.h @@ -0,0 +1,23 @@ +//! [0] +// lib/lib_global.h + +#ifndef LIB_GLOBAL_H +#define LIB_GLOBAL_H + +#if defined(_WIN32) || defined(WIN32) +#define MYLIB_DECL_EXPORT __declspec(dllexport) +#define MYLIB_DECL_IMPORT __declspec(dllimport) +#else +#define MYLIB_DECL_EXPORT __attribute__((visibility("default"))) +#define MYLIB_DECL_IMPORT __attribute__((visibility("default"))) +#endif + +#if defined(MYLIB_LIBRARY) +#define MYLIB_EXPORT MYLIB_DECL_EXPORT +#else +#define MYLIB_EXPORT MYLIB_DECL_IMPORT +#endif + +#endif // LIB_GLOBAL_H + +//! [0] diff --git a/tutorial/chapter-3/myproject.qbs b/tutorial/chapter-3/myproject.qbs new file mode 100644 index 000000000..152d6cce5 --- /dev/null +++ b/tutorial/chapter-3/myproject.qbs @@ -0,0 +1,8 @@ +Project { + name: "My Project" + minimumQbsVersion: "2.0" + references: [ + "app/app.qbs", + "lib/lib.qbs" + ] +} diff --git a/tutorial/chapter-4/app/app.qbs b/tutorial/chapter-4/app/app.qbs new file mode 100644 index 000000000..7051ff554 --- /dev/null +++ b/tutorial/chapter-4/app/app.qbs @@ -0,0 +1,10 @@ +//! [0] +// app/app.qbs + +MyApplication { + Depends { name: "mylib" } + name: "My Application" + targetName: "myapp" + files: "main.c" +} +//! [0] diff --git a/tutorial/chapter-4/app/main.c b/tutorial/chapter-4/app/main.c new file mode 100644 index 000000000..a560d1ba1 --- /dev/null +++ b/tutorial/chapter-4/app/main.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +#include "lib.h" + +int main() +{ + printf("Hello, world\n"); + printf("%s\n", get_string()); + return 0; +} diff --git a/tutorial/chapter-4/lib/lib.c b/tutorial/chapter-4/lib/lib.c new file mode 100644 index 000000000..5dcae0de3 --- /dev/null +++ b/tutorial/chapter-4/lib/lib.c @@ -0,0 +1,10 @@ +#include "lib.h" + +#ifndef CRUCIAL_DEFINE +# error CRUCIAL_DEFINE not defined +#endif + +const char *get_string() +{ + return "Hello from library"; +} diff --git a/tutorial/chapter-4/lib/lib.h b/tutorial/chapter-4/lib/lib.h new file mode 100644 index 000000000..ef39ca4a1 --- /dev/null +++ b/tutorial/chapter-4/lib/lib.h @@ -0,0 +1,8 @@ +#ifndef LIB_H +#define LIB_H + +#include "lib_global.h" + +MYLIB_EXPORT const char *get_string(); + +#endif // LIB_H diff --git a/tutorial/chapter-4/lib/lib.qbs b/tutorial/chapter-4/lib/lib.qbs new file mode 100644 index 000000000..1f7bf6e6b --- /dev/null +++ b/tutorial/chapter-4/lib/lib.qbs @@ -0,0 +1,13 @@ +//! [0] +// lib/lib.qbs + +MyLibrary { + name: "mylib" + files: [ + "lib.c", + "lib.h", + "lib_global.h", + ] + cpp.defines: base.concat(["CRUCIAL_DEFINE"]) +} +//! [0] diff --git a/tutorial/chapter-4/lib/lib_global.h b/tutorial/chapter-4/lib/lib_global.h new file mode 100644 index 000000000..76cce8d36 --- /dev/null +++ b/tutorial/chapter-4/lib/lib_global.h @@ -0,0 +1,18 @@ +#ifndef LIB_GLOBAL_H +#define LIB_GLOBAL_H + +#if defined(_WIN32) || defined(WIN32) +#define MYLIB_DECL_EXPORT __declspec(dllexport) +#define MYLIB_DECL_IMPORT __declspec(dllimport) +#else +#define MYLIB_DECL_EXPORT __attribute__((visibility("default"))) +#define MYLIB_DECL_IMPORT __attribute__((visibility("default"))) +#endif + +#if defined(MYLIB_LIBRARY) +#define MYLIB_EXPORT MYLIB_DECL_EXPORT +#else +#define MYLIB_EXPORT MYLIB_DECL_IMPORT +#endif + +#endif // LIB_GLOBAL_H diff --git a/tutorial/chapter-4/myproject.qbs b/tutorial/chapter-4/myproject.qbs new file mode 100644 index 000000000..d429542e7 --- /dev/null +++ b/tutorial/chapter-4/myproject.qbs @@ -0,0 +1,11 @@ +//! [0] +Project { + name: "My Project" + minimumQbsVersion: "2.0" + references: [ + "app/app.qbs", + "lib/lib.qbs" + ] + qbsSearchPaths: "qbs" +} +//! [0] diff --git a/tutorial/chapter-4/qbs/imports/MyApplication.qbs b/tutorial/chapter-4/qbs/imports/MyApplication.qbs new file mode 100644 index 000000000..e98794f3c --- /dev/null +++ b/tutorial/chapter-4/qbs/imports/MyApplication.qbs @@ -0,0 +1,23 @@ +//! [0] +// qbs/imports/MyApplication.qbs + +import qbs.FileInfo + +CppApplication { + version: "1.0.0" + consoleApplication: true + install: true + + cpp.rpaths: { + if (!cpp.rpathOrigin) + return []; + return [ + FileInfo.joinPaths( + cpp.rpathOrigin, + FileInfo.relativePath( + FileInfo.joinPaths("/", product.installDir), + FileInfo.joinPaths("/", "lib"))) + ]; + } +} +//! [0] diff --git a/tutorial/chapter-4/qbs/imports/MyLibrary.qbs b/tutorial/chapter-4/qbs/imports/MyLibrary.qbs new file mode 100644 index 000000000..c819fa38e --- /dev/null +++ b/tutorial/chapter-4/qbs/imports/MyLibrary.qbs @@ -0,0 +1,21 @@ +//! [0] +// qbs/imports/MyLibrary.qbs + +DynamicLibrary { + version: "1.0.0" + install: true + + Depends { name: 'cpp' } + property string libraryMacro: name.replace(" ", "_").toUpperCase() + "_LIBRARY" + cpp.defines: [libraryMacro] + cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined + + Export { + Depends { name: "cpp" } + cpp.includePaths: [exportingProduct.sourceDirectory] + } + + Depends { name: 'bundle' } + bundle.isBundle: false +} +//! [0] diff --git a/tutorial/tutorial.qbs b/tutorial/tutorial.qbs new file mode 100644 index 000000000..0d2ae3514 --- /dev/null +++ b/tutorial/tutorial.qbs @@ -0,0 +1,5 @@ +Product { + files: [ + "*/**", + ] +} |