イナヅマTVログ

Static Library

[iOS][Objective-C][Xcode 4.6.3] そうだStatic Library(.a)を作ってみよう

| 0件のコメント

Xcode 4.6.3でiOS Static Library(.a)を作る過程を忘れないためのメモ。
Apple: Introduction to Using Static Libraries in iOS

Static Libraryプロジェクトを作る

[File] > New > Project (command+shift+N)

Choose a template for your new project
Framework & Library > Cocoa Touch Static Library
Static Library

Choose options for your new project
Product Name: InazumaUtils
Choose options for your new project

ファイル構成
Project Navigator
InazumaUtils.h

#import <Foundation/Foundation.h>
 
@interface InazumaUtils : NSObject
 
+(NSString *)bundleID;
+(NSString *)bundlePath;
+(NSArray *)allBundles;
 
@end

InazumaUtils.m

#import "InazumaUtils.h"
 
@implementation InazumaUtils
 
+(NSString *)bundleID
{
    return [[NSBundle mainBundle] bundleIdentifier];
}
 
+(NSString *)bundlePath
{
    return [[NSBundle mainBundle] bundlePath];
}
 
+(NSArray *)allBundles
{
    return [NSBundle allBundles];
}
 
@end

IZDevice.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
 
@interface IZDevice : NSObject
 
+(NSString *)model;
+(NSString *)name;
+(NSString *)version;
+(NSString *)system;
 
@end

IZDevice.m

#import "IZDevice.h"
 
@implementation IZDevice
 
+(NSString *)model
{
    return [[UIDevice currentDevice] model];
}
 
+(NSString *)name
{
    return [[UIDevice currentDevice] name];
}
 
+(NSString *)version
{
    return [[UIDevice currentDevice] systemVersion];
}
 
+(NSString *)system
{
    return [[UIDevice currentDevice] systemName];
}
 
@end

わざわざ複数クラスにするような内容ではないのですが、実際は数十のクラス構成になっています。

Build Settings

■ Linker設定
Linking > Other Linker Flags
-ObjC が初期設定されている。
-all_loadを追加
・-ObjCをダブルクリック
・ポップアップウインドウが開く
・下部の[+]ボタンを押し追加
Other Linker Flags
Apple: Building Objective-C static libraries with categories

■ Header Search設定
Search Paths > Header Search Paths
Header Search Paths
$(BUILT_PRODUCTS_DIR)

TARGETS

[Product] > Build(command+B)
エラーが無ければInazumaUtils.aができる。
InazumaUtils.a

シミュレータでも使えるようにビルド

上記までの方法で書き出された.aファイルはiOS(armv7)端末専用のものらしい。
iOS端末上でなら使えるがシミュレータ上では使えない。
シミュレータでもテストしたい場合はOS X(i386)でも使えるように作成しないといけないらしい。

ビルド後シェルスクリプトを走らせて対応させるとStack Overflowで教えてもらう。
世界の知恵が集まってる、毎度のことながら感謝!
Stack Overflow: Build fat static library (device + simulator) using Xcode and SDK 4+

Add Build Phase > Add Run Script
Add Run Script

gistでスクリプトを公開してくれてる方がいるのでありがたく使わせていただく。
adamgit / gist:3705459

Automatically create cross-platform (simulator + device) static libraries for Objective C / iPhone / iPad

##########################################
#
# c.f. http://stackoverflow.com/questions/3520977/build-fat-static-library-device-simulator-using-xcode-and-sdk-4
#
# Version 2.7
#
# Latest Change:
# - Supports iPhone 5 / iPod Touch 5 (uses Apple's workaround to lipo bug)
# 
# Purpose:
#   Automatically create a Universal static library for iPhone + iPad + iPhone Simulator from within XCode
#
# Author: Adam Martin - http://twitter.com/t_machine_org
# Based on: original script from Eonil (main changes: Eonil's script WILL NOT WORK in Xcode GUI - it WILL CRASH YOUR COMPUTER)
#
 
set -e
set -o pipefail
 
#################[ Tests: helps workaround any future bugs in Xcode ]########
#
DEBUG_THIS_SCRIPT="false"
 
if [ $DEBUG_THIS_SCRIPT = "true" ]
then
echo "########### TESTS #############"
echo "Use the following variables when debugging this script; note that they may change on recursions"
echo "BUILD_DIR = $BUILD_DIR"
echo "BUILD_ROOT = $BUILD_ROOT"
echo "CONFIGURATION_BUILD_DIR = $CONFIGURATION_BUILD_DIR"
echo "BUILT_PRODUCTS_DIR = $BUILT_PRODUCTS_DIR"
echo "CONFIGURATION_TEMP_DIR = $CONFIGURATION_TEMP_DIR"
echo "TARGET_BUILD_DIR = $TARGET_BUILD_DIR"
fi
 
