Xcode:在每个修改源代码的构build之前运行脚本
我做了什么:
我有一个脚本
- 阅读一些configuration文件来生成源代码片段
- find相关的Objective-C源文件和
- 在步骤1中将源代码的某些部分replace为生成的代码。
还有一个Makefile,它有一个特殊的时间戳文件作为make目标,configuration文件作为目标源:
SRC = $(shell find ../config -iname "*.txt") STAMP = $(PROJECT_TEMP_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME).stamp $(STAMP): $(SRC) python inject.py touch $(STAMP)
我将这个Makefile作为一个“运行脚本构build阶段”添加到项目目标的构build阶段的堆栈之上。
发生了什么:
脚本构build阶段在编译源代码之前运行。
但是,由于脚本在执行过程中修改了源代码,因此我需要构build两次以获取构build产品的最新版本。 这是我想要发生的事情:
- 第一次运行:Xcode收集依赖信息—>没有改变
- 第一次运行:Xcode运行“运行脚本生成阶段”—>源代码在Xcode后面改变
- 第一次运行:Xcode完成构build,没有什么需要更新
- 第二次运行:Xcode收集依赖信息—>源码已经更改,需要重新编译!
- 第二次运行:Xcode运行脚本生成阶段“—>一切都是最新的
- 第二次运行:Xcode继续编译
在阅读构build阶段的Xcode文档之后,我尝试添加一个源文件,这个文件在每次脚本运行时都被更新为“运行脚本构build阶段”的输出,但没有任何改变。 由于configuration文件的数量在我的项目中可能有所不同,我不想指定每个input和输出文件。
题:
如何让Xcode知道在“运行脚本构build阶段”期间所做的源文件更改?
编辑:
- 补充说,我把脚本构build阶段放在其他构build阶段之前
到目前为止所提到的每一种技术都是过度的。 复制史蒂夫金的能见度评论:
在构build阶段选项卡中,只需将“运行脚本”步骤拖到更高的位置(例如在“编译源”之前)。
在XCode 6上testing
这个解决scheme可能已经过时了。 请参阅较高的投票答案。
使用“外部目标”:
- 从菜单中select“项目”>“新目标…”
- select“Mac OS X”>“其他”>“外部目标”并将其添加到您的项目中
- 打开它的设置并填写你的脚本设置
- 打开主目标设置的“常规”选项卡,并添加新的目标,因为它是直接依赖的
现在,新的“外部目标” 在主目标甚至开始收集依赖信息之前运行,以便在脚本执行过程中所做的任何更改都应包含在构build中。
还有一个稍微简单的选项,不需要单独的目标,但是如果你的脚本每次都修改相同的源文件,那么这个选项是唯一可行的。
首先,对于任何人为什么Xcode有时需要您构build两次(或者做一个干净的构build)来查看目标应用程序中反映的某些更改感到困惑,这里有一个简短的解释。 Xcode编译源文件,如果它生成的目标文件丢失,或者如果目标文件的最后修改date早于源文件的最后修改date,那么在第一个生成阶段开始时 。 如果您的项目在预编译构build阶段运行一个修改源文件的脚本,Xcode将不会注意到源文件的上次修改date已经更改,因此不会再费心去重新编译它。 只有当您第二次构build项目时,Xcode才会注意到date更改并重新编译文件。
如果您的脚本每次都修改相同的源文件,这是一个简单的解决scheme。 只需在构build过程结束时添加一个“运行脚本”构build阶段,如下所示:
touch Classes/FirstModifiedFile.m Classes/SecondModifiedFile.m exit $?
在构build过程结束时对这些源文件进行touch
操作,可以确保它们总是比对象文件具有更晚的修改date,所以Xcode会每次重新编译它们。
从Xcode 4开始,看起来好像如果将生成的文件添加到构build阶段的输出节中,它将尊重该设置,而不会生成... has been modified since the precompiled header was built
错误消息... has been modified since the precompiled header was built
。
如果您的脚本每次只生成一些文件,这是一个不错的select。
我也很久以来一直在挣扎着。 答案是使用ento的“外部目标”解决scheme。 他是这个问题发生的原因,以及我们如何在实践中使用它…
编译完plist之后,Xcode4构build步骤不会执行。 这当然很愚蠢,因为这意味着修改plist的任何预构build步骤都不会生效。 但是如果你仔细想想,他们实际上在下一个版本中会生效。 这就是为什么有些人谈到了“caching”plist的价值,或者“我必须做两件事才能成功”。 plist是build立的,然后你的脚本运行。 下一次你build立,plistbuild立使用你的修改文件,因此第二个版本。
ento的解决scheme是我发现实际做一个真正的预构build步骤的一种方法。 不幸的是,我也发现它不会导致plist更新没有一个干净的构build,我解决了这个问题。 下面是我们如何在plist中使用数据驱动的用户值:
- 添加一个指向Python脚本的外部构build系统项目并传递一些参数
- 将用户定义的构build设置添加到构build。 这些是你传递给python的参数(更多关于为什么我们会这样做)
- python脚本读取一些inputJSON文件,并构build一个plist预处理器头文件并触及主应用程序plist
- 主项目打开“预处理plist文件”并指向这个预处理器文件
使用主应用程序plist文件上的触摸导致主要目标每次生成plist。 我们将构build设置作为parameter passing的原因是,我们的命令行构build可以覆盖设置:
- 将预定义项目添加一个用户定义的variables“foo”。
- 在prebuild中,你可以使用$(foo)将值传递给python脚本。
- 在命令行中,您可以添加foo = test以传入新值。
python脚本使用基本设置文件,并允许用户定义的设置文件覆盖默认值。 你作出改变,立即结束在plist。 我们只使用这个设置必须在plist中。 对于其他任何事情,这是一个浪费的努力….生成一个JSON文件或类似的东西,并在运行时加载它:)
我希望这可以帮助…这是一个困难的几天搞清楚这一点。