I have a static library Xcode 4 project that includes a home-brewed rendering engine, and I re-use this engine in multiple apps. This engine uses OpenGL ES 2.0, and by extension, shaders. As shaders got more complicated, I moved away from storing them as NSStrings in a source file, and now store them as standalone text files with the .vert and .frag extensions.
This works fine for apps that include the rendering engine in their own source; the shaders are simply added to the app's "Copy Bundle Resources" build phase, and loaded at runtime into NSStrings and compiled, linked, etc.
This strategy doesn't work at all if the rendering engine that loads these shaders is in a static library project; there is no bundle into which to copy resources. I'm currently forced to have every client project of the static lib rendering engine include their own copies of the shaders in their own "Copy Bundle Resources" build phase. This is a giant pain, and defeats a large part of the convenience of making the render engine into a static lib in the first place.
I suppose this i开发者_如何转开发s specific instance of the more general problem of "Resources in a Static Library". The best solution I can think of is copying the shader files' contents into strings in a header file, which are then included in the rendering engine's source. I may even be able to automate the conversion from .frag to .h with some "Run Scripts" build phase magic, but it seems unfortunately complicated.
Is there anything I'm missing?
For the benefit of posterity, I'll share the solution I ended up using. At a high level, the solution is to compile the resource in question into the application binary, thus obviating the need to also copy it to bundle resources.
I decided a generic and reliable way to compile any file data into the binary would be to store the file contents in a static byte array in a header file. Assuming there is already a header file created and added to the static lib target, I made the following bash script to read a file, and write its contents as a byte array of hex literals with C syntax. I then run the script in "Run Script" build phase before the Compile Sources and Copy Headers build phases:
#!/bin/bash
# Hexify.sh reads an input file, and hexdumps its contents to an output
# file in C-compliant syntax. The final argument is the name of the array.
infile=$1
outfile=$2
arrayName=$3
fileSize=$(stat -f "%z" $infile)
fileHexString=$(hexdump -ve '1/1 "0x%.2x, "' $infile)
prefix=$arrayName
suffix="Size"
variableName=$arrayName$suffix
nullTermination="0x00"
echo "//" > $headerFile
echo "// This file was automatically generated by a build script." >> $headerFile
echo "// Do not modify; the contents of this file will be overwritten on each build." >> $headerFile
echo "//" >> $headerFile
echo "" >> $headerFile;
echo "#ifndef some_arbitrary_include_guard" >> $headerFile
echo "#define some_arbitrary_include_guard" >> $headerFile
echo "" >> $headerFile
echo "static const int $variableName = $((fileSize+1));" >> $outfile
echo "static const char $arrayName[$variableName] = {" >> $outfile
echo -e "\t$fileHexString$nullTermination" >> $outfile
echo "};" >> $outfile
echo "#endif" >> $headerFile
So, for example, if I have a resource file example.txt:
Hello this
is a file
And I were to run ./Hexify.sh example.txt myHeader.h exampleArray
, the header would look like this:
//
// This file was automatically generated by a build script.
// Do not modify; the contents of this file will be overwritten on each build.
//
#ifndef some_arbitrary_include_guard
#define some_arbitrary_include_guard
static const int exampleArraySize = 21;
static const char exampleArray[exampleArraySize] = {
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x74, 0x68, 0x69, 0x73, 0x0a,
0x69, 0x73, 0x20, 0x61, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x00
};
#endif
Now, at any point in time that I would have loaded said resource from the main bundle, I can instead refer to the byte array in that header file. Note that my version of the script adds a null terminated byte, which makes the data suitable for creating string objects. That may not apply in all cases.
As one final addendum, I apologize if that bash script makes any real bash programmers cringe; I have almost no idea what I'm doing with bash.
I feel your pain buddy, static libraries and resources don't go well together. I think the easiest way to do this is the one you already mentioned: Write a script that reads your shaders, escapes them properly and wraps them in C-compliant code.
I'm no expert, but maybe you could add the shader data to some section of your Mach-O executable upon linkage? But this eventually boils down to the same solution as mentioned above, with the only disadvantage that you're left with the ugly part of the job.
I'd go for the string constants using some shell script. PHP in my experience is pretty good at doing this kind of work. And of course bash scripts, but I'm not too good at that.
You could try to create a framework, it seems to fit your needs. There's an example on how to create such a framework for iOS on this page:
http://db-in.com/blog/2011/07/universal-framework-iphone-ios-2-0/
The guy that wrote the guide actually uses this technique to distribute his own iOS 3D engine project.
Edit: linked to newer version of the guide.
精彩评论