Пара слов о Header Map в XCode

    Семейство языков Си/Objective C/C++ нуждается в препроцессоре. Препроцессор пропускает компилируемый исходник через себя, прежде чем отдать текст на вход компилятору. Пожалуй самая важная часть работы препроцессора заключается в подстановке на место директив #include<имя-файла> содержимого указанного файла. Обычно указывают относительный путь (ex: stdio.h, sys/stat.h). Возникает закономерный вопрос — как препроцессор находит заголовочные файлы?

    Классический ответ такой: препроцессор последовательно перебирает пути в INCLUDE_PATH начиная с первого. Относительный путь из директивы include разрешается относительно (sic) папки из INCLUDE_PATH. Если файл не найден, переходим к следующему элементу INCLUDE_PATH. Если INCLUDE_PATH исчерпан, компилятор сообщает об ошибке.

    Но Apple как всегда вносит свои коррективы. При сборке в XCode дополнительно используются т.н. header map. Это индекс всех заголовочных файлов в проекте. Если XCode «знает» про foobar.h, то данный файл будет доступен просто по имени (#include<foobar.h>), вне зависимости от фактического размещения на файловой системе.

    Это прекрасное решение — до тех пор, пока оно работает как задумано. К сожалению, механизм header map плохо документирован, что не способствует быстрому разрешению проблем. Постараюсь восполнить этот пробел.


    Header Map

    определение
    Header Map представляет собой индексный файл, генерируемый XCode. Файл называется по имени текущей target и имеет расширение «.hmap». Индекс представляет собой множество строковых пар ключ—значение. Индекс используется для сопоставления полного пути к заголовочному файлу по аргументу директивы include. Поиск в индексе не чувствителен к регистру. Поиск в индексе имеет больший приоритет по сравнению с INCLUDE_PATH. Это сделано для ускорения сборки.

    По умолчанию header map активна. Для отключения нужно создать переменную USE_HEADERMAP=NO в конфигурации проекта.

    А что если в проекте используются несколько заголовочных файлов с одинаковыми именами? С настройками по умолчанию, в индекс попадет только один из этих файлов — какой именно, предсказать невозможно. Та же самая проблема возникает при совпадении имени файла в проекте с системным заголовочным файлом, без учета регистра (ex: Time.h).


    Просмотр Header Map


    Для диагностики бывает полезно заглянуть в сгенерированный header map. Это бинарный файл причем с замороченной оптимизацией общих подстрок. Хорошо, что добрые люди написали программу для конвертации в текстовый формат.

    python headermap.py build/hmap.build/Debug/primary.build/primary.hmap

    Здесь primary — имя цели.

    bar.h	/Users/nickz/hmap/bar.h
    foo.h	/Users/nickz/hmap/foo.h
    primary/foo.h	/Users/nickz/hmap/foo.h
    

    Если с foo.h и bar.h все более-менее понятно, то primary/foo.h вызывает вопросы. Как выяснилось, XCode создает такие записи для заголовочных файлов, являющихся членами текущей цели. (Для того, чтобы XCode позволил сделать заголовочным файлам членство в цели, нужно добавить в нее copy headers build phase.)

    В качестве префикса используется PRODUCT_NAME, причем если его поменять, в header map по-прежнему будет попадать старый префикс (проявляется на 3.2.5, возможно исправлено в новых версиях). Лечится закрытием–открытием проекта.


    Настройки


    USE_HEADERMAP = [YES]/NO
    Вкл/выкл.

    HEADERMAP_INCLUDES_FLAT_ENTRIES_FOR_TARGET_BEING_BUILT = [YES]/NO
    Включать в header map заголовочные файлы, которые являются членами активной цели. На 3.2.5 игнорируется.

    HEADERMAP_INCLUDES_FRAMEWORK_ENTRIES_FOR_ALL_PRODUCT_TYPES = [YES]/NO
    Включать в header map заголовочные файлы, которые являются членами активной цели. Файлы получают префикс $(PRODUCT_NAME)/. Если значение настройки —YES, такие записи создаются для любых типов целей; иначе только если тип цели — Framework.

    HEADERMAP_INCLUDES_PROJECT_HEADERS = [YES]/NO
    Включать в header map все заголовочные файлы из проекта без учета целей. На 3.2.5 игнорируется.

    Полный список настроек.
    • +13
    • 3.7k
    • 7
    Share post

    Comments 7

      0
      Голова пухнет от информации. Столько всего лишнего понапридумывали.
        0
        Эта фича ничем не хуже прочих.

        Для несложных проектов и начинающих разработчиков так просто удобнее. Кстати судя по направлению, в котором движется XCode (я про 4.x), Apple прежде всего ориентируется на эту целевую аудиторию при проектировании средств разработки (казалось бы, при чем тут i-устройства). Пичалька.
        0
        А нет ли возможности отключить эту фичу в Xcode полностью? Для всех новых проектов?
        Или для этого придётся править темплейты?
          0
          Боюсь, придется править темплейты. Еще вариант — изготовить .xcconfig, куда внести все свои нестандартные настройки, и просто копировать во все новые проекты. Это более правильный вариант, IMHO.

          Кстати я слышал что с отключенными header map возникают какие-то проблемы с вычислением зависимостей для генеренных файлов.
      • UFO just landed and posted this here
          0
          Кроссплатформенный проект вы вряд ли будете напрямую собирать икскодом.

        Only users with full accounts can post comments. Log in, please.