Почему у gcc есть опция -include?

Я вижу, что gcc имеет параметр -include file, который ведет себя (вроде), как первая строка файла,

#include "file"

Каковы некоторые полезные возможности для этой опции?

Ответ 1

Один из вариантов использования -include в реальной жизни - в системе сборки ядра Linux.

При создании ядра Linux вы можете запустить огромное меню параметров конфигурации для настройки встроенного ядра. Например, здесь приведен параметр конфигурации для того, хотите ли вы поддерживать более одного ядра процессора в архитектуре x86 для ядра Linux 3.0:

config SMP
        bool "Symmetric multi-processing support"
        ---help---
          This enables support for systems with more than one CPU. If you have
          a system with only one CPU, like most personal computers, say N. If
          you have a system with more than one CPU, say Y.
          [...]

В исходном коде эта опция отображается как символ препроцессора, CONFIG_SMP. Исходный код ядра и драйверов может сделать #ifdef CONFIG_SMP, когда для более чем одного процессора требуется другой код. (Его также можно использовать в Makefile с другим синтаксисом, чтобы выбрать, следует ли компилировать файл или подкаталог .c.)

Как определяются эти символы препроцессора? Они не определены в командной строке компилятора, так как тогда это было бы смехотворно длинным (в типичном ядре распределения есть буквально тысячи этих символов, я считаю более 4000 из них для ядра, запущенного на этом компьютере). Вместо этого создается волшебный заголовочный файл со всеми этими параметрами. Этот заголовочный файл затем автоматически включается во все скомпилированные файлы с помощью опции -include include/generated/autoconf.h.

Так как символы препроцессора CONFIG_ должны быть доступны повсюду во всех файлах исходного кода ядра, использование -include (которое неявно включает его перед первой строкой файла) является хорошей вещью. Без этого вам нужно будет выполнить одно из следующих действий:

  • Явно включать его в качестве первого файла include на каждый из тысяч файлов исходного кода ядра, и надеюсь, что никто не забывает включать его или добавляет что-то перед включением.
  • Явно включать его в обычно используемый заголовок (например, kernel.h) и надеяться, что ничего, что зависит от символа CONFIG_, появляется перед первым прямым или косвенным включением этого заголовка.

Любая из этих опций явно уступает -include.

В ядре Linux есть другое использование -include, но оно более эзотерическое. Части ядра (в частности, ранние части загрузочного кода) должны запускаться в реальном режиме. Вместо того, чтобы записываться полностью в сборке, как и в прошлом, эти части ядра используют хак, где ассемблеру поручается испустить 32-битный код реального режима (.code16gcc). Это должно быть сделано как самое первое в исходном коде, прежде чем что-либо еще, что делает его отличным совпадением с -include (заголовок, включенный на этот раз, имеет только оператор asm(".code16gcc");).

Ответ 2

Это полезно для таких вещей, как префиксные заголовочные файлы, которые будут # включены во все файлы в проекте - несколько похожи на страшный StdAfx.h в Windows или файлы .prefix.h в Mac OS.

Ответ 3

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

Ответ 4

Мои два цента об этом: Мне пришлось использовать директиву -include с автоматически сгенерированными заголовками с "временем компиляции". Таким образом, ваш код может работать с настройками по умолчанию, и вы не испортите свой код файлами, которые могут или не могут существовать (например, вычисление зависимостей будет жаловаться), но вы можете изменить поведение кода на основе внешней конфигурации.