Reputation: 559
I was writing a "Hello Rectangle" OpenGL application. I want it to be a standalone .app
instead of just a "UNIX executable." The code runs fine from Terminal.app.
I didn't use Xcode; I'm very tight on disk space and I'm not doing iOS dev.
Instead I'm using brewed toolchain, including gcc-8
, glfw
and glew
.
I structured my release dir according to this SO question as follows:
Contents
├── Frameworks
│ ├── libGLEW.2.1.dylib
│ ├── libgcc_s.1.dylib
│ ├── libglfw.3.dylib
│ └── libstdc++.6.dylib
├── MacOS
│ ├── hello-rect <- the actual binary
│ └── launcher.sh <- entry wrapper for working dir as the above link suggests
├── Resources
│ ├── rect.frag
│ └── rect.vert
└── Info.plist
I also modified the reference to dylibs in binary. But it won't run.
After a few hours of Googling, this SO answer and this SO question and several others convinced me that I should use CoreFoundation
to read those two shader files (text file) in Resources
.
I tried following code (pasted from the link above with slight modification), but it gets segfault:
const char* textFileReadCF(const char *&filename){
// Get a reference to the main bundle
CFBundleRef mainBundle = CFBundleGetMainBundle();
// Get a reference to the file's URL
CFStringRef filenameHandle = CFStringRef(filename);
// ------ segfault 11 here ------
CFURLRef fileURL = CFBundleCopyResourceURL(mainBundle, filenameHandle, NULL, NULL);
// Convert the URL reference into a string reference
CFStringRef filePath = CFURLCopyFileSystemPath(fileURL, kCFURLPOSIXPathStyle);
// Get the system encoding method
CFStringEncoding encodingMethod = CFStringGetSystemEncoding();
// Convert the string reference into a C string
const char *path = CFStringGetCStringPtr(filePath, encodingMethod);
// the real read function, read in file and returns all as a big char* arr
return textFileRead(path);
}
// this runs fine before packing and I didn't change,
// just for the sake of completeness
const char *textFileRead(const char *filename){
std::ifstream shaderFile(filename);
std::ostringstream shaderBuffer;
shaderBuffer << shaderFile.rdbuf();
std::string shaderBufferStr = shaderBuffer.str();
// Warning: safe only until shaderBufferStr is destroyed or modified
char * ret = new char[shaderBufferStr.size()];
std::strcpy(ret, shaderBufferStr.c_str());
return ret;
}
textFileReadCF
will be called twice with "rect.vert"
and "rect.vert"
(not literal, but const char*
holding them).
I have no idea why it goes segfault. I made sure there is one mainBundle
through assert
(omitted), and that filenameHandle
is valid as I can tell, but otherwise I don't know where to look.
Upvotes: 0
Views: 736
Reputation: 559
Turns out I do not need CoreFoundation
. I just need GLFW
.
In an earlier version, my release dir looked like this:
Contents
├── Frameworks
│ ├── libGLEW.2.1.dylib
│ ├── libgcc_s.1.dylib
│ ├── libglfw.3.dylib
│ └── libstdc++.6.dylib
├── MacOS
│ ├── hello-rect <- the actual binary
│ ├── launcher.sh <- entry wrapper for working dir as the above link suggests
│ ├── rect.frag
│ └── rect.vert
└── Info.plist
Because the unbundled version obviously looks nowhere but cwd
.
I didn't even know there is Resources
. At that time my code also didn't run; my Google trace let me believe that CoreFoundation
is the solution. Only until I checked docs about CoreFoundation
did I know there should be Resources
. And only by chance did I come across this SO question, when all the dots connected.
Apparently my version of GLFW
also behaves like the link states: (directly from source)
// Change to our application bundle's resources directory, if present
So all it takes to make the bundle run out of box is, just structure the release dir like in question, and directly call textFileRead
with "rect.vert"
and "rect.vert"
, and we're all set.
Upvotes: 1