首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > .NET Framework >

在Xcode中手动创办通用Framework及静态库文件

2013-03-28 
在Xcode中手动创建通用Framework及静态库文件经常我们都会将自己写过的一些代码段整理出来,然后封装、打包,

在Xcode中手动创建通用Framework及静态库文件

经常我们都会将自己写过的一些代码段整理出来,然后封装、打包,归档,等到别的项目需要使用的时候,再整个添加到新工程里去。这是个好习惯,而为了简单起见,通常都会将代码编译成一个链接库(.a, .so, .dll等),当然,要是没有团队协作而是在自己的机器上随便写写的话,随便设个环境变量之类的就好。但要是哪天需要在团队合作中使用,或者要开源给别人用之类的,就会碰到一群不知所以的程序猿,好比史前人类看到电脑一样的问你这个是什么那个怎么弄…所以,通常我们都会写一个install的脚本,帮你设好一堆的环境变量,把头文件丢到 /usr/local/include, 把库文件丢到 /usr/local/lib 里去。 以上情况不仅仅在写C/C++的时候才会遇到,而是一个比较通用的情况。最近便在整理曾经写的iOS的一个小库的时候,需要贡献出来给团队使用,而且,还不止一个团队在用这个库,于是想到了是否也可以将这个小库写成一个Framework呢? 原来的打算是将代码段编译成动态链接库(Dynamic Library),但是一Google便放弃了,因为Apple不允许,好吧,那就搞成静态链接库(Static Library)吧,纠结了半天终于用 lipo 命令编译出了一个 static library,于是蛋疼如我的想到,是否能将其放到系统目录下,这样以后所有的Project都能直接引用而不需要每次都复制到工程目录里? 于是果断把 library 丢到了 /usr/lib, /usr/local/lib 里,都不行…,并且尝试了把头文件丢到 /usr/include, /usr/local/include 里,Xcode的智能补全提示找到了文件,但是编译报错说找不到,既然如此,那一定是有什么地方不对(废话…) 根据GCC一类编译器的设定,在编译时会带上一系列的环境变量,诸如头文件搜索路径等,并且,由于iOS存在真机、模拟器两种不同的平台,以及一个Xcode版本可以同时支持多个不同版本的SDK,想到,是否这一系列的环境设置,是根据平台+SDK版本而有区分的? 既然如此,那么头文件的搜索路径和库文件的搜索路径应该是在Xcode对应的Library中,在Stack Overflow里找到这么一篇文章 Canonical list of Xcode Environment Variables(Xcode环境变量的权威列表),发现了几个可能的环境变量 SDKROOT, PLATFORM_DEVELOP_USR_DIR, XCODE_APP_SUPPORT_DIR。其中SDKROOT明显就是当前使用的SDK的根目录,而PLATFORM_DEVELOP_USR_DIR,就是搜索头文件和库的对应的 usr 目录,另一个XCODE_APP_SUPPROT_DIR,便是最接近整个Xcode资源目录的变量。(详细的对应目录稍后说明) 在搞定一堆脚本后,把头文件和库放到了对应的 usr 目录下,这次Xcode终于找到头文件了,但随之新的问题也出现了,在Target的 Link Binary With Libraries 中,无法搜索到对应的静态链接库。仔细查看了 lib 目录后发现,Xcode支持的仅有3种链接对象:Framework,dylib,Mach-O。而后两种由于都是动态链接的形式添加到项目中的,所以都不被Apple允许。于是只剩下Framework这一种方式。探寻Xcode的目录结构之后,发现SDK对应的 /System/Frameworks 目录,便是Xcode存放所有Frameworks的地方,并且没有其他系统或者配置项依赖,只要在该目录下建立对应的 Name.framework 目录,并按照格式放入文件,既可被Xcode搜索到。 前因后果至此终于理清楚了,于是我们手把手来建立一个自己的Fake Framework吧。 Step 1: 创建工程创建一个任意类型的App工程,此处选择Single View,只是因为简单而已。该工程的作用在于能够同时对编写的库进行有效的测试,当然建其他类型的工程也是可以的。 在Xcode中手动创办通用Framework及静态库文件 Step 2: 添加库代码,或者编写之这个具体想写什么就随意了,这里偷懒,就把之前写的 UIView (ParticalCurl) 贴进去 在Xcode中手动创办通用Framework及静态库文件 Step3: 确保库相关的代码能够编译通过我觉得这是一句废话…好吧,但这确实是很重要的一步,在你的项目中使用库相关的代码,并且确保都能编译通过以及能够正常的工作。 Step4: 建立新Target, 创建适合iPhone真机使用的静态库终于到了关键核心步骤了,各位看官久等了!选择 File -> New -> Target, 如下图: 在Xcode中手动创办通用Framework及静态库文件 在新建对话框中,选择 Framework & Library -> Cocoa Touch Static Library,如图 在Xcode中手动创办通用Framework及静态库文件 将Target命名为 [library-name].iphoneos,比如 blogguide.iphoneos,如图 在Xcode中手动创办通用Framework及静态库文件 随后,选中刚刚建立的blogguide.iphoneos这个target,切换到 Build Phases这个Tab,点开Compile Sources结点,删掉里面所有的东西,将库相关的 *.m 文件拖拽进去,然后点开Copy Headers,将库相关的 *.h 头文件拖到public下,如下图: 在Xcode中手动创办通用Framework及静态库文件 随后,选择 Product -> Edit Schema,将编译配置设置为 Release,如图 在Xcode中手动创办通用Framework及静态库文件 至此,一个可以在iphone真机上使用的静态链接库的工程就算搞定了,接下来,只要选择 Device 编译就好,如图: 在Xcode中手动创办通用Framework及静态库文件 选择 Target 和对应的 Platform,按下 CMD+B 编译就好了 Step 5: 重复Step4,建立可以在模拟器下使用的静态链接库第五步的操作和第四步完全相同,但是注意新建的Target的命名还是得改下,可以叫做blogguide.iphonesimulator。如图: 在Xcode中手动创办通用Framework及静态库文件同样,选择 对应的 Target和Platform进行编译。 Step 6: 合并两个平台的静态链接库,使用lipo命令好吧,这并不是最激动人心的时刻,因为这一步其实是可以省略的,不过如果不怕麻烦要多搞几个不同的二进制文件,其实就随意了…我只是给点建议,合并在一起比较容易管理,并且最终产物是一个通用的静态链接库,最终用户并不用操心这个库是使用在哪个平台上的,用就可以了,配置起来也很方便。操作如下:建立新的Aggregate类型的target,如图: 在Xcode中手动创办通用Framework及静态库文件 名字这种东西就是个代号,所以随意了…在右下角选择 Add Build Phase -> Add Run Script 在Xcode中手动创办通用Framework及静态库文件 贴进去如下代码: 
DESKTOP_DIR="/Users/$(whoami)/Desktop/libblogguide"mkdir -p $DESKTOP_DIRcp -r "${BUILT_PRODUCTS_DIR}/usr/local/include" "${DESKTOP_DIR}/include/"#create the liblipo -create     "${BUILT_PRODUCTS_DIR}/../Release-iphonesimulator/libblogguide.iphonesimulator.a" \    "${BUILT_PRODUCTS_DIR}/libblogguide.iphoneos.a" \    -output "${DESKTOP_DIR}/libblogguide.a"
 选择blogguide->iOS Device编译,如果之前的步骤都正常,那么如上的脚本在执行之后,将会在你的桌面上创建一个libblogguide的目录,并且其中会有一个libblogguide.a的静态链接库,以及一个include文件夹,包含了这个库的头文件。 Xcode在编译新工程时,会在 ~/Library/Developer/Xcode/DerivedData/下,创建 [工程名]-xxxxxxx 的目录,用来存放编译出的临时文件和对应的打包文件,当然,这个目录我们可以通过Xcode的环境变量获得,就是 $BUILT_PRODUCTS_DIR,指向的是该target的目录。在一个target目录的上层,存在 [BuildConfig]-[Platform] 格式的若干目录,分别存放了对应平台编译的结果。lipo命令的作用,就是创建通用文件,具体说明可以直接在命令行中 man lipo 查看。 Step 7: 创建Framework激动人心的时刻终于来了!一个Framework的基本目录结构为: 
