做windows开发几年了,一直用vc。感觉这个集编辑、调试、管理于一体的IDE,确实是组织维护中小规模项目的利器。在使用的过程中,也积累了不少vc经验技巧,甚至是习惯了那整套工作流程。当突然换到其它平台上开发时,面对新的系统和工具,总有点格格不入。比如说Mac上的xcode,也是一款全功能的IDE,但其在设计结构上与vc风格颇异,导致vcer在初步接触的时候,不是觉得缺胳膊少腿,就是脱裤子放屁,明明看着就是一漂亮的花瓶,可就是摘不到花瓶里的花朵。但是功夫不负有心人,或是说大道外殊内同,终于让我摸索出一条将xcode配置成vc style的方法,现在在两个平台上开发都有体贴可人的IDE用,程序员的幸福莫过于此呀。
好了,开始正题,看vcer如何驯服xcode这匹小红马!(注:本文是写给爱好vc的windows programmer看的,如果你本来就是mac或xcode的粉丝且不耻于MS的人品技术,那么请留步或是以批判的眼光来看待此文并欢迎指教)
首先介绍一下,我用vc是怎样组织工程的。整个项目是一游戏客户端引擎系统,包括基础类库、虚拟文件模块、资源模块、渲染器、脚本模块、本地窗口模块、应用程序生命周期模块、虚拟逻辑世界模块、各扩展对象模块等。以上模块分别以vc project形式存在并被编译成.lib静态库,另外有各种外壳(shell)程序如资源转换打包工具、shader转换工具、单元测试程序、游戏主程序等,它们也是vc project但被编译成.exe应用程序。上述所有vc project被组织在一个vc solution里。下面是一个简单的示意图:
另外分享几个关于vc的小技巧:
1、使用vsproPS文件来存放公用配置信息。vsprops文件是vc支持的一种配置文件,可认为是配置文件的头文件。每个vc project都可以继承一个vsprops配置文件,并且vsprops文件之间还可以继承。我一般会配置两个vsprops文件,一个叫depend.vsprops用来存公用配置,如第三方库的头文件和库文件依赖路径、自定义的预编译宏、各project输出的obj/lib/exe文件的位置;另一个叫spec.vsprops用来存每个开发者个人的配置,如整个第三方库根目录的路径、某些特殊调试宏等。如下图所示:
整个依赖关系为vc project -> depend.vsprops -> spec.vsprops。那么,有了两个vsprops后,该怎么分别配置其中的内容呢?
可以用两个例子说明:
2、配置第三方库依赖路径。
在我的项目里,第三方库是个容量巨大的目录,因为为了使用的方便,里面放的都是编译好的头文件和库文件。不同的开发人员从Git仓库中将其检出后,可能会根据自己硬盘情况放到不同的位置。这时,可以在spec.vsprops中定义一个变量$(depend_dir)指向实际存放第三方库的路径,然后在depend.vsprops中利用继承得到的$(depend_dir)来填写对各种第三方库的内部引用,如图:
depend.vsprops中使用的是统一变量$(depend_dir),内容与个人无关,这个文件是要提交到仓库里的。
spec.vsprops中的内容则是个人相关的,不用提交,因此每个人可随意修改,也不会影响到他人和最终发布。
3、配置个人调试用的预编译宏。有时候,某些功能会在发布版中关闭,但是为了调试方便,开发者会在自己机上编译开发版时打开。这种功能的开关一般都是用宏来做条件编译。因此,可以把开发者调试相关的宏定义在spec.vsprop中,而不是临时去修改代码,一旦忘记改回来又被提交的话,这些功能就不小心被放出去了。而使用vsprop的做法,只要保证发布机器上的spec.vsprop文件不被随便修改,就不会误发布任何功能。
4、要学会数数……
好了,说完vc,该介绍本文的主角xcode啦!先看一张图,是上述solution在xcode里的组织结果:
先看一下vc与xcode之间有直观对应的概念:
vc solution -> xcode workspace
vc project -> xcode project
vc configuration -> xcode scheme
vc vsprops -> xcode xcconfig
“一个大项目包含几个小工程、大项目和小工程充满各种神秘的配置选项”这个基本思路是一样的,但差别也不少,讲解如下:
1、首先xcode里把整个系统叫做workspace而非solution。在菜单栏File->New->Workspace即可新建一个这玩意,之后再用File->New->Project创建新工程时,记得在最后一步“Add to”选择刚才新建的workspace。由于xcode里居然允许project之间互相嵌套,我实在不能理解这种做法有什么好处,为了与vc习惯保持一致,我们应在接下来的Group中选择顶层项即让此project“直辖”于workspace。
2、重复步骤1,将各个project轮流创建好后,会发现它们杂乱排成一列,这时出于视觉习惯要调整各project的上下顺序,千万要小心!如果你不是粗心或是超级快手,你应该会在放下的一瞬间发现弹出一个对话框,询问里你是否要把project A添加到project B(A是你想要移动的,B是你想要移到它之后的),赶快点Cancel(希望你还没有点OK)。其实这也是上面说过的project间可以互相嵌套功能的体现,只是在UI设计上,有个很不容易辨别的细节:你再拖动一下试试,这次注意观察当你把A拖到B下方时,B下方边框上会显示一条粗线左端有一个圆圈,这时你保持水平左右移动一下,看到了什么没有?那个圆圈会左右跳动!当它特别靠左时,才表示此次操作是要把A移到B下方,而它相对靠右时,表示的是把A加入B,也就是刚刚、一般人第一次都会做错的操作!蛋疼了吧。。。
3、好了调整完顺序,你可能会想像在vc里那样,再建几个filter(文件夹),把这些project分门别类放好。于是你右键点来点去,菜单项里找来找去,终于……没找到!是的,xcode没有这个功能!现在你知道为什么第2步那么重要了……
4、xcode project与vc project有一个最大的不同:一个vc project只能有一个输出(lib、dll或是exe),但一个xcode project可以有多个输出,每个输出叫一个target,每个target都有一份独立的完整的(project级的)配置。这些target可以是纯逻辑上的划分,也可以是用来对应不同平台。我觉得这是个不错的设计,比如一个project里面一些文件可以用来生成程序A,另一些文件用来生成程序B,这两部份文件也有交集。如果是在vc里,那就只能配置成2个project了,或者为了完美解藕将公共部份再抽出来成1个project,2+1 project,简称3P……但在这个示例项目里,project已经划分得很细了,不存在上面说的情况。所以我也没用到过在一个project中定义两个不同逻辑target的功能。但是在上图里,显示testgui这个工程确实有2个target,那是因为跨平台的需要,我为每个project都定义了一个mac64 target和一个ios target。由于我并没有支持ios emulator,否则的话还可以再添加第3个用于模拟器的target。
5、xcode project有哪些设置?首先,是属于此工程的文件。文件不需要与工程在同目录下,事实上我一般都会将代码文件与IDE用的工程文件分开放,在solution里建一个build目录,里面再为各IDE建相应子目录。由于每个project会有多个target,因此针对每个文件还能单独设置它属于哪(几)个target。利用这一点,可以将分属于各个平台的源文件都添进来,方便查看编辑,同时为其设置适当的target属性,也不会影响编译。其实这个功能在vc中也有,利用vc和xcode这类IDE来查看源文件,可以方便的跳转到各种声明、定义中,比在ultraedit等普通编辑器里看要舒服多了。
下面是在vc里的设置。非win32平台相关的源文件全部打上了红色减号,表示不被编译。
下面是在xcode里设置文件属性的面板,由于这个文件是属于安卓平台的,因此两个target前都没有勾选。
在左侧工程树视图中,除了看到所包含的源文件之外,还能看到各种依赖库(Frameworks)、输出文件(Products),初一看可能觉得有点乱,但其实也还不错,与project有关的输入输出尽在眼底。但是Frameworks和Products下面的条目却不像源文件一样是直接通过右键菜单添加的,下面讲工程设置时会解释。
xcode project的设置,一共有四个优先级,从低到高分别是系统默认值、xc配置文件、project级配置、target级配置。比起vc来,多了project/target这两级的差别,至于xc配置文件,也就相当于vsprops文件的性质。
点中project或target后,就会显示其配置面板。大部份编译相关选项都在Build Settings里,并且呈平板式显示且右上角还有搜索功能,这比起VC的级联配置菜单要轻爽得多。具体的选项就不多解释了。下面说一下xcconfig文件的使用。使用的理由与vc同,都是希望将公共配置抽取出来,避免一经修改就要去大量的project里一个个手工调整。手动新建一个文本文件后缀改为.xcconfig放到workspace同级目录。然后在各个project中通过右键菜单里的Add Files to选项把它加进来,之后转到project级配置的info面板,第二栏Configurations里,有debug/Release两项,再展开后又会分别列出所有target,也就是说针对每一个target的debug和release版,都可以单独指定一个xcconfig文件。但没有必要那么繁琐,直接在debug/release这一级指定就可统一应用在所有target上。这里值得注意的是xcode里的Configuration与vc不一样,xcode里指的就是xcconfig配置文件,而同样的术语在vc中指的是debug/release/debug dll/release dll这类的整体编译环境,vc中Configuration的范围比xcode更大。
指定好xcconfig之后,来看一下这个文件里都能填些什么。先看下我填的内容:
EFFECTIVE_PLATFORM_NAME=
FXROOT=$(SRCROOT)/../..
DEPROOT=$(FXROOT)/../fxd
HEADER_SEARCH_PATHS[arch=armv6] = $(FXROOT) $(DEPROOT)/common $(DEPROOT)/iOS/include
HEADER_SEARCH_PATHS[arch=x86_64] = $(FXROOT) $(DEPROOT)/common $(DEPROOT)/darwin/include /opt/local/include
HEADER_SEARCH_PATHS[arch=i386] = $(FXROOT) $(DEPROOT)/common $(DEPROOT)/linux/include
SYMROOT=~/fxo/xcode/bin/$(SDKROOT)/$(ARCHS)
OBJROOT=~/fxo/xcode/obj/$(SDKROOT)/$(ARCHS)
CONFIGURATION_BUILD_DIR=$(SYMROOT)
LIBRARY_SEARCH_PATHS[arch=armv6] = $(inherited) $(SYMROOT) $(DEPROOT)/iOS/lib
LIBRARY_SEARCH_PATHS[arch=x86_64] = $(inherited) $(SYMROOT) $(DEPROOT)/darwin/lib /opt/local/lib
LIBRARY_SEARCH_PATHS[arch=i386] = $(inherited) $(SYMROOT) $(DEPROOT)/darwin/linux/lib /opt/local/lib
OTHER_CFLAGS[sdk=iphone*] = -DTARGET_IPHONE=1
OTHER_CPLUSPLUSFLAGS[sdk=iphone*] = $(inherited) -x objective-c++
这里面包括:1)头文件和库文件搜索路径,并且通过[arch=xxx]来区分不同架构;2)临时和最终文件的输出位置,这是通过SYMROOT和OBJROOT指定的。默认xcode会把输出文件写到它自己的安装目录下去,找起来不方便,所以我都会统一修改它们,但除了在这里定义输出文件外,还需要在另一处修改:主菜单File->Workspace Settings->Build->Advanced,选中Legacy,也就是随各target自定,即我们在xcconfig中指定的值;3)一些编译选项,如宏、编译器参数等。
6、把所有project配好后,那么就要设定其构建顺序、产出、调试等等solution级的参数了。这一级在xcode里叫做scheme,一个scheme定义了一套project(target)的集合,相当于vc里的Configuration。在Manage Scheme面板上可以创建新的scheme,在创建scheme时需要指定一个target,也就是此scheme的最终输出,这个target所属的架构也就决定了此scheme的架构。不同架构的scheme有不同的运行调试环境,比如x64架构的shceme,就只能在64位mac上运行,而arm架构的则可以在iphone和ipad两种设备以及相应的模拟器上运行(模拟器其实是i386架构的,这里的从属关系和设计概念我还没彻底搞清楚)。在添加完scheme时,记得把它的Container设成整个workspace而不是某个project,并且把后面的shared勾上,这样这个scheme的信息会存在workspace下的xcsharedata子目录里,把它提交到仓库中,就可供所有人一起使用了。如果没勾上shared,那么会存在xcuserdata里,这个目录一般是不提交的,类似vc里每个project还会有一个$(machine).user后缀文件,里面存的也是不应提交的诸如调试时工作目录、环境变量等信息的个人配置。
下面这个是Manage Scheme的界面,红色标出了要特别去设的地方。
一旦把project和scheme都配好后,就可以舒舒服服享受IDE开发调试的方便和爽快了。看这张~
用过vc的人想必会有体会,当突然转到命令行下工作,要用gdb这种看局部变量看源代码都得敲命令的古老程序来调试时,那种世界末日的感觉吧。诚然人不能完全依靠工具,但也没必要极端的回避工具。使用工具的目的也就是为了增加生产效率,当然是在深入理解工具背后原理的前提下。事实上我这个项目在所有平台都是可以用一套统一的makefile构建的(在windows下通过cygwin),包括ios和android。并且在初次接触一个平台,在其上工作时,使用命令行式的简单工具是非常必要的,因为这样更有助于理解工作流程的本质。但是一旦这套机制清楚了,就没必要困守成规,如果能寻找到一条更方便简捷的途径,又何乐不为呢。所以我才想在mac上对xcode一番探索,期间也在不少群和论坛上问过人,但得到的相当一部份答复不是说IDE都是浮云,就是说别把windows的那一套带到mac来,我只能笑而不语,个中艰辛虽不足道,但上面这段感触就当是对之的回答和本文的总结吧~