32

I'm trying to write Cocoapods specification for my library which must modify Xcode project and add "Run Script Build Phase" to project's target. I thought I can use post_install hook. But "pod spec lint" says that this hook is deprecated:

- WARN  | [iOS] The post install hook of the specification DSL has been deprecated, use the `resource_bundles` or the `prepare_command` attributes.

I have no idea how I can replace post_install hook with *resource_bundles* or *prepare_command*. Who knows any other approach to solve my problem? Is it possible?

And another problem is how to modify Xcode project to add build phase, but it is actual only when "post_hook problem" is solved.

mfaani
  • 33,269
  • 19
  • 164
  • 293
opedge
  • 1,532
  • 1
  • 10
  • 22

2 Answers2

41

Use the Xcodeproj ruby gem (which is part of Cocoapods) to write a script that modifies your Xcode project.

You then call this script using the prepare_command

require 'xcodeproj'
path_to_project = "${SOURCE_ROOT}/${PROJECT_NAME}.xcodeproj"
project = Xcodeproj::Project.open(path_to_project)
main_target = project.targets.first
phase = main_target.new_shell_script_build_phase("Name of your Phase")
phase.shell_script = "do sth with ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/your.file"
project.save()

Documentation: http://rubydoc.info/gems/xcodeproj

You can get a list of build setting variables and their values by running…

xcodebuild -project [ProjectName].xcodeproj -target "[TargetName]" -showBuildSettings

UPDATE: A few things have changed since this answer was written. The issue of accessing environment variables is currently being discussed here: https://github.com/CocoaPods/CocoaPods/issues/2115

mfaani
  • 33,269
  • 19
  • 164
  • 293
Onato
  • 9,916
  • 5
  • 46
  • 54
  • 1
    Is there an easy way to hte the path_to_project? I want users of my pod just to run pod install and everything in configured for them. – GuidoMB Aug 26 '14 at 19:47
  • Perhaps you can get it from one of the xcode build variables. http://stackoverflow.com/a/6911421/215748 – Onato Aug 29 '14 at 12:14
  • 1
    @GuidoMB You could do `current_pwd="$PWD"; project_dir=\`cd "../../"; pwd\`; cd "$current_pwd"`, the project dir path is then stored in the variable *project_dir*. You then use this in your ruby code for example by passing it as an argument. Created a gist with an example including path for the .xcodeproj file at https://gist.github.com/niklasberglund/4534113ce7db9572e772 – Niklas Berglund Sep 13 '14 at 17:36
  • 1
    This all works great, but in the wrong directory. I'm only able to get this to execute in Library/Caches/CocoaPods/Pods/... But not in the actual Pods directory in my project. Any idea of what I'm missing? – CodyMace Oct 16 '15 at 17:17
  • 1
    do I have to set `SOURCE_ROOT` and `PROJECT_NAME` somehow? I do get `[Xcodeproj] Unable to open `/Users/.../projects/git/.../${SOURCE_ROOT}/${PROJECT_NAME}.xcodeproj` because it doesn't exist.` – swalkner Jul 11 '16 at 08:18
  • Are you running this from the command line? These variables are set by Xcode when running the script as a build phase. – Onato Jul 11 '16 at 20:24
  • 1
    I'm getting the same issue. This is when I call pod install. – Liron Aug 02 '16 at 13:55
  • The path of the prepare_command is also at the path of the podspec and not at the path of the project. – Liron Aug 02 '16 at 13:57
  • Hi. Also getting the `Unable to open` error when performing `pod install`. Any way to find the correct `path_to_project`? – ArVan Aug 25 '16 at 11:11
  • 1
    Has anyone verified that this accutally adds a run script to the pods project target? I dont get any errors, but I dont see any runscript in my file either – vonGohren Jan 27 '17 at 12:16
  • `prepare_command` does not work with local pods. E.g. `path => `. Perhaps that is your issue? – Onato Jan 30 '17 at 02:12
  • it generated the run script only once for me, didn't manage to get it generated again even after numerous `pod install` or `update` ... idk what's going on – David Jun 06 '18 at 23:53
  • 1
    @Onato How can we specify the position of script in Build phases? I would like to add a script on the top of the stack. – Ali Abbas Feb 07 '19 at 08:06
  • 1
    @AliAbbas What you need to do is actually an array manipulation ```main_target.build_phases.delete(phase) main_target.build_phases.insert(0,phase) ``` – Jidong Chen May 16 '19 at 06:15
0

The complete guide can be found here: https://blog.cocoapods.org/CocoaPods-1.4.0/

But as a short version, you can add you can add Build Phase scripts like this:

s.script_phase = {
  :name => 'Hello World',
  :script => 'echo "Hello World"',
  :execution_position => :before_compile
}

If you want to execute a shell script this syntax can be used: (Your file should be executable chmod +x setup.sh)

s.script_phases = [
  { :name => 'Precompile',
    :script => '${PODS_TARGET_SRCROOT}/setup.sh',
    :execution_position => :before_compile
  }
]

If you want to add multiple scripts you can do it like this:

s.script_phases = [
  { :name => 'Precompile',
    :script => '${PODS_TARGET_SRCROOT}/setup.sh',
    :execution_position => :before_compile
  },
  { :name => 'Postcompile',
    :script => 'echo "yay!"',
    :execution_position => :after_compile
  }
]

Note: Do not forget to preserve the path to your script file: s.preserve_paths = '{path}/setup.sh'

mehdok
  • 1,499
  • 4
  • 29
  • 54