Creating a Versioned Framework.1.23

  • View
    230

  • Download
    0

Embed Size (px)

Citation preview

  • 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