#####################[ part 1 ]##################
# First, work out the BASESDK version number (NB: Apple ought to report this, but they 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" ]
then
OTHER_SDK_TO_BUILD=iphoneos${SDK_VERSION}
else
OTHER_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 ]##################
 
#####################[ 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 WILL CRASH YOUR COMPUTER if you try this (infinite recursion!)
#
#
# So: build ONLY the missing platforms/configurations.
 
if [ "true" == ${ALREADYINVOKED:-false} ]
then
echo "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}\" -project \"${PROJECT_NAME}.xcodeproj\" -target \"${TARGET_NAME}\" -sdk \"${OTHER_SDK_TO_BUILD}\" ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO" BUILD_DIR=\"${BUILD_DIR}\" BUILD_ROOT=\"${BUILD_ROOT}\" SYMROOT=\"${SYMROOT}\"
 
xcodebuild -configuration "${CONFIGURATION}" -project "${PROJECT_NAME}.xcodeproj" -target "${TARGET_NAME}" -sdk "${OTHER_SDK_TO_BUILD}" ${ACTION} RUN_CLANG_STATIC_ANALYZER=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" SYMROOT="${SYMROOT}"
 
ACTION="build"
 
#Merge all platform binaries as a fat binary for each configurations.
 
# Calculate where the (multiple) built files are coming from:
CURRENTCONFIG_DEVICE_DIR=${SYMROOT}/${CONFIGURATION}-iphoneos
CURRENTCONFIG_SIMULATOR_DIR=${SYMROOT}/${CONFIGURATION}-iphonesimulator
 
echo "Taking device build from: ${CURRENTCONFIG_DEVICE_DIR}"
echo "Taking simulator build from: ${CURRENTCONFIG_SIMULATOR_DIR}"
 
CREATING_UNIVERSAL_DIR=${SYMROOT}/${CONFIGURATION}-universal
echo "...I will output a universal build to: ${CREATING_UNIVERSAL_DIR}"
 
# ... remove the products of previous runs of this script
#      NB: this directory is ONLY created by this script - it should be safe to delete!
 
rm -rf "${CREATING_UNIVERSAL_DIR}"
mkdir "${CREATING_UNIVERSAL_DIR}"
 
#
echo "lipo: for current configuration (${CONFIGURATION}) creating output file: ${CREATING_UNIVERSAL_DIR}/${EXECUTABLE_NAME}"
xcrun -sdk iphoneos lipo -create -output "${CREATING_UNIVERSAL_DIR}/${EXECUTABLE_NAME}" "${CURRENTCONFIG_DEVICE_DIR}/${EXECUTABLE_NAME}" "${CURRENTCONFIG_SIMULATOR_DIR}/${EXECUTABLE_NAME}"
 
#########
#
# Added: StackOverflow suggestion to also copy "include" files
#    (untested, but should work OK)
#
echo "Fetching headers from ${PUBLIC_HEADERS_FOLDER_PATH}"
echo "  (if you embed your library project in another project, you will need to add"
echo "   a "User Search Headers" build setting of: (NB INCLUDE THE DOUBLE QUOTES BELOW!)"
echo '        "$(TARGET_BUILD_DIR)/usr/local/include/"'
if [ -d "${CURRENTCONFIG_DEVICE_DIR}${PUBLIC_HEADERS_FOLDER_PATH}" ]
then
mkdir -p "${CREATING_UNIVERSAL_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"
# * needs to be outside the double quotes?
cp -r "${CURRENTCONFIG_DEVICE_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"* "${CREATING_UNIVERSAL_DIR}${PUBLIC_HEADERS_FOLDER_PATH}"
fi
fi

シェルスクリプト追加
Run Script

“Type a script or drag ascript file your workspace” へスクリプト追加。

[Product] > Build(command+B)
Project Navigatorの.aファイル > 右クリック > Show in Finder

Finderの一つ上の階層へ移動。
Debug-universal
iPhone用、シミュレータ用、ユニバーサルと3種の.aファイルがビルドされている。

.aを使用する

プロジェクト/Frameworks
Debug-universalフォルダの.aファイルを追加。

.hファイルを全て追加。

どちらもcopyへチェックを入れています。
copy

【プロジェクトファイル構成】
Include Groupを作成し.hファイルを追加しました。
Project Navigator

#import "InazumaUtils.h"
#import "IZDevice.h"

ヘッダーファイルをimportし使用します。

【おまけ】
Static libraryが作成できました。
クラス内容を公開せずに他のプロジェクトで使用するコードを提供する時に使用できそうです。
ただ.aファイルは1ファイルになるのにヘッダーファイル(.h)は全て提供しなければならないようです。
*間違ってるかもです。
ライブラリの提供方法として若干不細工感が拭えません。

どうせヘッダーファイルを全て提供するのだったらFrameworkの方が1ファイル(フォルダ)で済むのでそちらの方が良さげな気もします。
[iOS][Objective-C][Xcode 4.6.3] そうだFramework(.framework)を作ってみよう

コメントを残す