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

<h2>что я сделал:</h2><p>у меня есть скрипт, который</p><ol> <li>прочитайте некоторые файлы конфигурации, чтобы сгенерировать фрагменты исходного кода</li> <li>найти соответствующие исходные файлы objective-c и</li> <li>замените некоторые части исходного кода сгенерированным кодом на шаге 1.</li> </ol><p>и makefile, который имеет специальный файл меток времени в качестве цели создания и файлы конфигурации в качестве целевых источников:</p><pre><code>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" поверх стека фаз сборки для цели проекта.что случилось:фаза сборки скрипта была запущена до компиляции исходного кода.однако, так как скрипт изменяет исходный код во время его выполнения, мне нужно было собрать <strong> дважды </strong>, чтобы получить самую последнюю версию продукта сборки. вот что я думаю, что происходит: 1-й запуск: xcode собирает информацию о зависимостях ---> без изменений 1-й запуск: xcode запускает "run script build phase" ---> источник меняется за спиной xcode 1-й запуск: xcode завершает сборку, думая, что ничего не нужно обновлять 2-й запуск: xcode собирает информацию о зависимостях ---> источник изменился, требуется перестройка! 2-й запуск: xcode запускает этап запуска сценария сборки "---> все обновлено 2-й прогон: xcode переходит к компиляции после прочтения <a href="http://gemma.apple.com/documentation/developertools/conceptual/xcodebuildsystem/200-build_phases/bs_build_phases.html">документация xcode по этапам сборки я попытался добавить исходный файл, который, как известно, будет обновляться каждый раз при запуске сценария в качестве результата "выполнить этапы построения сценария", но ничего не изменилось. поскольку количество файлов конфигурации может варьироваться в моем проекте, я не хочу указывать каждый входной и выходной файл.вопрос:как я делаю, чтобы xcode знал об изменениях исходного файла, сделанных во время "фазы построения сценария запуска"?правка: добавлено, что я поместил фазу сборки скрипта перед другими фазами сборки 

каждая упомянутая до сих пор техника является излишним. воспроизведение комментария <a href="https://stackoverflow.com/users/637441/steve-kim">steve kim для наглядности:на вкладке этапов сборки просто перетащите шаг "выполнить сценарий" в более высокое место (например, перед "компилировать источники").протестировано на xcode 6

это решение, вероятно, устарело. смотрите ответ с более высоким рейтингом.используйте "внешнюю цель": выберите "проект"> "новая цель ..." в меню выберите "mac os x"> "другие"> "внешняя цель" и добавьте ее в свой проект. откройте его настройки и заполните настройки скрипта откройте вкладку "общие" настроек основной цели и добавьте новую цель как ее прямую зависимость теперь запускается новая "внешняя цель" до главная цель даже начинает собирать информацию о зависимостях, поэтому любые изменения, сделанные во время выполнения скрипта, должны быть включены в сборку.

есть еще один, немного более простой вариант, который не требует отдельной цели, но он жизнеспособен, только если ваш скрипт имеет тенденцию каждый раз изменять одни и те же исходные файлы.во-первых, вот краткое объяснение для всех, кто не понимает, почему xcode иногда требует от вас сборки дважды (или чистой сборки), чтобы увидеть определенные изменения, отраженные в вашем целевом приложении. xcode компилирует исходный файл, если созданный им объектный файл отсутствует или если дата последнего изменения объектного файла раньше, чем дата последнего изменения исходного файла: в начале первого этапа сборки. если ваш проект запускает скрипт, который изменяет исходный файл на этапе сборки перед компиляцией, xcode не заметит, что дата последнего изменения исходного файла изменилась, поэтому он не потрудится перекомпилировать его. только когда вы создаете проект во второй раз, xcode заметит изменение даты и перекомпилирует файл.вот простое решение, если ваш скрипт каждый раз изменяет одни и те же исходные файлы. просто добавьте этап сборки run script в конце вашего процесса сборки вот так:touch classes/firstmodifiedfile.m classes/secondmodifiedfile.m exit $? </code></pre><p>запуск <code>touch</code> для этих исходных файлов в конце процесса сборки гарантирует, что у них всегда будет более поздняя дата последнего изменения, чем у их объектных файлов, поэтому xcode будет перекомпилировать их каждый раз.</p>

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

<p>я тоже долго боролся с этим. ответ заключается в использовании решения ento "внешняя цель". почему эта проблема возникает и как мы ее используем на практике ...</p><p>шаги сборки xcode4 не выполняются, пока после составления plist. конечно, это глупо, потому что это означает, что любые предварительные шаги, которые изменяют список, не вступят в силу. но если вы думаете об этом, они действительно вступают в силу ... на следующей сборке. вот почему некоторые люди говорили о "кэшировании" значений plist или "мне нужно сделать 2 сборки, чтобы это работало". что произойдет, если plist будет создан, то ваш скрипт будет запущен. в следующий раз, когда вы соберете, plist соберет ваши измененные файлы, следовательно, вторая сборка.</p><p>решение ento - единственный способ сделать настоящий шаг перед сборкой. к сожалению, я также обнаружил, что это не приводит к обновлению plist без чистой сборки, и я исправил это. вот как у нас есть управляемые данными пользовательские значения в списке:</p><ol> <li>добавьте проект внешней системы сборки, который указывает на скрипт python и передает некоторые аргументы</li> <li>добавить пользовательские настройки сборки в сборку. это аргументы, которые вы передаете python (подробнее о том, почему мы сделаем это позже)</li> <li>сценарий python считывает некоторые входные файлы json и создает заголовочный файл препроцессора plist и касается основного списка приложений.</li> <li>основной проект имеет включенные "файлы препроцессора" и указывает на этот файл препроцессора</li> </ol><p>использование прикосновения к основному файлу списка приложений приводит к тому, что главная цель генерирует список каждый раз. причина, по которой мы передаем настройки сборки в качестве параметров, заключается в том, что наша сборка из командной строки может переопределить настройки:</p><ol> <li>добавьте пользовательскую переменную "foo" в проект предварительной сборки.</li> <li>в вашей предварительной сборке вы можете использовать $ (foo) для передачи значения в скрипт python.</li> <li>в командной строке вы можете добавить foo = test, чтобы передать новое значение.</li> </ol><p>скрипт python использует файлы базовых настроек и позволяет пользовательским файлам настроек переопределять значения по умолчанию. вы вносите изменения, и они сразу же попадают в список. мы используем это только для настроек, которые должны быть в списке. для всего остального это пустая трата усилий .... вместо этого создайте файл json или что-то подобное и загрузите его во время выполнения :)</p><p>надеюсь, это поможет ... это была пара трудных дней, чтобы понять это.</p>