контроль того, какой файл заголовка проекта Xcode будет включать

Мой проект Xcode построен на вариантах одного и того же продукта с использованием двух целей. Разница между ними заключается только в том, какая версия включенной библиотеки используется. Для исходных файлов .c легко назначить правильную версию правильной цели, установив флажок цели. Однако включение заголовочного файла всегда включает один и тот же файл. Это верно для одной цели, но неверно для другой.

Есть ли способ контролировать, какой заголовочный файл включается каждой целью?

Вот моя иерархия файлов проекта (которая реплицируется в Xcode):

MyProject
  TheirOldLib
    theirLib.h
    theirLib.cpp
  TheirNewLib
    theirLib.h
    theirLib.cpp
myCode.cpp

и myCode.cpp делает такие вещи, как:

#include "theirLib.h"
…
somecode()
{
#if OLDVERSION
  theirOldLibCall(…);
#else
  theirNewLibCall(…);
#endif
}

И, конечно же, я определяю OLDVERSION для одной цели, а не для другой.

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

#include "TheirOldLib/theirLib.h"
#include "TheirNewLib/theirLib.h"

Итак, есть ли способ сообщить Xcode, какие theirLib.h включать для каждой цели?

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

Спасибо за любую помощь.


person Jean-Denis Muys    schedule 07.04.2010    source источник


Ответы (5)


Ключевой частью ответа является использование USE_HEADERMAP = NO, как предложил Крис в комментарии. Вот подробности.

Краткий рецепт (проверено в Xcode 3.2.2):

  1. добавьте пользовательскую настройку сборки USE_HEADERMAP = NO для каждой соответствующей цели. Вот как:
    1.1. Откройте информационную панель цели на панели "Сборка".
    1.2. Раскройте всплывающее меню действий в левом нижнем углу окна, выберите «Добавить пользовательские настройки».
    1.3. Во вновь добавленной строке установите для первого столбца («Настройка») значение USE_HEADERMAP, а для второго столбца («Значение») значение NO.

  2. добавьте правильный путь включения для каждой цели (настройки сборки цели «Пути поиска заголовков»). В моем примере это будет:
    2.1. добавить TheirOldLib для "старой" цели
    2.2. добавить TheirNewLib для "новой" цели

Шаг 1 отключает функцию автоматической карты заголовков Xcode, с помощью которой любой файл заголовка, включенный в проект, доступен напрямую по его имени, независимо от его фактического пути. Когда два заголовка имеют одно и то же имя, эта функция приводит к неразрешимой неоднозначности.

Шаг 2 позволяет #include "theirLib.h" работать без уточнения фактического имени пути к заголовочному файлу.

Эти два шага вместе выполняют два моих ограничения.

Наконец, насколько я могу судить, Apple не задокументировала USE_HEADERMAP. Я заполню отчет об ошибке для этого, так как этот параметр имеет решающее значение в ряде случаев, как показывает поиск в Google. Сообщается как rdar://7840694. Также в Open Radar как http://openradar.appspot.com/radar?id=253401

person Jean-Denis Muys    schedule 08.04.2010
comment
Это работает отлично. Спасибо. Плакат должен был пометить это как ответ. - person SmallChess; 04.12.2010
comment
Разве на шаге 2 не должно быть путей поиска заголовков пользователей вместо путей поиска заголовков? поскольку вы используете #include, а не #include ‹› - person Olof; 13.04.2012
comment
Я думаю, вы правы. Header Search Paths работает, но User Header Search Paths должно быть лучше. - person Jean-Denis Muys; 13.04.2012

USE_HEADERMAP=NO является излишним для некоторых проектов. Может быть достаточно просто использовать HEADERMAP_INCLUDES_FLAT_ENTRIES_FOR_TARGET_BEING_BUILT=NO.

Документация здесь:
https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html

person bleater    schedule 07.09.2011
comment
Это определенно. Давайте добавим, что этот параметр должен быть добавлен в пользовательский раздел, в xcode выберите цель, нажмите маленький +, добавьте пользовательский... готово - person Juraj Antas; 27.05.2021

