creating a versioned framework.1.23
Post on 06-Apr-2018
231 views
TRANSCRIPT
-
8/3/2019 Creating a Versioned Framework.1.23
1/33
CreatingaVersioned,Documented
FrameworkforiOSusingXcode4,GITandDoxyGen
V1.23 TillToenshoff,2011
-
8/3/2019 Creating a Versioned Framework.1.23
2/33
Author .......................................................................................................................... 3Motivation .................................................................................................................... 3Results ......................................................................................................................... 3
The Release Folder .................................................................................................. 3The Framework ........................................................................................................ 3The Version Information ........................................................................................... 4The Documentation .................................................................................................. 5Status ....................................................................................................................... 6
Creating the Xcode project .......................................................................................... 7Framework and Versioning Run Scripts ...................................................................... 9
Building .................................................................................................................... 9Additional Files .......................................................................................................... 17
Re/Sources ............................................................................................................. 17Making use of that Version information...................................................................... 23
Main definition file preprocessor Define .............................................................. 23Main implementation file constant String............................................................. 23The Build Process ...................................................................................................... 24Debugging .............................................................................................................. 24Distribution ............................................................................................................. 24
Rendering a Documentation ...................................................................................... 25Building .................................................................................................................. 25
DoxyGen .................................................................................................................... 30Create a DoxyGen config file using the DoxyGen GUI frontend Wizard ................ 30
-
8/3/2019 Creating a Versioned Framework.1.23
3/33
AuthorTill [email protected]
MotivationCreating highly polished, complete and useful binary modules for reuse within Xcodeprojects. Plain static libraries would not be sufficient as those are not produced forboth, simulator and device compatibility. Creating both flavors manually and joiningthem using lipo is feasible but takes many manual steps. As that process can beautomated, why not going the extra mile and creating a proper framework togetherwith a real docset, usable from within Xcode. And now, that we intend to automatethe entire process, why not including an automated versioning scheme allowing us to
identify the exact version of the resulting framework.
Results
TheReleaseFolder
You will receive a folder consisting of the Framework and the DocSet.
TheFramework
The resulting Framework pretty much looks, feels and works like one of the Apple-supplied Frameworks. But unlike Apples Frameworks, this one contains a static,universal library.
-
8/3/2019 Creating a Versioned Framework.1.23
4/33
TheVersionInformation
The Framework will be versioned according to your most recent GIT-Tag, GIT-Patch
and the Build-Number. For example, lets assume you tagged your latest versionwithin GIT to 1.2 and you did two commits since that tagging happened. Now let ussuppose you have built that project 123 times. As a result, your BundleVersion(Info.plist) will be set towards 1.2.2.123.Additionally, your Info.plist will contain a CFBuildHash value which will be set towardsthe latest GIT-Commit-Hash. That way, you will always be able to identify the correctversion of your sources being used for a certain release.Within the Framework, you will, additionally, have a FRAMEWORK_VERSIONpreprocessor define telling you the current version and a string constant that shouldbe named something like k$(PROJECT_NAME)Version e.g.
kCellularDebuggingVersion.Last but not least, even your documentation will be containing this exact version-identifier on the front-page.
-
8/3/2019 Creating a Versioned Framework.1.23
5/33
TheDocumentation
The DoxyGen generated DocSet is usable just like any other DocSet supplied with
Xcode. For more information regarding DoxyGen and its syntax for automateddocumentation generation, see one of those DoxyGen manual pages.
Navigate to Xcode Help->Documentation and API Reference.
Now click on that Eye-Button and select your freshly created DocSet.
Alt-Click on any documented symbol within any opened source-file and your newdocumentation will be used right within the Xcode quick-reference feature.
The linked symbol will lead you right into the full context and description of thatsymbol.
-
8/3/2019 Creating a Versioned Framework.1.23
6/33
Documentation showing the Release-Tag, the Patch-Level, the Build-Counter (andthe GIT-Commit-Hash)
Status
The current solution is, in parts, a bit cumbersome and has its caveats the biggestbeing a lot of initial preparation. I feel that this initial work is totally worth it, especially
considering that it is a one-time-investment.
-
8/3/2019 Creating a Versioned Framework.1.23
7/33
CreatingtheXcodeproject
Create a new Xcode project
-
8/3/2019 Creating a Versioned Framework.1.23
8/33
Select a Cocoa Touch Static Library
-
8/3/2019 Creating a Versioned Framework.1.23
9/33
Enter a Product Name
FrameworkandVersioningRunScripts
Building
Select the project root item and switch to the project main target
Add a new Build Phase Run Script
-
8/3/2019 Creating a Versioned Framework.1.23
10/33
Expand the Run Script item
-
8/3/2019 Creating a Versioned Framework.1.23
11/33
Enter the following script:
Now drag this Run Script to the second build-phase, just after Target Dependencies.
# NOTE: the project has to include a Version.plist for storing some meta-info## First, we get the GIT-related information into our Version.plist#echo "Gathering GIT information"buildTag=$(git describe --long | cut -f 1 -d "-")buildPatch=$(git describe --long | cut -f 2 -d "-")buildHash=$(git describe --long | cut -f 3 -d "-")
buildVersion="$buildTag.$buildPatch"
## Now, we will be raising a build-counter#echo "Raising build-counter"versionPlist=${SRCROOT}/Version.plistbuildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBuildNumber" $versionPlist)
buildNumber=$((buildNumber+1))
echo "build version will be: $buildVersion.$buildNumber"
## Stashing some of the freshly gathered information#/usr/libexec/PlistBuddy -c "Set :CFBuildNumber $buildNumber" $versionPlist/usr/libexec/PlistBuddy -c "Set :CFBuildVersion $buildVersion" $versionPlist/usr/libexec/PlistBuddy -c "Set :CFBuildHash $buildHash" $versionPlist
## This one renders a version.h-file containing the version-identifier aspreprocessor define
#echo "Rendering version.h"echo "#define FRAMEWORK_VERSION @\"$buildVersion.$buildNumber ($buildHash)\"" >${SRCROOT}/version.h
-
8/3/2019 Creating a Versioned Framework.1.23
12/33
Add another Run Script to the build process.
-
8/3/2019 Creating a Versioned Framework.1.23
13/33
Enter the following script
#################################################################### Cellular Documented Framework Build Script# Author: Till Toenshoff, Cellular GmbH# Based on the work of Eonil, Adam Martin, Oliver Drobnik, Netytan############################################################################################# UNIVERSAL LIBRARY######################### original: http://stackoverflow.com/questions/3520977/build-fat-static-library-device-simulator-using-xcode-and-sdk-4## Author: Adam Martin - http://twitter.com/redglassesapps# Based on: original script from Eonil (main changes: Eonil's script WILL NOTWORK in Xcode GUI - it WILL CRASH YOUR COMPUTER)#
# More info: see this Stack Overflow question:http://stackoverflow.com/questions/3520977/build-fat-static-library-device-simulator-using-xcode-and-sdk-4
#####################[ part 1 ]################### First, work out the BASESDK version number (NB: Apple ought to report this, butthey hide it)# (incidental: searching for substrings in sh is a nightmare! Sob)
SDK_VERSION=$(echo ${SDK_NAME} | grep -o '.\{3\}$')
# Next, work out if we're in SIM or DEVICE
if [ ${PLATFORM_NAME} = "iphonesimulator" ]thenOTHER_SDK_TO_BUILD=iphoneos${SDK_VERSION}elseOTHER_SDK_TO_BUILD=iphonesimulator${SDK_VERSION}fi
echo "XCode has selected SDK: ${PLATFORM_NAME} with version: ${SDK_VERSION}(although back-targetting: ${IPHONEOS_DEPLOYMENT_TARGET})"echo "...therefore, OTHER_SDK_TO_BUILD = ${OTHER_SDK_TO_BUILD}"######################[ end of part 1 ]##################
-
8/3/2019 Creating a Versioned Framework.1.23
14/33
#####################[ part 2 ]##################
## IF this is the original invocation, invoke WHATEVER other builds are required## Xcode is already building ONE target...## ...but this is a LIBRARY, so Apple is wrong to set it to build just one.# ...we need to build ALL targets# ...we MUST NOT re-build the target that is ALREADY being built: Xcode WILLCRASH YOUR COMPUTER if you try this (infinite recursion!)### So: build ONLY the missing platforms/configurations.
if [ "true" = ${ALREADYINVOKED} ]
thenecho "RECURSION: I am NOT the root invocation, so I'm NOT going to recurse"else# CRITICAL:# Prevent infinite recursion (Xcode sucks)export ALREADYINVOKED="true"
echo "RECURSION: I am the root ... recursing all missing build targets NOW..."echo "RECURSION: ...about to invoke: xcodebuild -configuration\"${CONFIGURATION}\" -target \"${TARGET_NAME}\" -sdk \"${OTHER_SDK_TO_BUULD}\"${ACTION} RUN_CLANG_STATIC_ANALYZER=NO"xcodebuild -configuration "${CONFIGURATION}" -target "${TARGET_NAME}" -sdk"${OTHER_SDK_TO_BUILD}" ${ACTION} RUN_CLANG_STATIC_ANALYZER=NOfi
ACTION="build"
#Merge all platform binaries as a fat binary for each configurations.
CURRENTCONFIG_DEVICE_DIR=${SYMROOT}/${CONFIGURATION}-iphoneosCURRENTCONFIG_SIMULATOR_DIR=${SYMROOT}/${CONFIGURATION}-iphonesimulatorCURRENTCONFIG_UNIVERSAL_DIR=${SYMROOT}/${CONFIGURATION}-universal
# ... remove the products of previous runs of this script# NB: this directory is ONLY created by this script - it should be safe todelete!
rm -rf "${CURRENTCONFIG_UNIVERSAL_DIR}"
mkdir "${CURRENTCONFIG_UNIVERSAL_DIR}"
#echo "lipo: for current configuration (${CONFIGURATION}) creating output file:${CURRENTCONFIG_UNIVERSAL_DIR}/${EXECUTABLE_NAME}"lipo -create -output "${CURRENTCONFIG_UNIVERSAL_DIR}/${EXECUTABLE_NAME}""${CURRENTCONFIG_DEVICE_DIR}/${EXECUTABLE_NAME}""${CURRENTCONFIG_SIMULATOR_DIR}/${EXECUTABLE_NAME}"
echo "Done with creating a universal library
-
8/3/2019 Creating a Versioned Framework.1.23
15/33
######################### FRAMEWORK######################### original: http://www.cocoanetics.com/2010/05/making-your-own-iphone-frameworks-in-xcode/
echo "Building framework..."
# name and build locationFRAMEWORK_NAME=${PROJECT_NAME}FRAMEWORK_BUILD_PATH="${PROJECT_DIR}/build/Framework"
# these never changeFRAMEWORK_VERSION=A
FRAMEWORK_CURRENT_VERSION=1FRAMEWORK_COMPATIBILITY_VERSION=1
# Clean any existing framework that might be thereif [ -d "$FRAMEWORK_BUILD_PATH" ]thenecho "Framework: Cleaning framework..."rm -rf "$FRAMEWORK_BUILD_PATH"fi
# Build the canonical Framework bundle directory structureecho "Framework: Setting up directories..."FRAMEWORK_DIR=$FRAMEWORK_BUILD_PATH/$FRAMEWORK_NAME.frameworkexport FRAMEWORK_DIR
mkdir -p $FRAMEWORK_DIRmkdir -p $FRAMEWORK_DIR/Versionsmkdir -p $FRAMEWORK_DIR/Versions/$FRAMEWORK_VERSIONmkdir -p $FRAMEWORK_DIR/Versions/$FRAMEWORK_VERSION/Resourcesmkdir -p $FRAMEWORK_DIR/Versions/$FRAMEWORK_VERSION/Headers
echo "Framework: Creating symlinks..."ln -s $FRAMEWORK_VERSION $FRAMEWORK_DIR/Versions/Currentln -s Versions/Current/Headers $FRAMEWORK_DIR/Headersln -s Versions/Current/Resources $FRAMEWORK_DIR/Resourcesln -s Versions/Current/$FRAMEWORK_NAME $FRAMEWORK_DIR/$FRAMEWORK_NAME
echo "Framework: Copying library..."cp "${CURRENTCONFIG_UNIVERSAL_DIR}/${EXECUTABLE_NAME}""$FRAMEWORK_DIR/Versions/Current/$FRAMEWORK_NAME"
echo "Framework: Copying assets into current version..."cp ${SRCROOT}/*.h "$FRAMEWORK_DIR/Headers"
-
8/3/2019 Creating a Versioned Framework.1.23
16/33
Note: This script will install the framework within your CellularFrameworks folder asdefined below.
Go to Xcode->Preferences->Source Trees and add a new variable using that plus-icon on the bottom of the screen.
echo "Fetching build version info"versionPlist=${SRCROOT}/Version.plistbuildVersion=$(/usr/libexec/PlistBuddy -c "Print CFBuildVersion" $versionPlist)buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBuildNumber" $versionPlist)
buildHash=$(/usr/libexec/PlistBuddy -c "Print CFBuildHash" $versionPlist)
echo "Rendering info.plist"buildPlist=$FRAMEWORK_DIR/Resources/Info.plistcat "${SRCROOT}/Framework.plist" | sed 's/${PROJECT_NAME}/'"${PROJECT_NAME}"'/' >$buildPlist/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildVersion.$buildNumber"$buildPlist/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString$buildVersion.$buildNumber" $buildPlist/usr/libexec/PlistBuddy -c "Set :CFBuildHash $buildHash" $buildPlist
echo "Copying results"if [ ! -d "${CELLULAR_FRAMEWORKS_PATH}/${PROJECT_NAME}" ]; thenmkdir -p "${CELLULAR_FRAMEWORKS_PATH}/${PROJECT_NAME}"ficp -rf $FRAMEWORK_DIR "${CELLULAR_FRAMEWORKS_PATH}/${PROJECT_NAME}"exit 0
-
8/3/2019 Creating a Versioned Framework.1.23
17/33
CELLULAR_FRAMEWORKS_PATH should point towards the location you intend touse for keeping a distributable version of your framework
AdditionalFiles
Re/Sources
Add a new Property List resource
-
8/3/2019 Creating a Versioned Framework.1.23
18/33
Name it Framework.plist
Make sure it contains the following
CFBundleDevelopmentRegionEnglishCFBundleExecutable${PROJECT_NAME}CFBundleIdentifiercom.yourcompany.${PROJECT_NAME}CFBundleInfoDictionaryVersion6.0CFBundlePackageTypeFMWKCFBundleShortVersionStringCFBundleSignature
????CFBundleVersionCFBuildHash
-
8/3/2019 Creating a Versioned Framework.1.23
19/33
Add another, new Property List resource
-
8/3/2019 Creating a Versioned Framework.1.23
20/33
Name it Version.plist
-
8/3/2019 Creating a Versioned Framework.1.23
21/33
Enter the following into the new Version.plist-file
Add a new Header file named version.h to your project actually, this step is optionalas the file will be created/rendered by the build-process. This is just for making surethat you make a note of that header-file and the ongoing changes within. Just makesure that you do not enter anything of value into this header as it will be rewritten oneach and every build.
CFBuildHashCFBuildNumber1CFBuildTagCFBuildVersion
-
8/3/2019 Creating a Versioned Framework.1.23
22/33
Your project browser should now look somewhat like the following
-
8/3/2019 Creating a Versioned Framework.1.23
23/33
MakinguseofthatVersioninformation
MaindefinitionfilepreprocessorDefineEdit the project main header file (CellularDebugging.h within this example).
MainimplementationfileconstantString
Edit the project main implementation file (CellularDebugging.m within this example).
#undef FRAMEWORK_VERSION#import "version.h"
NSString * const kCellularDebuggingVersion = FRAMEWORK_VERSION;
#undef FRAMEWORK_VERSION#import "version.h"#define CELLULARMOVIEPLAYER_VERSION FRAMEWORK_VERSION
extern NSString * const kCellularDebuggingVersion;
-
8/3/2019 Creating a Versioned Framework.1.23
24/33
TheBuildProcess
DebuggingFor testing, build a regular Debug version (Build For Testing).Note: It does not matter whether you select Device or Simulator building as the build-scripts will always build both.
Distribution
For distribution. build an Archive version (Build For Archiving).Note: It does not matter whether you select Device or Simulator building as the build-scripts will always build both.
-
8/3/2019 Creating a Versioned Framework.1.23
25/33
RenderingaDocumentation
Building
Add a new Target
Make it an Aggregate Target
-
8/3/2019 Creating a Versioned Framework.1.23
26/33
Name the Product
Select the new Targets Build Phases
Add a new Build Run Script as done before.
-
8/3/2019 Creating a Versioned Framework.1.23
27/33
Enter the following script:
echo "Fetching build version info"versionPlist=${SRCROOT}/Version.plistbuildVersion=$(/usr/libexec/PlistBuddy -c "Print CFBuildVersion" $versionPlist)buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBuildNumber" $versionPlist)
buildHash=$(/usr/libexec/PlistBuddy -c "Print CFBuildHash" $versionPlist)buildTag=$(/usr/libexec/PlistBuddy -c "Print CFBuildTag" $versionPlist)
######################### DOXYGEN######################### Preconditions:# - needs DoxyGen to be installed# see http://www.stack.nl/~dimitri/doxygen/download.html#latestsrc# - needs a doxygen.config within the source-root-folder# - needs a custom environment variable setup within the project build-settingscalled DOXYGEN_PATH# see http://developer.apple.com/tools/creatingdocsetswithdoxygen.htmlecho "running documentation build scripts..."
# Build the doxygen documentation for the project and load the docset intoXcode.# Use the following to adjust the value of the $DOXYGEN_PATH User-DefinedSetting:# Binary install location:/Applications/Doxygen.app/Contents/Resources/doxygen# Source build install location: /usr/local/bin/doxygen# If the config file doesn't exist, run 'doxygen -g $SOURCE_ROOT/doxygen.config'to# a get default file.
if ! [ -f $SOURCE_ROOT/doxygen.config ]thenecho doxygen config file does not exist
$DOXYGEN_PATH -g $SOURCE_ROOT/doxygen.configfi
# Append the proper input/output directories and docset info to the config file.# This works even though values are assigned higher up in the file. Easier thansed.
cp $SOURCE_ROOT/doxygen.config $TEMP_DIR/doxygen.config
echo "SOURCE_ROOT = $SOURCE_ROOT";echo "TEMP_DIR = $TEMP_DIR";echo "DOXYGEN_PATH = $DOXYGEN_PATH";echo "INPUT = $SOURCE_ROOT" >> $TEMP_DIR/doxygen.configecho "OUTPUT_DIRECTORY = $SOURCE_ROOT/DoxygenDocs.docset" >>$TEMP_DIR/doxygen.configecho "GENERATE_DOCSET = YES" >> $TEMP_DIR/doxygen.configecho "DOCSET_BUNDLE_ID = de.cellular.$PROJECT_NAME" >>$TEMP_DIR/doxygen.configecho "PROJECT_NUMBER = $buildVersion.$buildNumber ($buildHash)" >>$TEMP_DIR/doxygen.config
-
8/3/2019 Creating a Versioned Framework.1.23
28/33
Make sure the variable DOXYGEN_PATH is setup properly in your Xcodepreferences.
Go to Xcode->Preferences->Source Trees and add a new variable using that plus-icon on the bottom of the screen.
# Run doxygen on the updated config file.
# Note: doxygen creates a Makefile that does most of the heavy lifting.echo "running doxygen..."$DOXYGEN_PATH $TEMP_DIR/doxygen.config# make will invoke docsetutil. Take a look at the Makefile to see how this isdone.
echo "make doxygen html install..."make -C $SOURCE_ROOT/DoxygenDocs.docset/html install
exit 0
-
8/3/2019 Creating a Versioned Framework.1.23
29/33
DOXYGEN_PATH should point towards the doxygen executeable normally located at/Applications/Doxygen.app/Contents/Resources/doxygen
Add another Run Script
This script bundles the Framework with its documentation within the installationfolder.
#### Copy results to public location##echo "Copying results..."cp -rf"/Users/${USER}/Library/Developer/Shared/Documentation/DocSets/de.cellular.$PROJECT_NAME.docset""${CELLULAR_FRAMEWORKS_PATH}/${PROJECT_NAME}"echo "Done copying results"
-
8/3/2019 Creating a Versioned Framework.1.23
30/33
DoxyGen
CreateaDoxyGenconfigfileusingtheDoxyGenGUIfrontendWizard
Enter a proper working directory, project name, project synopsis. Enter . for theSource code directory.
-
8/3/2019 Creating a Versioned Framework.1.23
31/33
Select Documented entities only and Optimize for C++ output.
Check HTML and uncheck LaTeX
-
8/3/2019 Creating a Versioned Framework.1.23
32/33
Select Use built-in class diagram generator
Now switch over to the Expert tab and select HTML.
Make sure that GENERATE_DOCSET is checked. You may now specify properDOCSET-metadata like FEEDNAME, BUNDLE_ID, PUBLISHER_ID andPUBLISHER_NAME.
Save the DoxyGen configuration file within your source-root-folder and name itdoxygen.config.
-
8/3/2019 Creating a Versioned Framework.1.23
33/33
For an initial test, you may now select Run within DoxyGen. The results should looksomewhat like the following;
If all went smooth, you may now go ahead and try to full monty