Xcode:在每个修改源代码的构build之前运行脚本

我做了什么:

我有一个脚本

  1. 阅读一些configuration文件来生成源代码片段
  2. find相关的Objective-C源文件和
  3. 在步骤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产品的最新版本。 这是我想要发生的事情:

  1. 第一次运行:Xcode收集依赖信息—>没有改变
  2. 第一次运行:Xcode运行“运行脚本生成阶段”—>源代码在Xcode后面改变
  3. 第一次运行:Xcode完成构build,没有什么需要更新
  4. 第二次运行:Xcode收集依赖信息—>源码已经更改,需要重新编译!
  5. 第二次运行:Xcode运行脚本生成阶段“—>一切都是最新的
  6. 第二次运行:Xcode继续编译

在阅读构build阶段的Xcode文档之后,我尝试添加一个源文件,这个文件在每次脚本运行时都被更新为“运行脚本构build阶段”的输出,但没有任何改变。 由于configuration文件的数量在我的项目中可能有所不同,我不想指定每个input和输出文件。

题:

如何让Xcode知道在“运行脚本构build阶段”期间所做的源文件更改?

编辑:

  • 补充说,我把脚本构build阶段放在其他构build阶段之前

到目前为止所提到的每一种技术都是过度的。 复制史蒂夫金的能见度评论:

在构build阶段选项卡中,只需将“运行脚本”步骤拖到更高的位置(例如在“编译源”之前)。

在XCode 6上testing

这个解决scheme可能已经过时了。 请参阅较高的投票答案。


使用“外部目标”:

  1. 从菜单中select“项目”>“新目标…”
  2. select“Mac OS X”>“其他”>“外部目标”并将其添加到您的项目中
  3. 打开它的设置并填写你的脚本设置
  4. 打开主目标设置的“常规”选项卡,并添加新的目标,因为它是直接依赖的

现在,新的“外部目标” 主目标甚至开始收集依赖信息之前运行,以便在脚本执行过程中所做的任何更改都应包含在构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中使用数据驱动的用户值:

  1. 添加一个指向Python脚本的外部构build系统项目并传递一些参数
  2. 将用户定义的构build设置添加到构build。 这些是你传递给python的参数(更多关于为什么我们会这样做)
  3. python脚本读取一些inputJSON文件,并构build一个plist预处理器头文件并触及主应用程序plist
  4. 主项目打开“预处理plist文件”并指向这个预处理器文件

使用主应用程序plist文件上的触摸导致主要目标每次生成plist。 我们将构build设置作为parameter passing的原因是,我们的命令行构build可以覆盖设置:

  1. 将预定义项目添加一个用户定义的variables“foo”。
  2. 在prebuild中,你可以使用$(foo)将值传递给python脚本。
  3. 在命令行中,您可以添加foo = test以传入新值。

python脚本使用基本设置文件,并允许用户定义的设置文件覆盖默认值。 你作出改变,立即结束在plist。 我们只使用这个设置必须在plist中。 对于其他任何事情,这是一个浪费的努力….生成一个JSON文件或类似的东西,并在运行时加载它:)

我希望这可以帮助…这是一个困难的几天搞清楚这一点。