Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I embed a binary executable (to be executed at runtime) in a Qt program?

Tags:

qt

qprocess

I'm writing a cross-platform C++ program using Qt and I want to package/embed a number of binary executables within the program. The program should be able to execute these binaries at runtime.

I figured, I would need QResource and QProcess using start() and the ":/..." notation, but I don't seem to get the process running. Is there anything I am missing? Should it work like this? Does the binary need to be set as executable?

Background: I am writing a tool which uses Git and I don't want to require the end-user to install Git manually.

(Trying this on Mac OS X, BTW.)

Update:

I am using the following code (C++, Qt on Mac OS X):

QString program = ":/git";
QStringList arguments;
arguments << "help" << "commit";
myProcess->start(program, arguments);

The Git executable is in the project path, my resources.qrc references it like so:

<qresource prefix="/">
    <file>git</file>
</qresource>

I'm not getting an error, but the program is not executed. Behavior is the same when I set program to a non-existing file. Replacing ":/git" by the absolute path to git works perfectly.

like image 815
jan Avatar asked Dec 21 '09 18:12

jan


2 Answers

You can't execute a program directly from a resource.

If you had a program in a resource and you wanted to execute it, you'd first have to read it out of the resource, write it to a file, make the file executable, then execute it.

Also, when you say that you're not getting an error, that probably means that you aren't checking for errors properly.

like image 152
rohanpm Avatar answered Sep 22 '22 11:09

rohanpm


Several years late, but the question is still relevant. I had the same problem when wanting to embed rclone.

In the .pro file, add

# From http://stackoverflow.com/a/37561981
defineReplace(copyToDir) {
    files = $$1
    DIR = $$2
    LINK =

    for(FILE, files) {
        LINK += $$QMAKE_COPY $$shell_path($$FILE) $$shell_path($$DIR) $$escape_expand(\\n\\t)
    }
    return($$LINK)
}

defineReplace(copyToBuilddir) {
    return($$copyToDir($$1, $$OUT_PWD))
}

# Copy the binary files dependent on the system architecture
win32 {
    message("Windows")
    QMAKE_POST_LINK += $$copyToBuilddir($$PWD/rclone/windows/rclone.exe)
}else: unix:!macx {
    message("Linux")
    QMAKE_POST_LINK += $$copyToBuilddir($$PWD/rclone/linux/rclone)
}else: macx: {
    # Here we want to place the binaries inside the application bundle, so the 
    # QMAKE_POST_LINK approach will not work because it places them in the same
    # directory as the bundle and not inside it. Instead, use QMAKE_BUNDLE_DATA.
    message("macOS")
    MediaFiles.files += $$PWD/rclone/macOS/rclone
    MediaFiles.path = Contents/MacOS
    QMAKE_BUNDLE_DATA += MediaFiles
}

Notice how there are slight differences for each platform, but in general the approach is the same.

qmake will copy this files to the destination directory, and they will be accessible by simply making the process call to the local relative directory.

In the following code, I call the executable from QML, and it's going to be very similar in C++ as well:

var rcloneCommand
if (Qt.platform.os === "windows") {
    console.log("Windows")
    rcloneCommand = "rclone.exe"
} else {
    console.log("OSX/Linux")
    rcloneCommand = "./rclone"
}
qProcess.start(rcloneCommand, ["--config", "rclone.conf", "-v", "copy", "--stats", "1s", source, destination]);
like image 31
Kenn Sebesta Avatar answered Sep 20 '22 11:09

Kenn Sebesta