Xcode: запуск скрипта перед каждой сборкой, которая напрямую изменяет исходный код - PullRequest
54 голосов
/ 10 июня 2009

Что я сделал:

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

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

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

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

Я добавил этот Makefile в качестве «Фазы сборки Run Script» поверх стека фаз сборки для цели проекта.

Что случилось:

Фаза сборки сценария была запущена до компиляции исходного кода.

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

  1. 1-й запуск: Xcode собирает информацию о зависимостях ---> без изменений
  2. 1-й запуск: Xcode запускает "Run Script Build Phase" ---> источник меняется за спиной Xcode
  3. 1-й запуск: Xcode завершает сборку, думая, что ничего не нужно обновлять
  4. 2-й запуск: Xcode собирает информацию о зависимостях ---> источник изменился, необходимо перестроить!
  5. 2-й запуск: Xcode запускает этап запуска сценария запуска "---> все обновлено
  6. 2-й запуск: Xcode переходит к компиляции

После прочтения документации XCode по фазам сборки я попытался добавить исходный файл, который, как известно, обновляется каждый раз, когда скрипт запускается, как вывод «Выполнить фазы сборки скрипта», но ничего не изменилось. Поскольку количество файлов конфигурации может варьироваться в моем проекте, я не хочу указывать каждый входной и выходной файл.

Вопрос:

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

Edit:

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

Ответы [ 5 ]

80 голосов
/ 15 октября 2014

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

На вкладке фаз сборки просто перетащите шаг «Выполнить сценарий» в более высокое место (например, перед «Компиляцией источников»).

Проверено на XCode 6

28 голосов
/ 10 июня 2009

Возможно, это решение устарело. Смотрите ответ с более высоким рейтингом.


Использовать «Внешнюю цель»:

  1. Выберите «Проект»> «Новая цель ...» из меню
  2. Выберите «Mac OS X»> «Другое»> «Внешняя цель» и добавьте ее в свой проект
  3. Откройте его настройки и заполните настройки скрипта
  4. Откройте вкладку «Общие» настроек основной цели и добавьте новую цель в качестве прямой зависимости

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

3 голосов
/ 21 октября 2011

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

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

Вот простое решение, если ваш скрипт каждый раз изменяет одни и те же исходные файлы. Просто добавьте этап сборки Run Script в конце процесса сборки , например:

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

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

2 голосов
/ 14 июля 2013

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

Это хороший вариант, если ваш скрипт генерирует только несколько файлов каждый раз.

1 голос
/ 23 февраля 2013

Я тоже долго с этим боролся. Ответ заключается в использовании решения ento «Внешняя цель». ПОЧЕМУ эта проблема возникает и как мы ее используем на практике ...

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

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

  1. Добавить проект внешней системы сборки, который указывает на скрипт Python и передает некоторые аргументы
  2. Добавление пользовательских настроек сборки в сборку. Это аргументы, которые вы передаете Python (подробнее о том, почему мы делаем это позже)
  3. Скрипт python считывает некоторые входные файлы JSON и создает заголовочный файл препроцессора plist И касается основного списка приложений
  4. В основном проекте включены «файлы препроцессной обработки», и он указывает на этот файл препроцессора

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

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

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

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

...