Use QProcess with a executable in resource [duplicate] - c++

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.

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.

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]);

I don't think resources would work. Processes are created by operating system, and resources are handled by application.
One possible solution would be bundle additional executables somewhere in your application directory and execute them from that path.

So the problem doesn't seem to be extracting the git executable from the resource so much as executing it?
The git program is generate don disk correctly, can you check it's permissions ?

Related

QBS build system, can't initialize environment with vcvars64.bat

I'm trying to implement my own module to build C++ on Windows with clang-cl toolchain as there's no built-in support in QBS right now.
I chose to use lld-link instead of microsoft linker, so I have to supply it with all the MS library include paths manually. With these paths hardcoded, I manage to build my apps fine. But I'd like to make my module more flexible and use %LIB% environment variable set by vcvars32.bat|vcvars64.bat
As far as I understand, this could (should?) be done inside module's setupBuildEnvironment script. Here's what I try to read the %LIB% and fail:
import qbs.Environment
import qbs.Process
Module
{
setupBuildEnvironment:
{
var p = new Process();
p.exec("vcvars64.bat", [], true);
// makes no difference
// p.exec("cmd", ["/c", "vcvars64.bat"], true);
var lib = p.getEnv("LIB");
// this fails too
// var lib = Environment.getEnv("LIB");
console.info("LIB = " + lib);
p.close();
}
...
}
This gives me LIB = so I'm getting nowhere. My guess is that the process is already terminated at the moment of querying the variable (p.getEnv("LIB")), hence the empty result. The QBS docs for Process.getEnv() state nothing in this regard.
What is the correct QBS way to initialize environment with vcvars64.bat, and more broadly, what is the correct way to get environment of a process inside setupBuildEnvironment?
[update]
Well, embarassingly, this was easy to work around by creating a simple batch and getting rid of setupBuildEnvironment script altogether:
#echo off
call vcvars64 && qbs
But I'd like to avoid batch scripting as much as possible, so the question still stands.
The vars batch files just dump some information onto the console. That does not set an environment on the calling process in any way. You would need to parse the process output. I suggest you take a look at the MsvcProbe item in the qbs sources to see how that is implemented for MSVC. You might be able to adapt the code for clang-cl.

Qt read files from Resources Bundle Directory

I have a Qt program, that has some .txt files inside. In the beginning I just read them from some directory I made, but the time has come, and it become necessary to create a bundle for Mac OSX. I managed to put .txt files inside /Resources in .app directory of bundle with the help of cmake, but I didn't find how to read them from there.
How can I read those files, so I could run that app from everywhere?
As #MrEricSir pointed out, it's better to use the Qt resource system along with the native Qt classes for reading files via streams.
However, if you still need to store your data in the separate files, you can simply use the QCoreApplication::applicationDirPath() method which returns the path to the executable and wrap it into your own function in order to respect the application structure on different platforms.
For example:
QString getResourcesPath()
{
#if defined(Q_OS_WIN)
return QApplication::applicationDirPath() + "/";
#elif defined(Q_OS_OSX)
return QApplication::applicationDirPath() + "/../Resources/";
#elif defined(Q_OS_LINUX)
return QApplication::applicationDirPath() + "/../share/yourapplication/";
#else
return QApplication::applicationDirPath() + "/";
#endif
}
This sample code assumes that:
On Windows you are storing resources in the same directory as your executable.
On macOS you are storing resources in the Resources directory of your .app bundle. According to the Apple developer docs, executables are stored in the yourapplication.app/Contents/MacOS directory and resource files are usually stored in the yourapplication.app/Contents/Resources. The code simply travels to the Resources directory relatively to your executable.
On Linux you are using the standard Linux directory structure utilizing the separate bin and share directories. Of course, you may use the opt directory or don't bundle your application at all – in this case you won't need any conditional inclusions for Linux. For more information read about the Linux Filesystem Hierarchy Standard.

QDir absolutePath on Mac