Name.framework -|               -|- Headers               -|- Name(bin)
 只要按照这个目录结构存放我们的库,那么,在Xcode中就能以Framework的形式载入。于是按照Step 6的方式,再创建一个Aggregate Target,命名为install-lib,添加一个Run Script。之后,且慢。由于我们需要把我们的framework放到 Xcode 本身的目录中去,而这个目录是/Application目录中的一个子目录,所以需要管理员权限才能写入,虽然sudo命令支持通过CR模式传入密码,可以用另一个脚本echo密码,然后设置SUDO_ASKPASS环境变量为密码脚本,但始终不太美观,尤其是别的使用者要安装在自己的电脑上,还要改密码脚本。 于是,我们可以用Apple Script来执行,Apple Script中有do shell script *** with administrator privileges的指令,可以弹出输入密码的对话框。在工程中添加新的Shell Script文件,叫做 install.sh,在install-lib的Run Script中,贴入如下代码: 
osascript -e "set shellScript to \"/bin/sh install.sh $whoami $XCODE_APP_SUPPORT_DIR\"" \          -e "do shell script shellScript with administrator privileges"
 该段脚本使用osascript命令执行Apple Script,并通过Apple Script,使用管理员权限执行install.sh脚本。 注意我们在Apple Script所执行的脚本中给install.sh传入了一个 $XCODE_APP_SUPPORT_DIR 的参数,以此来获得Xcode的安装路径。 随后,我们就来编写install.sh,来达到安装Framework的目的。在install.sh中写下如下代码: 
