Xcode:在每次直接修改源代码的构建之前运行脚本

时间:2009-06-10 15:45:22

标签: xcode scripting build dependencies

我做了什么:

我有一个

的脚本
  1. 阅读一些配置文件以生成源代码片段
  2. 查找相关的Objective-C源文件和
  3. 使用步骤1中生成的代码替换源代码的某些部分。
  4. 和一个Makefile,它有一个特殊的时间戳文件作为make目标,配置文件作为目标源:

    SRC = $(shell find ../config -iname "*.txt")
    STAMP = $(PROJECT_TEMP_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME).stamp
    $(STAMP): $(SRC)
        python inject.py
        touch $(STAMP)
    

    我将此Makefile添加为项目目标的构建阶段堆栈之上的“运行脚本构建阶段”。

    发生了什么:

    在编译源代码之前运行了脚本构建阶段。

    但是,由于脚本在执行期间修改了源代码,因此我需要构建两次以获取最新版本的构建产品。这就是我想象中发生的事情:

    1. 第一次运行:Xcode收集依赖性信息--->没有变化
    2. 第一次运行:Xcode运行“运行脚本构建阶段”--->源代码在Xcode的后面改变了
    3. 第一次运行:Xcode完成构建,不需要更新任何内容
    4. 第二次运行:Xcode收集依赖性信息--->来源已经改变,需要重建!
    5. 第二次运行:Xcode运行Run Script Build Phase“--->一切都是最新的
    6. 第二次运行:Xcode继续编译
    7. 在阅读Xcode documentation on Build Phases之后,我尝试添加一个源文件,该文件在每次运行脚本时都会更新,作为“运行脚本构建阶段”的输出,但没有任何更改。由于配置文件的数量可能在我的项目中有所不同,我不想指定每个输入和输出文件。

      问题:

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

      编辑:

      • 补充说我在其他构建阶段之前放置了脚本构建阶段

7 个答案:

答案 0 :(得分:80)

到目前为止提到的每种技术都是过度杀伤力。再现steve kim对可见性的评论:

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

在XCode 6上测试

答案 1 :(得分:28)

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


使用“外部目标”:

  1. 选择“项目”>菜单中的“新目标......”
  2. 选择“Mac OS X”> “其他”> “外部目标”并将其添加到您的项目中
  3. 打开其设置并填写脚本设置
  4. 打开主要目标设置的“常规”标签,并添加新目标,因为它是直接相关性
  5. 现在新的“外部目标”在之前运行主目标甚至开始收集依赖性信息,因此在脚本执行期间所做的任何更改都应该包含在构建中。

答案 2 :(得分:3)

还有一个稍微简单的选项,不需要单独的目标,但只有你的脚本每次都要修改相同的源文件时它才有可能。

首先,这里有一个简短的解释,对于那些为什么Xcode有时需要你构建两次(或做一个干净的构建)以查看目标应用程序中反映的某些更改的人感到困惑。如果源文件丢失,或者如果目标文件的上次修改日期早于源文件的上次修改日期,则在第一个构建阶段开始时,Xcode会编译源文件。如果您的项目运行的脚本在预编译构建阶段修改源文件,Xcode将不会注意到源文件的上次修改日期已更改,因此不会重新编译它。只有在第二次构建项目时,Xcode才会注意到日期更改并重新编译文件。

如果您的脚本每次都修改相同的源文件,这是一个简单的解决方案。只需在构建过程结束时添加运行脚本构建阶段 ,如下所示:

touch Classes/FirstModifiedFile.m Classes/SecondModifiedFile.m
exit $?

在构建过程结束时对这些源文件运行touch可确保它们始终具有比其目标文件更晚的最后修改日期,因此Xcode将每次重新编译它们。

答案 3 :(得分:2)

从Xcode 4开始,看起来如果将生成的文件添加到构建阶段的输出部分,它将遵循该设置,而不会生成... has been modified since the precompiled header was built错误消息。

如果您的脚本每次只生成少量文件,这是一个不错的选择。

答案 4 :(得分:1)

我很长时间都在努力解决这个问题。答案是使用ento的“外部目标”解决方案。他是为什么会出现这个问题,以及我们如何在实践中使用它......

在编译plist之后,Xcode4构建步骤才会执行。当然,这很愚蠢,因为这意味着任何修改plist的预构建步骤都不会生效。但是如果你考虑一下,它们实际上会在NEXT构建中生效。这就是为什么有些人谈到了plist值的“缓存”,或者“我必须做2个版本才能使它工作”。发生了什么是plist,然后你的脚本运行。下次构建时,plist使用修改后的文件构建,因此是第二次构建。

ento的解决方案是我发现实际做一个真正的预构建步骤的一种方法。不幸的是我还发现,如果没有干净的构建,它不会导致plist更新,我修复了它。以下是我们如何在plist中使用数据驱动的用户值:

  1. 添加指向python脚本并传递一些参数的外部构建系统项目
  2. 将用户定义的构建设置添加到构建中。这些是你传递给python的参数(更多关于为什么我们以后这样做)
  3. python脚本读取一些输入JSON文件并构建一个plist预处理器头文件并触及主应用程序plist
  4. 主项目已打开“预处理plist文件”并指向此预处理器文件
  5. 在主app plist文件上使用touch会导致主目标每次都生成plist。我们将构建设置作为参数传递的原因是我们的命令行构建可以覆盖设置:

    1. 将用户定义的变量“foo”添加到预建项目中。
    2. 在预构建中,您可以使用$(foo)将值传递给python脚本。
    3. 在命令行中,您可以添加foo = test以传入新值。
    4. python脚本使用基本设置文件,并允许用户定义的设置文件覆盖默认值。你做了一个改变,并立即在plist中结束。我们只将它用于必须在plist中的设置。对于任何其他事情,这是浪费精力....生成一个json文件或类似的东西,并在运行时加载它:)

      我希望这会有所帮助......这是一个艰难的日子,想出这个。

答案 5 :(得分:0)

@ento的外部目标解决方案自Xcode 11.5起不再起作用。解决方案是在运行脚本的Output Files下添加所有将要更改的文件。

答案 6 :(得分:0)

另一种选择是使用您的脚本创建一个子项目框架,并将其添加为所有目标的依赖项。这个子项目的阶段脚本现在应该在所有目标之前执行。

enter image description here