Im getting two different paths when i run the same build within Qt Creator and when I double click on it from the Finder on a Mac.
Here is my code:
QDir dir = QDir::currentPath();
dir.cdUp();
dir.cdUp();
dir.cdUp();
QString rootPath = dir.absolutePath();
When I run it (debug) mode in Qt Creator my path is:
/Users/myuser/Projects/AppName/build/mac
When I double click on the file that is located on
/Users/myyser/Projects/AppName/build/mac from finder it returns
/ only.
Why would I get two different paths?
Version: Qt5.2.1
Update
Seems like its a bug from reading the following URLhttp://qt-project.org/forums/viewthread/34019
Why would I get two different paths?
As they write in the thread you linked, QDir::currentPath() does not necessarily returns the application directory. It will return the path from wherever the application is run, which will be different than the application directory when running the application from the command line, or even from "start menu" alike places and so on.
If you wish to deal with the application directory to navigate from there, you would need to use the following method instead:
QString QCoreApplication::applicationDirPath() [static]
Returns the directory that contains the application executable.
For example, if you have installed Qt in the C:\Qt directory, and you run the regexp example, this function will return "C:/Qt/examples/tools/regexp".
On Mac OS X this will point to the directory actually containing the executable, which may be inside of an application bundle (if the application is bundled).
The last sentence even clarifies the Mac OS X case.
The current directory can be anything, it solely depends on how your process is launched. What you've shown so far is that Qt Creator and Finder start the process with different current directory, that's all.
The only use for currentPath without setting it first, that I can think of, is in command line / console applications. Why do you think you need to use it? To what end?

QMake - how to add a post-build step after EVERY build

Having a bit of trouble - I've added the following lines to qmake in order to get it to copy files into the app bundle on Mac.
mac {
QMAKE_POST_LINK = $$PWD/package_mac.sh
}
The .sh file runs sometimes, and seems to work (at the moment it just runs touch geese which creates a file named geese in the build directory (excellent!).
But, it doesn't run every time I build, it seems to be only when files are changed. Really, I want a way to get qmake to copy over all my game's resources into the correct places on each platform (so build folder on Windows, app package on Mac, etc...) every time I build.
Any ideas?
Have you tried using POST_TARGETDEPS (with QMAKE_EXTRA_TARGETS)? The downside to this approach is that you'll have to create some sort of dummy file (since to the best of my knowledge .PHONY isn't supported by qmake), which you should probably also clean later.
Taking your example:
mac {
package-script = $$PWD/package_mac.sh
}
package-target.target = $$PWD/package.tmp
package-target.depends = FORCE
package-target.commands = $$package-script
(See this question for a similar discussion.)

How to make XCode put required resources in "build" folder?

I am trying out lua script with C++ in Mac OS X. I was finding a way to make the program returning the current working directory. That's no problem with getcwd, but then I came one thing:
My foo.lua stays at its initial path only. When I compile program, it is not being copied over to the build/Debug directory. Sure, I can grab my script there, but that's just impractical. XCode or any IDE should carry resources to the build zone. XCode does this automatically with iPhone app, but this seems to be a different case. For this case, how to command XCode to put the respective resources in the build directories?
int main (int argc, char * const argv[]) {
...
...
luaL_dofile(luaVM,"/Users/yourNameHere/Desktop/LuaSandbox/LetsTryLua/foo.lua");
//typing the whole absolute path here is just ugly and impractical.
...
...
printf("working directory: %s", buffer);
//output is: working directory: /Users/yourNameHere/Desktop/LuaSandbox/LetsTryLua/build/Debug
...
...
Rather than hard code the path to your Lua script you may want to use the NSBundle API's to find it:
NSBundle * mainNSBundle = [NSBundle mainBundle];
NSString * luaFilePath = [mainNSBundle pathForResource:#"foo"
ofType:#"lua"
inDirectory:NULL
forLocalization:NULL];
luaL_dofile(luaVM,[luaFilePath UTF8String]);
This will find it in the bundle's folder (if you added the "Copy Bundle Resources" build step to your target as the above poster suggested.
Because you're using a .lua file as a resource, I suspect that isn't recognised as a standard resource type and hence it hasn't been automatically copied. You should be able to do this though by adding an extra Copy Bundle Resources build step to your target and then add your file to it in the project view.
If you're creating a command line tool that is not a bundle, then there's never going to be a good solution. If you're creating a regular app then the aforementioned solution will work, but you're going to have to stop assuming that your working directory is set to anything even remotely meaningful at any point in time and use the appropriate methods for finding resources stored within your bundle.