Xcode: запуск script перед каждой сборкой, которая напрямую изменяет исходный код

Что я сделал:

У меня есть script, который

  • Прочитайте некоторые файлы конфигурации, чтобы генерировать фрагменты кода
  • Найдите соответствующие исходные файлы Objective-C и
  • Замените некоторые части исходного кода на сгенерированный код на шаге 1.

и Makefile, который имеет специальный файл timestamp в качестве целевого объекта и файлы конфигурации в качестве целевых источников:

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

Я добавил этот Makefile как "Запустите script этап сборки" поверх стека фаз сборки для целевой цели проекта.

Что произошло:

Фаза сборки script была запущена перед компиляцией источника.

Однако, поскольку script изменяет исходный код во время его выполнения, мне нужно было построить дважды, чтобы получить самую последнюю версию продукта сборки. Вот что я себе представляю:

  • 1-й прогон: Xcode собирает информацию о зависимостях --- > без изменений
  • 1-й запуск: Xcode запускает "Запуск script Фаза сборки" --- > источник изменяется за Xcode назад
  • 1-й запуск: Xcode завершает сборку, не думая, что ничего не нужно обновлять.
  • 2-й запуск: Xcode собирает информацию о зависимостях --- > источник изменился, требуется перестроить!
  • 2-й запуск: запуск Xcode Запуск script Фаза сборки "--- > все обновлено
  • 2-й запуск: Xcode переходит к компиляции

После чтения документации Xcode на этапах сборки я попытался добавить исходный файл, который, как известно, обновляется каждый раз при запуске script как вывод "Run script Build Phases", но ничего не изменилось. Поскольку количество файлов конфигурации может меняться в моем проекте, я не хочу указывать каждый файл ввода и вывода.

Вопрос:

Как сделать Xcode осведомленным об изменениях исходного файла, сделанных во время "Запускать script Фаза сборки"?

Edit:

  • Добавил, что я разместил фазу сборки script до других фаз сборки

Ответ 1

Каждая техника, упомянутая до сих пор, является излишним. Воспроизведение steve kim комментария для видимости:

На вкладке фазы сборки просто перетащите шаг "Запуск Script" в более высокое место (например, перед "Скомпилировать источники" ).

Протестировано на XCode 6

Ответ 2

Это решение, вероятно, устарело. См. Более высокий голосовой ответ.


Используйте "Внешняя цель":

  • Выберите "Проект" > "Новая цель..." в меню
  • Выберите "Mac OS X" > "Другое" > "Внешняя цель" и добавьте его в свой проект.
  • Откройте свои настройки и запустите настройку script
  • Откройте вкладку "Общие" основных целевых настроек и добавьте новую цель в качестве прямой зависимости.

Теперь новая "внешняя цель" запускается до того, как основная цель даже начинает собирать информацию о зависимостях, поэтому любые изменения, сделанные во время выполнения script, должны быть включены в сборку.

Ответ 3

Существует еще одна, немного более простая опция, которая не требует отдельной цели, но она жизнеспособна, только если ваш script имеет тенденцию изменять одни и те же исходные файлы каждый раз.

Во-первых, здесь короткое объяснение для всех, кто смутил вопрос о том, почему Xcode иногда требует, чтобы вы дважды строили (или делали чистую сборку), чтобы увидеть определенные изменения, отраженные в целевом приложении. Xcode компилирует исходный файл, если отсутствует файл объекта, который он создает, или если дата последнего изменения файла объекта раньше, чем дата последнего изменения исходного файла была в начале первой фазы сборки. Если в вашем проекте запущен script, который изменяет исходный файл на фазе сборки до компиляции, Xcode не заметит, что дата последнего изменения исходного файла изменилась, поэтому он не захочет перекомпилировать его. Это только при создании проекта во второй раз, когда Xcode заметит изменение даты и перекомпилирует файл.

Здесь простое решение, если ваш script будет каждый раз изменять одни и те же исходные файлы. Просто добавьте этап сборки "Запуск script" в конце процесса сборки следующим образом:

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

Запуск touch в этих исходных файлах в конце процесса сборки гарантирует, что они всегда будут иметь последнюю дату последнего изменения, чем их объектные файлы, поэтому Xcode будет перекомпилировать их каждый раз.

Ответ 4

Как и в Xcode 4, похоже, если вы добавите сгенерированные файлы в раздел вывода фазы сборки, он будет соблюдать этот параметр и не будет генерировать сообщения об ошибках ... has been modified since the precompiled header was built.

Это хороший вариант, если ваш script генерирует только несколько файлов.

Ответ 5

Я так долго боролся с этим. Ответ заключается в использовании ento "External Target". Он ПОЧЕМУ эта проблема возникает и как мы ее используем на практике...

Шаги сборки Xcode4 не выполняются до тех пор, пока ПОСЛЕ компиляции plist. Это глупо, конечно, потому что это означает, что любые шаги предварительной сборки, которые изменяют plist, не вступят в силу. Но если вы подумаете об этом, они фактически начнут действовать... в NEXT build. Вот почему некоторые люди говорили о "кешировании" значений plist или "я должен сделать 2 сборки, чтобы заставить их работать". Что происходит, когда построено plist, выполняется ваш script. В следующий раз, когда вы создадите, plist строит, используя ваши измененные файлы, следовательно, вторая сборка.

Решение

ento - это тот способ, который, как я нашел, действительно выполняет настоящий шаг предварительной сборки. К сожалению, я также обнаружил, что он не вызывает обновление plist без чистой сборки, и я исправил это. Вот как мы имеем пользовательские значения, управляемые данными в plist:

  • Добавить проект внешней сборки, который указывает на python script и передает некоторые аргументы
  • Добавьте в сборку пользовательские настройки сборки. Это аргументы, которые вы передаете python (подробнее о том, почему мы это делаем позже)
  • python script читает некоторые входные файлы JSON и строит файл заголовка препроцессора plist И касается основного планшета приложения
  • В главном проекте включены файлы "preprocess plist" и указывает на этот файл препроцессора.

Использование касания к основному файлу plist приложения приводит к тому, что основная цель генерирует plist каждый раз. Причина, по которой мы передаем параметры сборки в качестве параметров, заключается в том, что наша сборка в командной строке может переопределять настройки:

  • Добавить пользовательскую переменную "foo" в проект предварительной сборки.
  • В вашей предварительной структуре вы можете использовать $(foo) для передачи значения в python script.
  • В командной строке вы можете добавить foo = test для передачи нового значения.

В python script используются файлы базовых настроек и разрешено переопределять значения по умолчанию для пользовательских файлов настроек. Вы вносили изменения и сразу же попадали в plist. Мы используем это только для настроек, которые ДОЛЖНЫ быть в plist. Для чего-то еще это пустая трата усилий... создайте файл json или что-то подобное вместо этого и загрузите его во время выполнения:)

Надеюсь, это поможет... это были пара грубых дней, когда мы это выяснили.