Thern
Thern

Reputation: 1059

How to tell CMake to install a library first and then use it?

I am quite new to CMake and still struggling with the fundamentals. Currently I try to tell CMake to first download ZLib from GitHub and then use it for my BuildTest app. (By the way, I use Qt Creator to manage my project.) To my understanding, I have to use ExternalProject_Add to download and install zlib, and then I can link the installed library to my project. But whatever I do, I can't get it running, probably due to some misunderstanding of how CMake works. Executing CMake alone gives no error, but when I create my project, it fails with the following message:

ninja: error: 'zlib-install/lib/libz.a', needed by 'appBuildTest.exe', missing and no known rule to make it

This is consistent with the fact that the zlib-install folder is empty, although I would expect the build to download ZLib and install it to this folder. Since I added add_dependencies(appBuildTest zlib), I would also expect that the build is finished before the linker tries to link it to my app. I suppose I am overlooking something simple, but I seem to be unable to find the issue.

This is my CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)

project(BuildTest VERSION 0.1 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 6.5 REQUIRED COMPONENTS Quick)

qt_standard_project_setup(REQUIRES 6.5)

set(ZLIB_DIR "${CMAKE_SOURCE_DIR}/ThirdParty/zlib")
set(ZLIB_BINARY_DIR "${CMAKE_BINARY_DIR}/zlib-build")

# Add zlib as an external project using Git to download it
include(ExternalProject)

ExternalProject_Add(
    zlib
    PREFIX ${ZLIB_BINARY_DIR}
    GIT_REPOSITORY https://github.com/madler/zlib.git
    GIT_TAG master
    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/zlib-install
)

qt_add_executable(appBuildTest
    main.cpp
)

qt_add_qml_module(appBuildTest
    URI BuildTest
    VERSION 1.0
    QML_FILES
        Main.qml
)

target_link_libraries(appBuildTest
    PRIVATE Qt6::Quick
    ${CMAKE_BINARY_DIR}/zlib-install/lib/libz.a  # Link with zlib
)

target_include_directories(appBuildTest
    PRIVATE ${CMAKE_BINARY_DIR}/zlib-install/include
)

# Add the zlib build dependency to ensure it is built before the application
add_dependencies(appBuildTest zlib)

# Install the application
include(GNUInstallDirs)
install(TARGETS appBuildTest
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

If you like to have a minimal running example, I employed a very simple GUI (Main.qml):

import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 300
    height: 200
    title: "BuildTest - Zlib Example"

    Rectangle {
        width: parent.width
        height: parent.height
        color: "#ffffff"

        // A simple button
        Button {
            text: "Compress/Decompress Data"
            anchors.centerIn: parent
            onClicked: {
                // Trigger compression and decompression from C++
                console.log("Button clicked")
            }
        }

        Text {
            id: resultText
            text: "Press the button to start"
            anchors.top: parent.top
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.topMargin: 20
        }
    }
}

And this is a main.cpp that should use ZLib:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QByteArray>
#include <QTextStream>
#include <QVariant>
#include <QDebug>
#include <zlib.h>

void compressAndDecompressData() {
    QByteArray originalData = "Some text that will be compressed and decompressed using zlib.";

    // Compress the data
    uLongf compressedSize = compressBound(originalData.size());
    QByteArray compressedData(compressedSize, Qt::Uninitialized);

    int result = compress(reinterpret_cast<Bytef*>(compressedData.data()), &compressedSize, reinterpret_cast<const Bytef*>(originalData.constData()), originalData.size());

    if (result != Z_OK) {
        qWarning("Compression failed");
        return;
    }

    // Decompress the data
    QByteArray decompressedData(originalData.size(), Qt::Uninitialized);
    uLongf decompressedSize = originalData.size();

    result = uncompress(reinterpret_cast<Bytef*>(decompressedData.data()), &decompressedSize, reinterpret_cast<const Bytef*>(compressedData.constData()), compressedSize);

    if (result != Z_OK) {
        qWarning("Decompression failed");
        return;
    }

    // Display the original, compressed, and decompressed data
    qDebug() << "Original data:" << originalData;
    qDebug() << "Decompressed data:" << decompressedData;
}

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    compressAndDecompressData();

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/Main.qml")));

    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

Upvotes: 0

Views: 23

Answers (0)

Related Questions