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.
Related
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 ?
i have made a MFC-based game and my project includes images and sounds. Now i want to create an installer setup in order to distribute it.
i have used the resources by providing their exact path in e.g
img->Load(L"C:\\Users\\Ad33l's\\Desktop\\block mania\\block mania\\res\\db.png");
MCIWndCreate(NULL, NULL,WS_POPUP|MCIWNDF_NOPLAYBAR|MCIWNDF_NOMENU,L"C:\\Users\\Ad33l's\\Desktop\\block mania\\block mania\\res\\tick.wav");
1.Can someone tell me any way to avoid Hard-coding the actual resource path as these resource files will not be present at this same exact path in other computers ?
2.Also guide me to handle these resource files during the creation of standalone SETUP (i am using advance installer )
(as an actual answer).
Do not use absolute path, always use relative path; relative to your EXE file is one solution.
The EXE path can be found using GetModuleFileName.
char appPath[MAXFILENAMELEN];
GetModuleFileName(NULL, appPath, MAXFILENAMELEN);
(addendum) appPath name is misleading, it contains the full name of the application; you need to extract the path from the full application name.
We do something like this:
(edit to make it compilable in unicode)
TCHAR applicationPath[MAX_PATH];
GetModuleFileName(NULL, applicationPath, MAX_PATH);
CString sSoundFile(applicationPath);
PathRemoveFileSpec(sSoundFile.GetBuffer());
sSoundFile.ReleaseBuffer();
From there, you can do something like (pseudocode-ish):
img.Load( appPath + "//Images//db.png" );
You can have a variable that saves the directory they want to install your program in. After they choose the directory they want to install it in, go off of that. Or you can also use system folders like the appdata folder
A first solution would be to configure your setup project to install the installation files under the DesktopFolder\block mania\block mania\res. Then, you can access within your application the current user Desktop location and append to it the remaining fix location (block mania\block mania\res).
Another solution would be to configure the setup project to create registries at install time which will store the paths of the installation files. Then, your application could read the installation paths from registry.
Finally you could also create at install time environment variables containing the paths of the installation file and, then use within your application the environment variables to access the installed files locations.
Is there a way to specify the root directory at runtime within a program's execution? The reason why I ask is because I'd like to be able to have an assets folder which I can access in code simply via "assets/" relatively, as opposed to having to type "absolute/path/to/assets". Worst case scenario I can create a macro or a const std::string/QString to append to everything, but I find that it would be cleaner to just allow the project to be able to access everything relatively FROM the source directory, as opposed to the actual build directory.
How can this be achieved, if at all?
edit
I should also state that I'm operating in Linux, and don't have any plans for multi-platform use at the moment, mainly due to the fact that I'm just creating study projects.
Switch your working directory to the application executable's directory, like so:
if (QDir::setCurrent(QCoreApplication::applicationDirPath())) {
// success!
...
}
This is cross-platform.
Hello now i try translate project on other platform(first platform it's windows, now company planed use Linux platform), i use eclipse in windows and Linux, language is c++. Name project PostLib and he have next structure: PostLib/common, PostLib/inc, PostLib/inc_ext, PostLib/src.
PostLib/common its part for control memory leaks and for smart pointer. PostLib/inc PostLib/inc_ext - this part for header, and PostLib/src for sources code.My problem next: In project settings, i use PostLib/common, PostLib/inc and PostLib/inc_ext, and Eclipse write error: File Def.h(PostLib/inc_ext) can not find file p_mem.h(PostLib/common). How can it be?
When i look property all direction in project i saw interesting thing: Property on directory PostLib/inc_ext difference than all project, on other directory property same like in project/ Property i mean Property->Settings->GCC C++ Compiler->Directories, i try add path (PostLib/common) , but no effect, Eclipse write error File Def.h(PostLib/inc_ext) can not find file p_mem.h(PostLib/common).
On Linux systems directory separator is /, not \!
PostLib\common\p_mem.h is a path in windows, but is not in Linux. The equivalent for Linux is PostLib/common/p_mem.h.
Also, filesystem paths on Linux are case sensitive. p_mem.h is a different file from P_Mem.h: be sure you used the right characters for files and directories.
C++ How to get a filename (and path) of the executing .so module in Unix?
Something similar to GetModuleFileName on Windows.
Although it is not a POSIX standard interface, the dladdr() function is available on many systems including Linux, Solaris, Darwin/Mac OS X, FreeBSD, HP-UX, and IRIX. This function takes an address, which could be a pointer to a static function within the module for example (if cast to void *), and fills in a Dl_info structure with information including the path name of the shared object containing that address (in the dli_fname member).
Unfortunately, there is no way to do that using UNIX or POSIX. If you need to use it to look up some sort of data, you should use the $PATH environment variable and search for the data in a path that is relative to each entry in $PATH. For example, it is not uncommon to store binaries in "installdir/bin" for some installation directory "installdir" and to store the associated data in "installdir/share/name_of_program" for some installation directory and some program named "name_of_program". If that is the case, then looking at "../share/name_of_program/name_of_resource_file" relative to each entry in getenv("PATH") is a good way of searching for resources. Another thing you could do is allow the necessary information to be provided on the commandline or in some configuration file, and only perform the search if needed as a fallback option.
Edit
Now that you've stated your rationale for this, I would advise you to simply use the QSettings class from Qt for your configuration information, as it uses the preferred native mechanism for each platform (the registry on Windows, a PLIST file on Mac OS X, the Gnome GConf database on Linux). You may want to take a look at my C++ Project Template as it uses Qt to do just this, and it provides simple commandline options to easily tweak the configuration settings ("--prefset", "--prefget", and "--preflist" manipulate QSettings).
That said, if you absolutely must use an XML configuration file of your own instead of using the preferred native mechanism, I strongly advise you to place the system-wide configuration in "installdir/etc" while placing your library in "installdir/lib" for some installation directory "installdir", as that is the typical place for configuration files on UNIX systems, and "installdir/lib" should ONLY be used for library files, not for configuration files and other errata. I suggest you place a user-specific version of the configuration file in "$XDG_CONFIG_HOME" (if it is defined) or in "$HOME/.config" (where "$HOME" is the user's home folder).
When searching for the system-wide configuration file, I would recommend that you search within $XDG_CONFIG_DIRS if it is defined; if it isn't defined, then falling back to "/etc/xdg" or searching for "../etc/name_of_your_program.conf.xml" relative to "$PATH" and possibly also relative to the "$LD_LIBRARY_PATH", "$DYLD_LIBRARY_PATH", "$DYLD_FALLBACK_LIBRARY_PATH"), the contents of "/etc/ld.so.conf" if it exists, and the contents of "/etc/ld.so.conf.d/*.conf" if those files exist, halting your search as soon as you encounter the first valid such configuration file would be a sensible approach.
Credit goes to Roger for pointing out the XDG Basedir Spec and for his excellent constructive criticisms.
Possible solutions:
You can read the /proc/{PID}/mmap file for the list of shared libraries. Where {PID} is the process pid (you can get it using getpid()).
Call the command line tool ldd for the program binary file (stored in argv[0]).
If you write a solution from scratch take a look of ldd commands source code from uClibc how to get the list of shared libs from an elf binary.