Xcode ускоряет сборку, создавая файлы карты заголовков.

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

Вот пример такого файла карты:
(Примечание: Это не фактический синтаксис файла карты заголовка, это просто удобочитаемое представление)

Foo.h -> /usr/include/Foo.h
Bar.h -> /home/user/Documents/ProjectA/src/include/Bar.h
foo/bar/foobar.h -> /home/user/Documents/ProjectB/inc/foo/bar/foobar.h

Эти три записи совпадают

#include "Foo.h"
#include "Bar.h"
#include "foo/bar/foobar.h"

Теперь Xcode имеет три параметра, которые управляют генерацией файлов карт заголовков.

  1. HEADERMAP_INCLUDES_FLAT_ENTRIES_FOR_TARGET_BEING_BUILT
    Если YES (по умолчанию), все файлы заголовков, принадлежащие создаваемой цели, добавляются в файл сопоставления заголовков и могут быть включены с помощью include "header.h". Обратите внимание, что заголовки могут принадлежать только целям фреймворка/библиотеки/пакета, а не целям приложения/программы.

  2. HEADERMAP_INCLUDES_FRAMEWORK_ENTRIES_FOR_ALL_PRODUCT_TYPES
    Если YES (по умолчанию), заголовки всех других целей добавляются в файл сопоставления заголовков и могут быть включены с помощью include <TargetName/header.h>. Обратите внимание, что это также верно для целей, не связанных с платформой.

  3. HEADERMAP_INCLUDES_PROJECT_HEADERS
    Если YES (по умолчанию), все остальные заголовки, присутствующие в файле проекта, к которому принадлежит построенная цель, также добавляются в файл сопоставления заголовков и могут быть включены с помощью include "header.h".

Кроме того, существует общая настройка USE_HEADERMAP, которая определяет, должен ли вообще генерироваться файл сопоставления заголовков. Только если YES (по умолчанию), Xcode сгенерирует файл карты заголовков и передаст его в качестве аргумента компилятору.

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

Если заголовок импортирован с параметром <...>, он будет выполнять поиск во всех каталогах, указанных с параметром -I (HEADER_SEARCH_PATHS), во всех каталогах, указанных с параметром -isystem (SYSTEM_HEADER_SEARCH_PATHS в Xcode), и в стандартных каталогах заголовков системы (/usr/include выбранного SDK и другие каталоги, принадлежащие установленным инструментам разработчика); именно в таком порядке и в каждой категории в указанном порядке.

Если заголовок импортирован с "...", он будет искать в том же каталоге, что и создаваемый файл .c/.m, во всех каталогах, указанных с опцией -iquote (USER_HEADER_SEARCH_PATHS в Xcode), и в тех же каталогах он ищет <...> ; именно в таком порядке и в каждой категории в указанном порядке.

person Mecki    schedule 28.08.2020

Почему вы не можете просто использовать разные пути включения в каждой цели?

person Azeem.Butt    schedule 08.04.2010
comment
Если у вас есть два заголовка с одинаковым именем, но разными путями, вам нужно установить USE_HEADERMAP = NO в пользовательской настройке сборки в обеих целях. - person cdespinosa; 08.04.2010
comment
Аккуратный. Наверное, я никогда не пробовал. Приятно видеть, что Майк Феррис все еще борется против юзабилити. - person Azeem.Butt; 08.04.2010
comment
использование разных путей включения в каждой цели не работает: включаемые файлы в проекте всегда доступны Xcode без пути вообще. Это причина, по которой проблема возникает в первую очередь: без пути для устранения неоднозначности, похоже, нет способа указать Xcode, какой файл является правильным для включения. Крис предлагает отключить эту функцию, чтобы вернуться к более традиционной схеме пути включения. Подробно напишу ответ. - person Jean-Denis Muys; 08.04.2010

Используйте USE_HEADERMAP=NO и в «Пути поиска заголовков пользователей» сначала включите свой собственный каталог, а затем рекурсивно каталог проекта: ${PROJECT_DIR}/TheirNewLib ${PROJECT_DIR}/**

person Ivan    schedule 03.02.2019