#! /bin/sh# Install BlogGuide after buildUSERNAME=$1SUPPORT_LIBRARY=$2DESKTOP_DIR="/Users/${USERNAME}/Desktop/libblogguide"# change the head file include typeINCLUDEPATH="${DESKTOP_DIR}/include"includeFiles=$(ls $INCLUDEPATH)for headfile in $includeFiles; do    filePath="${INCLUDEPATH}/${headfile}"    #echo "process file: $filePath" >> /tmp/aggtarget.log    includes=$(grep "#import \"" ${filePath} | awk -F"#import \"" '{print $2}' | awk -F "\"" '{print $1}')    #echo "Find import head: $includes" >> /tmp/aggtarget.log    for includePiece in $includes; do        filename=$(echo $includePiece | awk -F"." '{print $1}')        extension=$(echo $includePiece | awk -F"." '{print $2}')        #echo "file: $filename, extension: $extension" >> /tmp/aggtarget.log        sed -i "" "s/#import \"${filename}.${extension}\"/#import \<PYUtility/${filename}.${extension}\>/g" $filePath    donedone# install to each SDKPLATFORM_ROOT_DIR=${SUPPORT_LIBRARY}/../../PlatformsPLATFORM_LIST=$(ls $PLATFORM_ROOT_DIR)#echo $PLATFORM_LIST >> /tmp/aggtarget.logfor platform in $PLATFORM_LIST; do    SDK_ROOT_DIR=${PLATFORM_ROOT_DIR}/$platform/Developer/SDKs    SDK_LIST=$(ls $SDK_ROOT_DIR)    for sdk in $SDK_LIST; do        FRAMEWORKS_ROOT_DIR=${SDK_ROOT_DIR}/$sdk/System/Library/Frameworks        BLOGGUIDE_FRAMEWORK_DIR=${FRAMEWORKS_ROOT_DIR}/BlogGuide.framework        rm -rf ${BLOGGUIDE_FRAMEWORK_DIR}        mkdir ${BLOGGUIDE_FRAMEWORK_DIR}        mkdir ${BLOGGUIDE_FRAMEWORK_DIR}/Headers        cp -r "${INCLUDEPATH}""${BLOGGUIDE_FRAMEWORK_DIR}/Headers"        cp "${DESKTOP_DIR}/libblogguide.a""${BLOGGUIDE_FRAMEWORK_DIR}/BlogGuide"    donedone
 然后就编译吧~编译时,会弹出输入密码的对话框: 在Xcode中手动创办通用Framework及静态库文件 编译成功后,重启Xcode,就可以在 Link Binary With Libraries里搜到之前创建的BlogGuide.framework了 在Xcode中手动创办通用Framework及静态库文件 至此,大功搞成!按此方法创建的framework,由于是使用的静态链接库,因而不会别苹果拒绝,同时,这样的framework给自定义库的使用、分享、传播带来了很多便利之处,虽然麻烦了一点,其实我只是想说我比较蛋疼而已。

热点排行