An XCode project template to build universal frameworks (arm7, arm7s, and simulator) for iOS / iPhone.
An XCode project template to build universal frameworks (arm7, arm7s, and simulator) for iOS / iPhone.
With Xcode 6, Apple has added iOS framework support to their build tools, so this repo can at last be retired!
Please use Apple's framework target for all new projects, as it is less hacky and is supported by Apple themselves.
An XCode project template to build universal (arm6, arm7, and simulator) frameworks for iOS.
By Karl Stenerud
I haven't been able to solve the problem of deeply nested projects within projects, but the new python scripts have been working in my other projects for over a year now and are quite stable for 90% of use cases.
Unfortunately, I don't have the time to solve the last 10% of use cases. As a compromise, I've created a branch "mk7" which contains the shell script version of the build system. If Mk8 doesn't work for your unique case, give Mk7 a try.
Development will continue in order to keep things working for the other 90% of use cases.
If you can help, please feel free to contact me or send pull requests. All scripting is done in Python now. All template development happens within the "devel" directory. build.py builds the templates and all template source files are in "src".
You can now update existing projects to use the newest build scripts. Running the update_project.py script will replace your project's universal framework build script with the script in devel/src/BuildFW.py.
Before upgrading, please back up your project file!
Steps to Upgrade (Mk 7 or earlier):If your project was built using Mk 7 or earlier, delete the first two universal framework build scripts. The first will be right after "Target Dependencies" and starts with the following (or something close):
set -e
set +u
if [[ $UFW_MASTER_SCRIPT_RUNNING ]]
then
# Nothing for the slave script to do
exit 0
fi
set -u
The second script is after "Copy Bundle Resources" and starts with the following (or something close). Note that this script may not exist in very early versions of the framework project:
HEADERS_ROOT=$SRCROOT/$PRODUCT_NAME
FRAMEWORK_HEADERS_DIR="$BUILT_PRODUCTS_DIR/$WRAPPER_NAME/Versions/$FRAMEWORK_VERSION/Headers"
## only header files expected at this point
PUBLIC_HEADERS=$(find $FRAMEWORK_HEADERS_DIR/. -not -type d 2> /dev/null | sed -e "s@.*/@@g")
The final script (the one you want to keep) will start with something similar to the first script you deleted.
Now proceed with the next steps below.
Steps to Upgrade (All versions)Make sure the top of the "Run Script" phase for the universal framework script starts with the following comment: "# TAG: BUILD SCRIPT". If it doesn't, add it in!
Close your project
Run the project update script from shell: $ ./update_project.py ~/Projects/MyProj/MyProj.xcodeproj/project.pbxproj
Reopen your project
The project update script will create a backup (project.pbxproj.orig) of the old project file. To disable this behavior, use the "-n" switch.
The script now requires you to select which kind of framework (normal or embedded) you will be creating, using the config_framework_type configuration variable. Only the selected framework type will be created and shown to the user.
Note: Xcode requires the normal framework dir to exist, so when building an embedded framework, the script simply creates a symlink to the copy inside the embeddedframework. Be sure to tell your users not to to copy the regular "framework" symlink by mistake!
When you build normally (by selecting Build or CMD-B), the project will NO LONGER build a universal framework. It will build for the CURRENT ARCHITECTURE ONLY!
To build a universal framework, you must select Archive from the Product menu. Upon completing the archive build, it will automatically open the folder containing the fully built framework.
This cuts the compilation time down by 2/3, since it no longer has to do a full build process when building as a dependency.
Since "archive" is not a supported xcodebuild build action, you must specify the env variable "UFW_ACTION=archive" in your xcodebuild command to build it as a universal framework.
To avoid opening the destination folder when building from command line, set the env variable "UFW_OPEN_BUILD_DIR=False" in your xcodebuild command.
The initial beta version had 2 scripts: a clean script and a build script. Mk 7 has 3 scripts. With the new build process there is only need for one script.
When Xcode creates the initial header and module file for a framework, the header file won't be included as a member of the framework target (This is a bug in Xcode; it does the same thing with Mac frameworks), so you need to do this manually. In Build Phases under Copy Headers, click the + and add the header, then drag it to the Public section.
The Run Script build phases will have the option Show environment variables in build log checked. A bug in Xcode causes it to ignore the template setting and leave it checked always. This can cause issues when diagnosing a build failure because Xcode will only show the first 200 log entries in a build phase, most of which are taken up by spitting out all of the environment variables! So be sure to turn it off manually.
So to sum up, when starting a new framework project, always do the following:
Distributing libraries in a developer-friendly manner is tricky. You need to include not only the library itself, but also any public include files, resources, scripts etc.
Apple's solution to this problem is frameworks, which are basically folders that follow a standard structure to include everything required to use a library. Unfortunately, in disallowing dynamically linked libraries in iOS, Apple also removed static iOS framework creation functionality in XCode.
Xcode is still technically capable of building frameworks for iOS, and with a little tweaking it can be re-enabled.
Static frameworks are perfectly acceptable for packaging code intended for the app store. Despite appearances, it's just a static library at the core.
A dynamic framework is designed to be installed in your operating system and shared by many programs. By default, Xcode only supports dynamic frameworks, and only for Mac since you can't use dynamic frameworks in iOS.
A static framework gets linked into your app like a static library would. However, Xcode doesn't include support for static frameworks. These templates add in that support. Frameworks are superior to libraries because they can include code as well as public headers in a single package.
Although frameworks are an improvement over libraries, Xcode ignores any resources contained within frameworks. So if you have xibs, images, sounds, or other files in your framework, Xcode won't see them. An embedded framework is a way to trick Xcode into seeing the included resources. As far as Xcode is concerned, they are simply folders, and so there are a few minor issues with embedded frameworks:
They don't show up in the Products group.
When you delete an embedded framework from a project, Xcode will not delete the outer folder (XX.embeddedframework), so if you try to re-add later, it will complain. You need to manually delete the XX.embeddedframework folder manually using Finder.
Things get a little tricky when you have a framework project as a dependency if your framework has resources that the parent project needs. You may need to manually add the resources to the parent or sibling project.
In this distribution are two template systems, each with their strengths and weaknesses. You should choose whichever one best suits your needs and constraints (or just install both).
The biggest difference is that Xcode can't build real frameworks unless you install the static framework xcspec file inside the Xcode app, which might be a dealbreaker for some (this applies to the PROJECT, not the framework itself).
Note: Both types will build the exact same binary. The only difference is in how Xcode treats the project.
I don't want to modify Xcode: Fake framework
I'm just distributing the final framework binary (not the project): Either kind
I'm distributing my framework project to other developers who may not want to modify Xcode: Fake framework
I need to set up the framework project as a dependency of another project (in a workspace or as a child project): Real framework (or Fake framework, using the -framework trick - see below)
The fake framework is based on the well known "relocatable object file" bundle hack, which tricks Xcode into building something that mostly resembles a framework, but is really a bundle.
The fake framework template takes this a step further, using some scripting to generate a real static framework (based on a static library rather than a relocatable object file). However, the framework's project still defines it to be of type 'wrapper.cfbundle', which makes it a second class citizen according to Xcode.
So while it produces a proper static framework that works just as well as a "real" static framework, things can get tricky when you have dependencies.
If you're just setting up a standalone project, then you're not using dependencies, so there's no problem.
If, however, you use project dependencies (such as in workspaces), Xcode won't be happy. The fake framework won't show up in the list when you click the '+' button under "Link Binary With Libraries" in your main application project. You can manually drag it from "Products" under your fake framework project to add the dependency.
Note: In older versions of Xcode, you'd get warnings like the following:
warning: skipping file '/somewhere/MyFramework.framework'
(unexpected file type 'wrapper.cfbundle' in Frameworks & Libraries build phase)
This would be followed by linker errors for anything in your fake framework. As of Xcode 4.3.1, this doesn't seem to happen anymore.
If you do encounter this issue, you can work around it by adding a "-framework" switch with your framework's name in "Other Linker Flags" in the project that uses the framework:
-framework MyFramework
It won't get rid of the warning, which is annoying, but it does link properly.
The real framework is real in every sense of the word. It is a true static framework made by re-introducing specifications that Apple left out of Xcode.
In order to be able to build a real framework project, you must install an xcspec file inside the Xcode installation.
If you are releasing a project (rather than the built product) that builds a real framework, anyone who wishes to build that framework must also install the xcspec file (using the install script in this distribution) so that their Xcode can understand the target type.
Note: If all you're doing is distributing the fully built framework, and not the framework's project, then the end user doesn't need to install anything.
I've submitted a report to Apple in the hopes that they'll update the specification files in Xcode, but that could take
No open issues yet, or sync has not completed.