GCC это драйвер процесса сборки программы. Он выполняет свою работу путем организации последовательности запуска других программ, чтобы успешно прошла компиляция, ассемблирование и компоновка (линковка). GCC интерпретирует параметры своей командной строки и использует их, чтобы определить, какие программы он должен вызвать, и какие параметры командной строки следует использовать для каждой вызываемой программы. Это поведение управляется spec-строками. В большинстве случаев для каждой программы существует одна spec-строка, которую может применить GCC, однако у некоторых программ существует несколько spec-строк для управления их поведением. Встроенные в GCC spec-строки можно перезадать опцией -specs=имя_файла (или --specs=имя_файла), где указывается spec-файл.
Spec-файлы это простые текстовые файлы, которые содержат в себе spec-строки (далее вместо "spec-строка" может быть указано просто "spec"). Эти строки составляют последовательность директив, отделенных друг от друга пустыми строками. Тип директивы определяется по первому символу строки, не являющемуся пробелом. Этот символ может быть одним из следующих:
%command
Выдает команду процессору spec-файла. Здесь могут появляться следующие команды:
%include < file> Ищет файл и вставляет его текст в текущую позицию specs-файла.
%include_noerr < file> Работает наподобие '%include', но не генерирует сообщение об ошибке, если подключаемый файл не найден.
%rename old_name new_name Переименовывает spec-строку, old_name заменяется на new_name.
*[spec_name]:
Это указывает компилятору создать, переназначить или удалить именованную spec-строку. Все строки после этой директивы до следующей директивы или пустой строки считаются текстом для spec-строки. Если результат выполнения этой команды пустая строка, то эта spec-строка удаляется (или, если spec-строка не существует, то ничего не произойдет). Иначе, если spec в настоящее время не существует, будет создана новая spec. Если spec существует, то её содержимое будет переназначено текстом этой директивы, за исключением случая, когда первый символ текста является '+', тогда текст директивы пристыковывается к существующей spec.
[suffix]:
Создает новую пару '[suffix] spec'. Все строки после этой директивы и до следующей директивы или пустой строки считаются составляющими spec-строку для показанного suffix. Когда компилятор сталкивается с входным файлом с именем suffix, он обрабатывает spec-строку, чтобы решить, как компилировать этот файл.
Следующий пример говорит, что файл, заканчивающийся на '.ZZ', должен быть передан программе 'z-compile', для которой передается опция командной строки -input и с результатом выполнения подстановки '%i' (см. далее):
.ZZ:
z-compile -input %i
В качестве альтернативы предоставлению spec-строки, следующий за директивой suffix текст может быть одним из следующих:
@language Это указывает, что suffix является псевдонимом (alias) для известного language. Это подобно использованию ключа командной строки -x для GCC, чтобы явно указать язык. Следующий пример укажет, что .ZZ файлы по факту являются исходным кодом C++:
.ZZ:
@c++
#name Это приведет к сообщениям ошибки, которые говорят:
name compiler not installed on this system.
У GCC имеется большой список встроенных в него суффиксов. Эта директива добавляет запись в конец списка суффиксов, но поскольку этот список просматривается от конца к началу, то эта техника позволяет переназначить уже существующие суффиксы.
У GCC имеется следующий встроенный в него список spec-строк. Spec-файлы могут переназначать эти строки или создавать свои собственные. Обратите внимание, отдельные цели компиляции (target) могут иметь также свои собственные spec-строки для этого списка.
asm Опции для передачи ассемблеру.
asm_final Опции для передачи пост-процессору ассемблера.
cpp Опции для передачи препроцессору C.
cc1 Опции для передачи компилятору C.
cc1plus Опции для передачи компилятору C++.
endfile Объектные файлы для подключения в конец линковки.
link Опции для передачи компоновщику (линкеру).
lib Библиотеки для подключения в командную строку для линкера.
libgcc Решает, какую библиотеку поддержки GCC передать линкеру.
linker Устанавливает имя линкера.
predefines Определения define для передачи препроцессору C.
signed_char Определения define для передачи в CPP, чтобы указать, что char по умолчанию считается типом со знаком.
startfile Объектные файлы для подключения в начале линковки.
Вот небольшой пример spec-файла:
%rename lib old_lib *lib:
--start-group -lgcc -lc -leval1 --end-group %(old_lib)
Этот пример переименовывает spec с именем 'lib' в 'old_lib', и затем перезадает предыдущее определение 'lib' на новое. Это новое определение добавляет некоторые дополнительные опции командной строки перед подключением текста старого определения.
Spec-строки это список опций командной строки для передачи в соответствующую им программу. Дополнительно spec-строки могут содержать последовательности с префиксом '%' замены текстовой переменной или вставки текста в командную строку. Использование подобных конструкций позволяет генерировать довольно сложные командные строки.
Ниже приведена таблица всех определенных '%'-последовательностей для spec-строк. Обратите внимание, что пробелы не создаются автоматически вокруг результатов развертывания этих последовательностей. Таким образом, вы можете соединить их друг с другом, или скомбинировать их с постоянным текстом в один аргумент.
%%
Заменит один '%' имя программы или аргумент.
%"
Заменит пустой аргумент.
%i
Заменит имя входного обрабатываемого файла.
%b
Заменит базовое имя для выходных результатов, связанных с обрабатываемым входным файлом. Часто это подстрока до (и не включая) последней точки и не включая директорию, но если не активна %w, это расширяется до базового имени дополнительных выходных результатов, на которые могут влиять явное имя выходных результатов, и различные другие опции, которые управляют, как задается имя для дополнительных выходных результатов.
%B
То же самое, что и '%b', но включает суффикс файла (текст после последней точки). Без %w это расширяется в базовое имя для дампа выходных результатов.
%d
Помечает аргумент, содержащий или следующий за '%d' как имя временного файла, чтобы этот файл был удален после успешного завершения GCC. В отличие от '%g' это не вносит текст в аргумент.
%gsuffix
Заменит имя файла у которого есть суффикс suffix, который выбирается один раз на компиляцию, и помечает аргумент так же, как '%d'. Чтобы уменьшить возможности для атак типа отказ в обслуживании, имя файла теперь выбирается таким образом, что его трудно предсказать, даже когда известно ранее выбранные имена файлов. Например, '%g.s … %g.o … %g.s' может превратиться в 'ccUVUUAU.s ccXYAXZ12.o ccUVUUAU.s'. Значение suffix, совпадающее с regexp '[.A-Za-z]*' или специальной строке '%O', которая обрабатывается точно так же, как если бы '%O' был предварительно обработан. Ранее '%g' просто заменялся на имя файла, выбранное один раз на компиляцию, не обращая внимания на какой-либо присоединенный суффикс (который таким образом считался обычным текстом), что повышало вероятность успеха таких атак.
%usuffix
Подобно '%g', но генерирует новый временный файл при каждом появлении вместо одного файла на компиляцию.
%Usuffix
Заменяет последнее имя файла, сгенерированное с '%usuffix', генерируя новое, если нет такого последнего имени файла. При отсутствии какого-либо '%usuffix' это работает просто как '%gsuffix', за исключением того, что не используется одно и то же пространство суффиксов, так что '%g.s … %U.s … %g.s … %U.s' приведет к генерации двух различных имен файлов, один для каждого '%g.s', и другой для каждого '%U.s'. Ранее '%U' просто заменялось на имя файла, выбранное предыдущим '%u', не обращая внимания на какой-либо присоединенный суффикс.
%jsuffix
Заменит имя HOST_BIT_BUCKET, если оно есть, и если оно записываемое, и если не используется -save-temps; иначе заменит имя временного файла, наподобие как это делает '%u'. Этот временный файл предназначен не для обмена данными между процессами, а скорее как механизм удаления ненужного мусора (junk disposal mechanism).
%|suffix %msuffix
Работает наподобие '%g', кроме активности -pipe. В этом случае '%|' заменяется на одиночное тире, а '%m' ни на что не заменяется. Это два самых общих способа инструктировать программу, что она должна считывать из стандартного ввода, или записывать в стандартный вывод. Если вам нужно что-то более сложное, то можете использовать конструкцию '%{pipe:X}', см. пример gcc/fortran/lang-specs.h.
%.SUFFIX
Заменит .SUFFIX для суффиксов, которые совпали с аргументами ключа при последующем выводе с '%*'. SUFFIX завершается следующим пробелом или %.
%w
Пометит аргумент, содержащий или следующий за '%w' как назначенный выходной файл для этой компиляции. Это поместит аргумент в последовательность аргументов, которую подставляет '%o'.
%V
Показывает, это эта компиляция не создает выходного файла.
%o
Подставляет имена всех выходных файлов, автоматически обрамляя их пробелами. Также необходимо указать пробелы вокруг '%o', иначе результат будет неопределенным. '%o' предназначен для использования в spec-строках с целью запуска линкера. Входные файлы, имена которых не имеют распознанного суффикса, вообще не компилируются, но подключаются вместе с выходными файлами, т. е. линкуются.
%O
Подстановка суффикса для объектных файлов. Обратите внимание, что это обрабатывается специально, когда следует сразу за '%g', '%u' или '%U', из-за необходимости формирования полных имен файлов. Обработка такова, что '%O' обрабатывается точно так же, как если бы замена произошла, за исключением того, что '%g', '%u' и '%U' в настоящее время не поддерживают дополнительные символы суффикса, следующие за '%O', как например '.o'.
%I
Подстановка любого -iprefix (сделанного из GCC_EXEC_PREFIX), -isysroot (сделанного из TARGET_SYSTEM_ROOT), -isystem (сделанного из COMPILER_PATH и опций -B) и -imultilib по мере необходимости.
%s
Текущий аргумент это имя библиотеки или startup-файла некоторого вида. Поиск для этого файла в стандартном списке директорий и замена найденного полного имени. Текущая рабочая директория включена в список сканируемых директорий.
%T
Текущий аргумент это имя файла настроек компоновщика (linker script). Поиск этого файла осуществляется в текущем списке директорий на предмет сканирования библиотек. Если файл найден, то в командную строку вставляется опция --script, за которой идет полный путь до найденного файла. Если файл не найден, то генерируется сообщение об ошибке. Замечание: в текущей рабочей директории поиск не осуществляется.
%estr
Напечатает строку str как сообщение об ошибке. Строка str терминируется новой строкой. Используйте это при обнаружении несогласованных параметров.
%nstr
Напечатает строку str как примечание. Строка str терминируется новой строкой.
%(name)
В этом месте заменит содержимое spec-строки name.
%x{option}
Накопление опции для '%X'.
%X
Вывод накопленных опций линкера, указанных spec-строкой '%x'.
%Y
Вывод накопленных опций ассемблера, указанных через -Wa.
%Z
Вывод накопленных опций препроцессора, указанных -Wp.
%M
Вывод multilib_os_dir.
%R
Вывод конкатенации target_system_root и target_sysroot_suffix.
%a
Обработка asm spec. Это используется для вычисления ключей для передачи в ассемблер.
%A
Обработка asm_final spec. Это spec-строка для передачи ключей в постпроцессор ассемблера, если нужна такая программа.
%l
Обработка link spec. Это spec-строка для вычисления командной строки, передаваемой компоновщику (линкеру). Обычно это приводит к использованию последовательностей '%L %G %S %D and %E'.
%D
Вывод дампа опции -L для каждой директории, которая по мнению GCC может содержать startup файлы. Если цель компиляции (target) поддерживает multilib-ы, то текущая multilib-директория добавляется к каждому из этих путей.
%L
Обработка lib spec. Это spec-строка для принятия решения, какие библиотеки подключаются в командную строку линкера.
%G
Обработка libgcc spec. Это spec-строка для принятия решения, какая библиотека поддержки GCC подключается в командную строку линкера.
%S
Обработка startfile spec. Это spec-строка для принятия решения, какие объектные файлы первыми будут переданы линкеру. Обычно это может быть файл с именем crt0.o.
%E
Обработка endfile spec. Это spec-строка, которая указывает последние объектные файлы, передаваемые линкеру.
%C
Обработка cpp spec. Это используется для составления аргументов, передаваемых препроцессору C.
%1
Обработка cc1 spec. Это используется для составления опций, передаваемых актуальному компилятору C (cc1).
%2
Обработка cc1plus spec. Это используется для составления опций, передаваемых актуальному компилятору C++ (cc1plus).
%*
Замена переменной части совпавшей опции, см. далее. Обратите внимание, что каждая запятая в замещенной строке заменяется на одиночный пробел.
%< S
Удаляет всех вхождения -S из командной строки. Замечание: эта команда зависит от позиции. Команды '%' в spec-строке перед этой видят -S, команды '%' в spec-строке после этой не видят -S.
%< S*
Подобно '%< S', но соответствует всем ключам, начинающимся на -S.
%>S
Подобно '%< S', но сохраняет -S в командной строке GCC.
%:function(args)
Вызовет функцию по имени function с передачей ей аргументов args. Здесь args сначала обрабатываются как вложенная spec-строка, затем разделяется на вектор аргументов обычным образом. Функция возвращает строку, которая обрабатывается как если бы она литерально появлялась как часть текущей spec.
Предоставляются следующие встроенные spec-функции:
getenv Spec-функция getenv принимает 2 аргумента: переменную окружения и строку. Если переменная окружения не определена, то произойдет фатальная ошибка. Иначе возвращаемое значение это значение переменной окружения, объединенной с указанной в параметре строкой. Например, если переменная окружения TOPDIR определена как /path/to/top, тогда:
%:getenv(TOPDIR /include)
... расширится в /path/to/top/include.
if-exists Эта spec-функция принимает один аргумент, абсолютный путь до файла. Если файл существует, то if-exists вернет его pathname. Вот небольшой пример использования:
*startfile:
crt0%O%s %:if-exists(crti%O%s) crtbegin%O%s
if-exists-else Spec-функция if-exists-else подобна spec-функции if-exists, отличается только тем, что принимает 2 аргумента. Первый аргумент это абсолютный путь до файла. Если этот файл существует, то if-exists-else вернет pathname. Если не существует. то будет возвращен второй аргумент. Таким образом if-exists-else может использоваться для выбора одного или другого файла, основываясь на наличии первого. Вот небольшой пример использования:
*startfile:
crt0%O%s %:if-exists(crti%O%s) \
%:if-exists-else(crtbeginT%O%s crtbegin%O%s)
if-exists-then-else Spec-функция if-exists-then-else принимает как минимум 2 аргумента, и может опционально принимать третий аргумент. Первый аргумент это абсолютный путь до файла. Если этот файл существует, то функция вернет второй аргумент. Если файл не существует, то функция вернет третий аргумент, если он указан, или иначе вернет NULL. Это может использовано для раскрытия одного или другого текста, в зависимости от существования файла. Вот небольшой пример использования:
-l%:if-exists-then-else(%:getenv(VSB_DIR rtnet.h) rtnet net)
sanitize Spec-функция sanitize не принимает аргументов. Она вернет не-NULL, если активны какие-то санитайзеры адресов, потоков или неопределенного поведения.
%{%:sanitize(address):-funwind-tables}
replace-outfile Spec-функция replace-outfile принимает 2 аргумента. Она ищет первый аргумент в массиве выходных файлов, и заменяет его на второй аргумент. Вот небольшой пример использования:
%{fgnu-runtime:%:replace-outfile(-lobjc -lobjc-gnu)}
remove-outfile Spec-функция remove-outfile принимает один аргумент. Она ищет его значение в массиве выходных файлов и удалит его. Вот небольшой пример использования:
%:remove-outfile(-lm)
version-compare Spec-функция version-compare принимает 4 или 5 аргументов в следующей форме:
< comparison-op> < arg1> [< arg2>] < switch> < result>
Функция вернет результат, если сравнение было вычислено как true, иначе вернет NULL. Поддерживаемые значения для оператора сравнения comparison-op следующие:
>= True, если версия switch позже (или такая же), чем arg1.
!> Действие, противоположное >=.
< True, если версия switch более ранняя, чем arg1.
!< Действие, противоположное < .
>< True, если версия switch равна arg1 или более поздняя, и более ранняя, чем arg2.
< > True, если версия switch более ранняя, чем arg1, или arg2, или более поздняя.
Если switch вообще не присутствует, то условием будет false, за исключением случая, когда перед comparison-op стоит логическая инверсия !.
В следующем примере добавится -lmx, если было передано -mmacosx-version-min=10.3.9.
%:version-compare(>= 10.3 mmacosx-version-min= -lmx)
include Spec-функция include ведет себя наподобие %include, с тем достоинством, что она может быть вложена внутрь spec, так что может добавлять условие. Она принимает один аргумент, имя файла, и ищет его в пути startfile. Она всегда возвратит NULL.
%{static-libasan|static:%:include(libsanitizer.spec)%(link_libasan)}
pass-through-libs Spec-функция pass-through-libs принимает любое количество аргументов. Она ищет любые опции -l и любые не опции, заканчивающиеся на .a (что предполагает имена файлов архива входной библиотеки линкера), и возвратит результат, содержащий все найденные аргументы, с добавлением к каждому -plugin-opt=-pass-through=, и все это объединяется через пробелы. Этот список предназначен для передачи LTO-плагину линкера.
%:pass-through-libs(%G %L %G)
print-asm-header Функция print-asm-header не принимает аргументов, и просто напечатает примерно такой баннер:
Assembler options
=================
Use "-Wa,OPTION" to pass "OPTION" to the assembler.
Это используется, чтобы отделить опции компилятора от опций ассемблера в выводе --target-help.
gt Spec-функция gt принимает 2 или большее количество аргументов. Она возвратит "" (пустую строку), если аргумент от второго до последнего больше, чем последний аргумент, иначе возвратит NULL. В следующем примере вставляется link_gomp spec, если предоставленная в командной строке опция -ftree-parallelize-loops= больше 1:
%{%:gt(%{ftree-parallelize-loops=*:%*} 1):%:include(libgomp.spec)%(link_gomp)}
debug-level-gt Spec-функция debug-level-gt принимает один аргумент и возвратит "" (пустую строку), если debug_info_level больше, чем указанное число, иначе вернет NULL.
%{%:debug-level-gt(0):%{gdwarf*:--gdwarf2}}
%{S}
Заменяет ключ -S, если этот ключ предоставлен для GCC. Если этот ключ не указан, то ничего не подставляется. Обратите внимание, что начальное тире при указании этой опции опускается, и автоматически вставляется, если выполняется замена. Таким образом, spec-строка '%{foo}' будет соответствовать опции командной строки -foo, и выведет опцию командной строки -foo.
%W{S}
Наподобие %{S}, но помечает последний указанный аргумент как удаляемый в случае ошибки файл.
%@{S}
Наподобие %{S}, но выводит результат в FILE, и заменяет @FILE, если был предоставлен аргумент @file.
%{S*}
Заменяет все ключи, указанные для GCC, которые начинаются на -S, но которые также принимают аргумент. Это используется для ключей наподобие -o, -D, -I и т. п. GCC считает -o foo как один ключ, имя которого начинается на 'o'. %{o*} заменит этот текст, включая пробел. Таким образом, генерируются 2 аргумента.
%{S*&T*}
Наподобие %{S*}, но сохраняет порядок следования опций S и T (порядок S и T в spec не имеет значения). Здесь может быть любое количество разделенных амперсандом (&) переменных; опционально для каждого может быть wildcard. Полезно для CPP как '%{D*&U*&A*}'.
%{S:X}
Заменяет X, если для GCC указан ключ -S.
%{!S:X}
Заменяет X, если для GCC не был указан ключ -S.
%{S*:X}
Заменяет X, если для GCC указаны один или большее количество ключей, у которых имена начинаются на -S. Обычно X заменяется только один раз не обращая внимания, сколько появилось таких ключей. Однако если %* появился где-то в X, то X заменяется по одному для каждого совпавшего ключа с %*, замененным на часть этого ключа, который совпал с *.
Если %* появился как последняя часть spec-последовательности, то добавляется пробел после конца последней замены. Однако если в последовательности больше текста, то пробел не генерируется. Это позволяет замене %* использоваться как часть строки большего размера. Например spec-строка наподобие следующей:
%{mcu=*:--script=%*/memory.ld}
... когда совпадет с опций наподобие -mcu=newchip, создаст:
--script=newchip/memory.ld
%{.S:X}
Заменит X, если обрабатывается файл с суффиксом S.
%{!.S:X}
Заменит X, если не обрабатывается файл с суффиксом S.
%{,S:X}
Заменит X, если обрабатывается файл для языка S.
%{!,S:X}
Заменит X, если не обрабатывается файл для языка S.
%{S|P:X}
Заменит X если либо -S, либо -P были предоставлены для GCC. Это может также комбинироваться с '!', '.', ',' и * последовательностями, хотя они имеют более строгую привязку, чем '|'. Если %* появляется в X, то все альтернативы должны быть помечены звездочкой, и заменяется только первая совпавшая альтернатива.
Например, spec-строка наподобие:
%{.c:-foo} %{!.c:-bar} %{.c|d:-baz} %{!.c|d:-boggle}
... выведет следующие опции командной строки из следующих входных опций командной строки:
fred.c -foo -baz jim.d -bar -boggle -d fred.c -foo -baz -boggle -d jim.d -bar -baz -boggle
%{%:function(args):X}
Вызовет функцию по имени function с аргументами args. Если функция вернет не-NULL, то X заменится, если вернет NULL, то замены не будет.
%{S:X; T:Y; :D}
Если для GCC предоставлена S, то заменит X; иначе если для GCC предоставлена T, то заменит Y; иначе заменит D. Здесь может быть любое количество элементов в последовательности условий. Это может комбинироваться с ., ,, !, | и * по мере необходимости.
Совпавший с ключом S текст в '%{S}', '%{S:X}' или подобной конструкции может использовать обратный слеш для игнорирования специального значения следующего за ним символа, что позволяет литеральное соответствие символа, который иначе бы попал в специальную обработку. Например, '%{std=iso9899\:1999:X}' заменит X, если была предоставлена опция -std=iso9899:1999.
Текст условия X в '%{S:X}' или подобной конструкции может содержать вложенные '%' конструкты или пробелы, или даже новые строки. Они обрабатываются обычным образом, как было описано выше. Завершающий пробел в X игнорируется. Пробел также может появляться в любом месте этих конструктов слева от двоеточия, кроме . или * и соответствующего слова.
Ключи -O, -f, -m и -W в этих конструктах обрабатываются специальным образом. Если позже в командной строке было найдено другое значение -O или отрицающая форма ключа -f, -m или -W, то более ранний ключ игнорируется, кроме {S*}, где S это просто одна буква, которая передает все соответствующие опции.
Символ '|' в начале текста предиката используется, чтобы показать, что команда должна быть перенаправлена (pipe) в следующую команду, но только если указана -pipe.
Это встроено в GCC: какие ключи получают аргументы, а какие нет. Вы можете подумать, что было бы полезно обобщить это, чтобы позволить spec каждого компилятора указывать, какие ключи принимают аргументы. Однако это не может быть осуществлено целостно. GCC даже не может решить, какие входные файлы были указаны, если не знает, какие ключи принимают аргументы, и он должен знать, какие входные файлы компилировать, чтобы сказать, какие компиляторы запускать.
GCC также неявно знает, что аргументы, начинающиеся на -l, должны рассматриваться как выходные файлы компилятора и переданы линкеру в их правильном положении среди других выходных файлов.
[Ссылки]
1. Specifying Subprocesses and the Switches to Pass to Them site:gcc.gnu.org. |