Программирование ARM SCons: опции командной строки, переменные сборки Fri, March 29 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

SCons: опции командной строки, переменные сборки Печать
Добавил(а) microsin   

У SCons имеется множество опций командной строки, управляющих поведением команды scons. Опция всегда начинается с одной или двух горизонтальных черточек (дефис).

Как не указывать всякий раз одни и те же опции: переменная окружения SCONSFLAGS. Вы можете обнаружить, что часто приходится вбивать одни и те же опции в командной строке scons каждый раз при необходимости её запуска. Например, если вводите -j 2, чтобы SCons могла запускать две команды сборки параллельно. Чтобы избежать каждый раз ввода -j 2, можно установить переменную внешнего окружения SCONSFLAGS в строку, содержащую -j 2, как и другие опции командной строки, которые всегда должна использовать SCons. SCONSFLAGS является исключением из обычного правила, по которому сама SCons избегает просмотра переменных окружения из запускаемого вами шелла.

Например, если вы используете POSIX-шелл, такой как bash или zsh, и всегда хотите запускать SCons с опцией -Q, то можете установить переменную окружения SCONSFLAGS следующим образом:

% export SCONSFLAGS="-Q"
% scons
    ... сообщения сборки ...

Командные оболочки csh-стиля на POSIX-системах устанавливают переменную окружения SCONSFLAGS следующим образом:

$ setenv SCONSFLAGS "-Q"

Для интерпретатора команд Windows (cmd) можно установить переменную окружения SCONSFLAGS следующим образом:

C:\Users\foo>set SCONSFLAGS="-Q"

Чтобы установить SCONSFLAGS постоянно, на POSIX-системах вы можете добавить эту настройку в файл запуска шелла (shell startup file), и на Windows вы можете использовать панель управления System Properties (свойства системы), чтобы выбрать постоянно настроенные переменные окружения (либо глобальные, для всех пользователей, либо только для текущего пользователя).

Считывание значений, установленных опциями командной строки: функция GetOption. SCons предоставляет функцию GetOption, чтобы можно было получить значения, установленные различными опциями командной строки.

Одним из случаев, когда может понадобиться использовать GetOption, это проверка, указана была или нет опция -h (или --help). Обычно SCons не печатает этот текст подсказки до тех пор, пока не прочитает все файлы SConscript, потому что текст подсказки может быть добавлен в любом из дочерних subsidiary файлах SConscript (которые могут находиться в разных подкаталогах проекта). Конено, чтение всех файлов SConscript займет некоторое дополнительное время. Если вы знаете, что ваша конфигурация не определяет любые дополнительные подсказки в файлах SConscript, то можете ускорить отображение подсказки по командной строке, используя функцию GetOption, чтобы загрузить дочерние файлы SConscript только когда опция -h или --help не была указана:

if not GetOption('help'):
   SConscript('src/SConscript', export='env')

Как правило строка, которую вы передали в функцию GetOption для выборки значения опции командной строки, совпадает с длинным именем параметра (указываемым с двойным дефисом), хотя имеются некоторые исключения. Список опций командной строки и строк для GetOption приведен далее в секции "Строки для считывания или установки значений опций командной строки".

GetOption может использоваться для получения значений опций, определенных вызовом AddOption. Но тогда вызов GetOption должен появляться после вызова AddOption. Если вызов AddOption предоставил аргумент с ключевым словом dest, то строка с таким именем это то, что следует передать в качестве аргумента в GetOption, в противном случае это (возможно измененная) версия первого длинного имени опции - см. описание AddOption.

Установка значений опций командной строки: функция SetOption. Вы также можете установить значения опций командной строки SCons из файлов SConscript, используя функцию SetOption. Строки, которые вы используете для установки значений командной строки SCons, можно посмотреть далее в секции "Строки для считывания или установки значений опций командной строки".

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

Для примера предположим, что администраторы ваших рабочих станций используют стандартизованную настройку переменной окружения NUM_CPU, чтобы указать количество процессоров в каждой системе. Вот кусочек кода Python для доступа к такой переменной окружения, что обеспечивает гибкую установку опции -j вызовом функции SetOption:

import os
 
num_cpu = int(os.environ.get('NUM_CPU', 2))
SetOption('num_jobs', num_cpu)
print("running with -j %s" % GetOption('num_jobs'))

В этом примере кода значение для опции --jobs берется из значения, указанного в переменной окружения NUM_CPU. Здесь мы видим одно из исключений правила, когда строка в параметре функций SetOption и GetOption записывается иначе, чем она указывается в опции командной строки, т. е. вместо --jobs указывается num_jobs (по историческим причинам). Приведенный здесь код печатает значение num_jobs с целью иллюстрации процесса. Используется значение по умолчанию 2 (если переменная окружения NUM_CPU не задана), чтобы обеспечить минимальный параллелизм даже на однопроцессорных системах:

% scons -Q
running with -j 2
scons: `.' is up to date.

Однако если переменная окружения NUM_CPU установлена, то вместо значения по умолчанию будет использоваться значение из NUM_CPU:

% export NUM_CPU="4"
% scons -Q
running with -j 4
scons: `.' is up to date.

Однако если значение опции -j или --jobs было явно указано в командной строке, то оно будет использоваться в первую очередь, независимо от того, была ли установлена переменная окружения NUM_CPU:

% scons -Q -j 7
running with -j 7
scons: `.' is up to date.
% export NUM_CPU="4"
% scons -Q -j 3
running with -j 3
scons: `.' is up to date.

Строки для считывания или установки значений опций командной строки. Строки, которые вы передаете в функции GetOption и SetOption, обычно соответствуют длинному имени опции (т. е. тому, которое указывается с двумя дефисами --), при этом любые дальнейшие дефисы должны заменяться на подчеркивания.

SetOption в настоящее время не поддерживается для опций, добавленных с помощью AddOption.

Полный список строк и соответствующих им переменных:

Строка для GetOption и SetOptionОпция (опции) командной строки
cache_debug --cache-debug
cache_disable --cache-disable
cache_force --cache-force
cache_show --cache-show
clean -c, --clean, --remove
config --config
directory -C, --directory
diskcheck --diskcheck
duplicate --duplicate
file -f, --file, --makefile, --sconstruct
help -h, --help
ignore_errors --ignore-errors
implicit_cache --implicit-cache
implicit_deps_changed --implicit-deps-changed
implicit_deps_unchanged --implicit-deps-unchanged
interactive --interact, --interactive
keep_going -k, --keep-going
max_drift --max-drift
no_exec -n, --no-exec, --just-print, --dry-run, --recon
no_site_dir --no-site-dir
num_jobs -j, --jobs
profile_file --profile
question -q, --question
random --random
repository -Y, --repository, --srcdir
silent -s, --silent, --quiet
site_dir --site-dir
stack_size --stack-size
taskmastertrace_file --taskmastertrace
warn --warn --warning

Добавление своих опций командной строки: функция AddOption. SCons также позволяет вам определить свои собственные опции командной строки с помощью функции AddOption. Она принимает такие же аргументы, что и метод add_option из стандартного библиотечного Python-модуля optparse.

Примечание: функция AddOption фактически реализована с использованием подкласса optparse.OptionParser.

Как только вы добавили свою опцию командной строки функцией AddOption, значение этой опции сразу же становится доступным (если эта опция была) для стандартной функции GetOption. Аргумент для GetOption должен быть таким же, как и имя переменной, которая хранит опцию. Если указано ключевое слово dest аргумента для AddOption, то значением является имя указанной переменной. Если не указано, то это имя (без начальных дефисов) первого длинного имени опции, заданного для AddOption после замены всех остальных дефисов символами подчеркивания, поскольку дефисы не допускаются в именах идентификаторов Python.

SetOption в настоящее время не поддерживает установку опций, добавленных функцией AddOption.

Один полезный пример использования этого функционала - предоставить --prefix, чтобы помочь в описании, где установить файлы:

AddOption(
   '--prefix',
   dest='prefix',
   type='string',
   nargs=1,
   action='store',
   metavar='DIR',
   help='installation prefix',
)
 
env = Environment(PREFIX=GetOption('prefix'))
 
installed_foo = env.Install('$PREFIX/usr/bin', 'foo.in')
Default(installed_foo)

Код в приведенном выше примере использует функцию GetOption для установки construction-переменнной $PREFIX в значение, которое вы указываете в опции командной строки --prefix. Из-за того, что переменная $PREFIX разворачивается в строку null, если она не инициализирована, то запуск SCons без опции --prefix установит файл в директорию /usr/bin/:

% scons -Q -n
Install file: "foo.in" as "/usr/bin/foo.in"

Однако если указать в командной строке --prefix=/tmp/install, то файл будет установлен в директорию /tmp/install/usr/bin/:

% scons -Q -n --prefix=/tmp/install
Install file: "foo.in" as "/tmp/install/usr/bin/foo.in"

Замечение: аргументы опций, отделенные от длинных опций пробелом вместо символа =, не могут быть корректно распознаны SCons. В то время как --input=ARG явер выбирается последующим arg, для --input ARG невозможно определить без инструкций, является ли ARG аргументом, принадлежащим введенной опции, или же это позиционный аргумент. SCons обрабатывает позиционные аргументы либо как опции сборки командной строки, либо как цели сборки (targets) которые доступны для использования в SConscript (подробности см. последующих секциях). Таким образом, они должны быть собраны до обработки SConscript. Так как вызовы AddOption, которые предоставляют инструкции по обработке для разрешения любой неоднозначности, происходят в SConscript, SCons не знает вовремя для опций, добавленных таким способом, и могут произойти неожиданные вещи, такие как аргументы опций, назначенные как target, и/или исключения из-за отсутствия аргументов опции.

Вследствие этого следует избегать стиля указания значений опций с пробелом вместо =. Для опций с одиночным аргументом, используйте в командной строке форму --input=ARG. Для опций с несколькими аргументами (где количество аргументов nargs больше одного), установите nargs в одном из вызовов AddOption, и либо скомбинируйте аргументы опций в одно слово с помощью символа-разделителя и обрабатывайте это слово в своем коде (для примера такого использования см. встроенную опцию --debug, которая позволяет указать несколько аргументов в качестве одного слова с разделителем запятой); или позволить указать одну и ту же опцию несколько раз путем установки action='append'. Оба метода могут поддерживаться одновременно.

Переменные сборки: variable=value. Вы можете захотеть управлять разными аспектами своей сборки путем указания в командной строке присваиваний в виде переменная=значение. Для примера предположим, что нужно иметь возможность собрать отладочную версию программы путем запуска SCons следующим образом:

% scons -Q debug=1

SCons предоставляет словарь ARGUMENTS, где сохранены все присваивания variable=value из командной строки. Это позволит вам модифицировать отдельные аспекты сборки в ответ на спецификации командной строки (обратите внимание, что если не требуется всегда указывать переменную, то вероятно необходимо использовать метод get словаря Python, который позволит вам указать значение по умолчанию для использования, если в командной строке ничего не было указано).

Следующий код установит construction-переменную $CCFLAGS в ответ на флаг debug, установленый в словаре ARGUMENTS:

env = Environment()
debug = ARGUMENTS.get('debug', 0)
if int(debug):
   env.Append(CCFLAGS='-g')
env.Program('prog.c')

В результате появляется возможность использовать в командной строке опцию debug=1:

% scons -Q debug=0
cc -o prog.o -c prog.c
cc -o prog prog.o
% scons -Q debug=0
scons: `.' is up to date.
% scons -Q debug=1
cc -o prog.o -c -g prog.c
cc -o prog prog.o
% scons -Q debug=1
scons: `.' is up to date.

SCons отслеживает точную командную строку, используемую для сборки каждого объектного файла, и как результат может определить, что объектный файл и фaйл исполняемого кода должен быть пересобран, когда меняется значение аргумента debug.

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

Чтобы удовлетворить эти требования, SCons предоставляет переменную ARGLIST, которая дает прямой доступ к установкам variable=value командной строки, в оригинальном порядке, в котором установки были указаны, и без удаления дубликатов. Каждый элемент в переменной ARGLIST сам по себе это двухэлементный список, содержащий ключевое слово и значение установки, и вы можете организовать цикл для обработки этого списка, или иной выбор элементов ARGLIST, чтобы обработать определенные настройки, которые хотите применять каким-то образом в своей конфигурации. Например, следующий код позволит вам добавить construction-переменную CPPDEFINES указанием нескольких настроек define= в командной строке:

cppdefines = []
for key, value in ARGLIST:
   if key == 'define':
      cppdefines.append(value)
env = Environment(CPPDEFINES=cppdefines)
env.Object('prog.c')

В результате получится следующий вывод:

% scons -Q define=FOO
cc -o prog.o -c -DFOO prog.c
% scons -Q define=FOO define=BAR
cc -o prog.o -c -DFOO -DBAR prog.c

Обратите внимание, что переменные ARGLIST и ARGUMENTS не влияют друг на друга, однако предоставляют вместо этого разный вид на настройки variable=value, указанные в командной строке. Вы можете обе эти переменные в одной и той же конфигурации SCons. Обычно словарь ARGUMENTS более удобен для использования (поскольку вы можете просто извлекать установленную переменную через доступ к словарю Python), и ARGLIST более гибкий (потому что можно проверить, в каком порядке были предоставлены настройки переменной в командной строке).

Управление переменными сборки командной строки. Использование переменных сборки командной строки наподобие debug=1 удобно, но как следствие может потребовать дополнительной работы по написанию кода Python для распознавания каждой такой переменной, проверки ошибок и предоставления соответствующих сообщений, а также применения значений для construction-переменной. Чтобы помочь с этим, SCons предоставляет класс Variables для упрощения определения таких переменных сборки, и механизм для применения переменных сборки в construction-окружении. Это позволит вам управлять тем, как перемененные сборки влияют на construction-окружения.

Для примера предподоложим, что вы хотите установить construction-переменную RELEASE через командную строку, когда настало время собрать конечный релиз ПО, и значение этой переменной должно быть добавлено в командную строку с соответствующим определением, чтобы оно было передано компилятору C. Ниже показано, как вы можете установить подходящее значение в словаре для construction-переменной $CPPDEFINES:

vars = Variables(None, ARGUMENTS)
vars.Add('RELEASE', default=0)
env = Environment(variables=vars, CPPDEFINES={'RELEASE_BUILD': '${RELEASE}'})
env.Program(['foo.c', 'bar.c'])

Этот файл SConstruct сначала создает объект Variables, который использует значения из командной строки в словаре ARGUMENTS (вызов vars=Variables(None, ARGUMENTS)). Затем он использует метод объекта Add для индикации, что переменная RELEASE может быть установлена в командной строке, и что если значение не задано, то по умолчанию значение будет 0. Созданный объект Variables передается в вызов Environment для создания construction-окружения, с использованием ключевого слова variables. Тогда это позволит вам установить переменную сборки RELEASE в командной строке, и отобразить переменную в командной строке, используемую для сборки каждого объектного файла из исходного файла C:

% scons -Q RELEASE=1
cc -o bar.o -c -DRELEASE_BUILD=1 bar.c
cc -o foo.o -c -DRELEASE_BUILD=1 foo.c
cc -o foo foo.o bar.o

Историческое замечание: в старых SCons (до версии 0.98.1), эти переменные сборки были известны как "command-line build options". В то время класс назывался Options, и предварительно определенные функции для construct-опций назывались BoolOption, EnumOption, ListOption, PathOption, PackageOption и AddOptions (в отличие от текущих имен, см. далее главу "Предварительно определенные функции переменной сборки"). Вы можете встретиться с этими именами в старых файлах SConscript, страничках wiki, блогах, статьях StackExchange и т. п. Больше эти старые имена не работают, но ментальная замена "Option" на "Variable" позволяет переносить концепции в текущие модели использования.

Предоставления подсказки для переменных сборки командной строки. Чтобы переменные сборки, указываемые в командной строке, были более удобны для использования, вы можете захотеть описать их, когда запускаете (опцией -h) вывод подсказки пользователя. Вы можете написать текст подсказки вручную, но в SCons есть и другая возможность. Объекты переменных представляют метод GenerateHelpText для генерации текста, который описывает различные переменные, добавленные в него. Текст по умолчанию включает саму строку подсказки плюс другая информация, такая как разрешенные значения (сгенерированный текст можно также поменять методом FormatVariableHelpText). Затем вы передаете вывод из этого метода в функцию Help:

vars = Variables(None, ARGUMENTS)
vars.Add('RELEASE', help='Set to 1 to build for release', default=0)
env = Environment(variables=vars)
Help(vars.GenerateHelpText(env))

Теперь SCons отобразит некоторый полезный текст, когда используется опция -h:

% scons -Q -h
 
RELEASE: Set to 1 to build for release
    default: 0
    actual: 0

Для подсказки по опциям командной строки scons используйте команду scons -H.

Как вы можете видеть, вывод подсказки покажет как значение по умолчанию, так и текущее реальное значение переменной сборки.

Чтение переменных сборки из файла. Хотя возможность указать значение переменной сборки в командной строке удобно, но это может быть утомительным каждый раз указывать переменные в командной строке при запуске SCons (если не написать для этого специальный скрипт). Для упрощения вы можете предоставить настроенные переменные сборки в локальном файле путем предоставления имени файла для создаваемого объекта Variables:

vars = Variables('custom.py')
vars.Add('RELEASE', help='Set to 1 to build for release', default=0)
env = Environment(variables=vars, CPPDEFINES={'RELEASE_BUILD': '${RELEASE}'})
env.Program(['foo.c', 'bar.c'])
Help(vars.GenerateHelpText(env))

Тогда это позволит вам управлять переменной RELEASE установкой её в файле custom.py:

RELEASE = 1

Обратите внимание, что этот файл реально выполняется как обычный скрипт Python. Теперь, когда вы запустите SCons:

% scons -Q
cc -o bar.o -c -DRELEASE_BUILD=1 bar.c
cc -o foo.o -c -DRELEASE_BUILD=1 foo.c
cc -o foo foo.o bar.o

И если вы поменяете содержимое custom.py так:

RELEASE = 0

.. то объектные файлы будут пересобраны в соответствии с новой переменной:

% scons -Q
cc -o bar.o -c -DRELEASE_BUILD=0 bar.c
cc -o foo.o -c -DRELEASE_BUILD=0 foo.c
cc -o foo foo.o bar.o

И наконец, вы можете комбинировать оба метода:

vars = Variables('custom.py', ARGUMENTS)

Здесь значения в файле опций custom.py перезаписываются значениями, указанными в командной строке.

[Предварительно определенные функции переменной сборки]

В SCons есть несколько удобных функций, предоставляющих готовое поведение для различных типов переменных сборки командной строки. Все эти функции возвратят кортеж (упорядоченный набор значений фиксированной длины), который готов к передачи в вызов метода Add или AddVariables. Конечно, вы свободны определить свое собственное поведение для функций.

Значения True/False: функция переменной сборки BoolVariable. Часто удобно иметь возможность указать переменную, которая управляет простым двоичным значением - true или false. Еще удобнее было бы использовать различные предпочтения для предоставления для того, как представить значения true или false. Функция BoolVariable упрощает размещение этих общих представлений true или false.

Функция BoolVariable принимает 3 аргумента: имя переменной сборки, значение по умолчанию переменной сборки и строка подсказки для переменной. Затем она возвратит соответствующую информацию для передачи в метод Add объекта Variables, примерно так:

vars = Variables('custom.py')
vars.Add(BoolVariable('RELEASE', help='Set to build for release', default=0))
env = Environment(variables=vars, CPPDEFINES={'RELEASE_BUILD': '${RELEASE}'})
env.Program('foo.c')

При наличии этой переменной сборки теперь можно разрешить переменную RELEASE, установив её в значение yes или t:

% scons -Q RELEASE=yes foo.o
cc -o foo.o -c -DRELEASE_BUILD=True foo.c
% scons -Q RELEASE=t foo.o
cc -o foo.o -c -DRELEASE_BUILD=True foo.c

Другие значения, эквивалентные true: y, 1, on и all.

И наоборот, для RELEASE можно теперь установить значение false, если задать для неё no или f:

% scons -Q RELEASE=no foo.o
cc -o foo.o -c -DRELEASE_BUILD=False foo.c
% scons -Q RELEASE=f foo.o
cc -o foo.o -c -DRELEASE_BUILD=False foo.c

Другие значения, эквивалентные false: n, 0, off и none.

И наконец, если вы попытаетесь указать любое другое значение, то SCons выдаст подходящее сообщение об ошибке:

% scons -Q RELEASE=bad_value foo.o
 
scons: *** Error converting option: RELEASE
Invalid value for boolean option: bad_value
File "/home/my/project/SConstruct", line 3, in < module>

Одно значение из выбора вариантов: функция переменной сборки EnumVariable. Предположим, что вы хотите устанавливать переменную COLOR для выборки цвета фона, отображаемого в приложении, но хотите при этом ограничить выбор только определенными цветами. Это можно легко организовать использованием функции EnumVariable, которая принимает список allowed_values (разрешенные значения) в дополнение к аргументам имени переменной, значения по умолчанию и текста подсказки:

vars = Variables('custom.py')
vars.Add(
    EnumVariable(
        'COLOR',
        help='Set background color',
        default='red',
        allowed_values=('red', 'green', 'blue'),
    )
)
env = Environment(variables=vars, CPPDEFINES={'COLOR': '"${COLOR}"'})
env.Program('foo.c')
Help(vars.GenerateHelpText(env))

Теперь вы можете явно установить переменную сборки COLOR в любое из указанных разрешенных значений:

% scons -Q COLOR=red foo.o
cc -o foo.o -c -DCOLOR="red" foo.c
% scons -Q COLOR=blue foo.o
cc -o foo.o -c -DCOLOR="blue" foo.c
% scons -Q COLOR=green foo.o
cc -o foo.o -c -DCOLOR="green" foo.c

Если попытаться установить COLOR в значение, которого нет в списке, то будет сгенерировано сообщение об ошибке:

% scons -Q COLOR=magenta foo.o
 
scons: *** Invalid value for option COLOR: magenta.  Valid values are: ('red', 'green', 'blue')
File "/home/my/project/SConstruct", line 10, in < module>

Этот пример также может служить иллюстрацией по генерации подсказки: сообщение подсказки здесь охватывает не только сам текст подсказки, но также и информацией, собранной из аргументов allowed_values и default:

% scons -Q -h
 
COLOR: Set background color (red|green|blue)
    default: red
    actual: red

Для подсказки по опциям командной строки scons используйте команду scons -H.

Функция EnumVariable также предоставляет способ отобразить привязать альтернативные имена для разрешенных значений. Предположим, что нужно сделать слово navy синонимом для blue. Это можно сделать добавлением словаря map, который привязывает свои ключевые значения к желаемому разрешенному значению:

vars = Variables('custom.py')
vars.Add(
    EnumVariable(
        'COLOR',
        help='Set background color',
        default='red',
        allowed_values=('red', 'green', 'blue'),
        map={'navy': 'blue'},
    )
)
env = Environment(variables=vars, CPPDEFINES={'COLOR': '"${COLOR}"'})
env.Program('foo.c')

Теперь вы можете указать navy в командной строке, и SCons будет транслировать это в blue, когда наступит момент использовать переменную COLOR для сборки target:

% scons -Q COLOR=navy foo.o
cc -o foo.o -c -DCOLOR="blue" foo.c

По умолчанию, когда используется функция EnumVariable, разрешенные значения учитывают регистр символов (case-sensitive):

% scons -Q COLOR=Red foo.o
 
scons: *** Invalid value for option COLOR: Red.  Valid values are: ('red', 'green', 'blue')
File "/home/my/project/SConstruct", line 10, in < module>
% scons -Q COLOR=BLUE foo.o
 
scons: *** Invalid value for option COLOR: BLUE.  Valid values are: ('red', 'green', 'blue')
File "/home/my/project/SConstruct", line 10, in < module>
% scons -Q COLOR=nAvY foo.o
 
scons: *** Invalid value for option COLOR: nAvY.  Valid values are: ('red', 'green', 'blue')
File "/home/my/project/SConstruct", line 10, in < module>

Функция EnumVariable может принять дополнительный аргумент с ключевым словом ignorecase, и когда он установлен в 1, то SCons не будет проверять регистр символов для указанных значений (case-insensitive):

vars = Variables('custom.py')
vars.Add(
    EnumVariable(
        'COLOR',
        help='Set background color',
        default='red',
        allowed_values=('red', 'green', 'blue'),
        map={'navy': 'blue'},
        ignorecase=1,
    )
)
env = Environment(variables=vars, CPPDEFINES={'COLOR': '"${COLOR}"'})
env.Program('foo.c')

Результаты запуска:

% scons -Q COLOR=Red foo.o
cc -o foo.o -c -DCOLOR="Red" foo.c
% scons -Q COLOR=BLUE foo.o
cc -o foo.o -c -DCOLOR="BLUE" foo.c
% scons -Q COLOR=nAvY foo.o
cc -o foo.o -c -DCOLOR="blue" foo.c
% scons -Q COLOR=green foo.o
cc -o foo.o -c -DCOLOR="green" foo.c

Обратите внимание, что значение 1 для ignorecase сохраняет проверку разрешенных значений, игнорируется только регистр символов. Если вы хотите, чтобы SCons транслировала имена в нижний регистр символов, независимо от того, в каком регистре пользователь указал значение, то можете установить ignorecase в значение 2:

vars = Variables('custom.py')
vars.Add(
    EnumVariable(
        'COLOR',
        help='Set background color',
        default='red',
        allowed_values=('red', 'green', 'blue'),
        map={'navy': 'blue'},
        ignorecase=2,
    )
)
env = Environment(variables=vars, CPPDEFINES={'COLOR': '"${COLOR}"'})
env.Program('foo.c')

Теперь SCons использует значения red, green или blue независимо от того, как они были указаны в командной строке:

% scons -Q COLOR=Red foo.o
cc -o foo.o -c -DCOLOR="red" foo.c
% scons -Q COLOR=nAvY foo.o
cc -o foo.o -c -DCOLOR="blue" foo.c
% scons -Q COLOR=GREEN foo.o
cc -o foo.o -c -DCOLOR="green" foo.c

Несколько значений из списка: функция переменной сборки ListVariable. Другой способ, который вы можете использовать для управления переменной сборки - указание списка разрешенных значений, в котором могут быть выбраны одно или несколько значений (где EnumVariable позволяет выбрать только одно значение). Эту возможность SCons предоставляет функцией ListVariable. Если, к примеру, вы хотите иметь возможность установить переменную COLORS в одно или большее количество разрешенных значений:

vars = Variables('custom.py')
vars.Add(
    ListVariable(
        'COLORS', help='List of colors', default=0, names=['red', 'green', 'blue']
    )
)
env = Environment(variables=vars, CPPDEFINES={'COLORS': '"${COLORS}"'})
env.Program('foo.c')

Теперь вы можете указать список, где элементы отделены запятой, транслируемый в список с разделителем символом пробела для передачи в команды сборки:

% scons -Q COLORS=red,blue foo.o
cc -o foo.o -c -DCOLORS="red -Dblue" foo.c
% scons -Q COLORS=blue,green,red foo.o
cc -o foo.o -c -DCOLORS="blue -Dgreen -Dred" foo.c

Дополнительно функция ListVariable позволит вам указать слова all или none для соответственно выбора всех разрешенных значений или ни одного из них:

% scons -Q COLORS=all foo.o
cc -o foo.o -c -DCOLORS="red -Dgreen -Dblue" foo.c
% scons -Q COLORS=none foo.o
cc -o foo.o -c -DCOLORS="" foo.c

И конечно недопустимое значение все еще будет генерировать сообщение об ошибке:

% scons -Q COLORS=magenta foo.o
 
scons: *** Error converting option: COLORS
Invalid value(s) for option: magenta
File "/home/my/project/SConstruct", line 7, in < module>

Вы можете использовать эту последнюю характеристику как способ принудительного применения как минимум одной из ваших допустимых опций путем указания допустимых значений с параметром names, и затем указанием значения, которого нет в этом списке, как параметр default. В таком случае если в командной строке не указано значение, то будет выбрано значение default, SCons выводит ошибки, поскольку это недопустимо. Пример фактически настроен таким способом, когда 0 используется как default:

% scons -Q foo.o
 
scons: *** Error converting option: COLORS
Invalid value(s) for option: 0
File "/home/my/project/SConstruct", line 7, in < module>

Эта техника работает также и для EnumVariable.

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

vars = Variables('custom.py')
vars.Add(
    PathVariable(
        'CONFIG', help='Path to configuration file', default='/etc/my_config'
    )
)
env = Environment(variables=vars, CPPDEFINES={'CONFIG_FILE': '"$CONFIG"'})
env.Program('foo.c')

Это позволит вам при необходимости переназначить переменную сборки CONFIG в командной строке:

% scons -Q foo.o
cc -o foo.o -c -DCONFIG_FILE="/etc/my_config" foo.c
% scons -Q CONFIG=/usr/local/etc/other_config foo.o
scons: `foo.o' is up to date.

По умолчанию PathVariable проверить, что указанный путь существует, и если такого пути нет, то генерирует ошибку:

% scons -Q CONFIG=/does/not/exist foo.o
 
scons: *** Path for option CONFIG does not exist: /does/not/exist
File "/home/my/project/SConstruct", line 7, in < module>

PathVariable предоставляет несколько методов, которые вы можете использовать, чтобы поменять это поведение. Если вы хотите убедиться, что указанные пути это фактически файлы, а не директории, то используйте для этого метод проверки PathVariable.PathIsFile:

vars = Variables('custom.py')
vars.Add(
    PathVariable(
        'CONFIG',
        help='Path to configuration file',
        default='/etc/my_config',
        validator=PathVariable.PathIsFile,
    )
)
env = Environment(variables=vars, CPPDEFINES={'CONFIG_FILE': '"$CONFIG"'})
env.Program('foo.c')

И наборорт, для проверки, что все указанные пути директории, а не файлы, используйте метод PathVariable.PathIsDir:

vars = Variables('custom.py')
vars.Add(
    PathVariable(
        'DBDIR',
        help='Path to database directory',
        default='/var/my_dbdir',
        validator=PathVariable.PathIsDir,
    )
)
env = Environment(variables=vars, CPPDEFINES={'DBDIR': '"$DBDIR"'})
env.Program('foo.c')

Если вы хотите убедиться, что все указанные пути это директории, и хотите, чтобы они были созданы, если пока не существуют, то используйте метод PathVariable.PathIsDirCreate:

vars = Variables('custom.py')
vars.Add(
    PathVariable(
        'DBDIR',
        help='Path to database directory',
        default='/var/my_dbdir',
        validator=PathVariable.PathIsDirCreate,
    )
)
env = Environment(variables=vars, CPPDEFINES={'DBDIR': '"$DBDIR"'})
env.Program('foo.c')

И наконец, если вам все равно, существует ли путь, является ли он файлом или директорией, то используйте метод PathVariable.PathAccept для принятия любого предоставленного пути:

vars = Variables('custom.py')
vars.Add(
    PathVariable(
        'OUTPUT',
        help='Path to output file or directory',
        default=None,
        validator=PathVariable.PathAccept,
    )
)
env = Environment(variables=vars, CPPDEFINES={'OUTPUT': '"$OUTPUT"'})
env.Program('foo.c')

Разрешенные/запрещенные имена путей: функция переменной сборки PackageVariable. Иногда вы можете захотеть предоставить больше управления для переменной имени пути, явно разрешая или запрещая его ключевыми словами yes или no, в дополнение к указанию явного имени пути. Для этого SCons предоставляет функцию PackageVariable:

vars = Variables("custom.py")
vars.Add(
    PackageVariable("PACKAGE", help="Location package", default="/opt/location")
)
env = Environment(variables=vars, CPPDEFINES={"PACKAGE": '"$PACKAGE"'})
env.Program("foo.c")

Когда файл SConscript предоставляет функцию PackageVariable, вы все еще можете использовать умолчание пути или предоставленное имя для переназначения имени пути, однако теперь можно явно установить указанную переменную в значение, указывающее на то, что пакет должен быть разрешен (в этом случае следует использовать значение по умолчанию) или запрещен:

% scons -Q foo.o
cc -o foo.o -c -DPACKAGE="/opt/location" foo.c
% scons -Q PACKAGE=/usr/local/location foo.o
cc -o foo.o -c -DPACKAGE="/usr/local/location" foo.c
% scons -Q PACKAGE=yes foo.o
cc -o foo.o -c -DPACKAGE="True" foo.c
% scons -Q PACKAGE=no foo.o
cc -o foo.o -c -DPACKAGE="False" foo.c

Добавление сразу нескольких переменных сборки командной строки. И наконец, SCons предоставляет способ добавить несколько переменных сборки в объект Variables. Вместо того, чтобы вызывать метод Add несколько раз, вы можете вызвать метод AddVariables с переменными сборки, добавляемыми в объект. Каждая переменная сборки указывается либо как кортеж аргументов, либо как вызов одной из предварительно определенных функций для предварительно упакованной командной строки переменных сборки, которая возвратит такой кортеж. Обратите внимание, что отдельный кортеж не может принимать аргументы ключевых слов так, как это может делать вызов Add или одна из функций переменных сборки. Порядок следования переменных, заданных AddVariables, не имеет значения.

vars = Variables()
vars.AddVariables(
    ('RELEASE', 'Set to 1 to build for release', 0),
    ('CONFIG', 'Configuration file', '/etc/my_config'),
    BoolVariable('warnings', help='compilation with -Wall and similiar', default=1),
    EnumVariable(
        'debug',
        help='debug output and symbols',
        default='no',
        allowed_values=('yes', 'no', 'full'),
        map={},
        ignorecase=0,
    ),
    ListVariable(
        'shared',
        help='libraries to build as shared libraries',
        default='all',
        names=list_of_libs,
    ),
    PackageVariable(
        'x11', help='use X11 installed here (yes = search some places)', default='yes'
    ),
    PathVariable('qtdir', help='where the root of Qt is installed', default=qtdir),
)

Обработка неизвестных переменных сборки командной строки: функция UnknownVariables. Конечно люди иногда в командной строке ошибочно указывают имена переменной. SCons не генерирует ошибку или предупреждение для любой неизвестной переменной, указанной в командной строке, потому что нельзя достоверно определить, действительно ли данная переменная "ошибочной", и создает ли она потенциальную проблему, или нет. В конце концов вы можете обработать аргументы напрямую, используя ARGUMENTS или ARGLIST с некоторым кодом Python в своем файле SConscript.

Однако если вы используете объект Variables для определения заданного набора переменных сборки командной строки, которые, как ожидается, будут установлены, то вы можете захотеть предоставить собственное сообщение об ошибке или предупреждение, если задан параметр переменной, которая не входит в список имен переменных, известных для объекта Variables. Это можно сделать вызовом метода UnknownVariables объекта Variables, чтобы получить параметры, не распознанные Variables:

vars = Variables(None)
vars.Add('RELEASE', help='Set to 1 to build for release', default=0)
env = Environment(variables=vars, CPPDEFINES={'RELEASE_BUILD': '${RELEASE}'})
unknown = vars.UnknownVariables()
if unknown:
    print("Unknown variables: %s" % " ".join(unknown.keys()))
    Exit(1)
env.Program('foo.c')

Метод UnknownVariables вернет словарь, содержащий ключевые слова и значения любых переменных, указанных в командной строке, которые не входят в число переменных, известных объекту Variables object (известных по вызовам метода Add). В примере выше проверяется, является ли словарь, возвращенный UnknownVariables, не пустым, и если он непустой, то печатается список Python, содержащий неизвестные переменные, и затем вызывается функция Exit для завершения SCons:

% scons -Q NOT_KNOWN=foo
Unknown variables: NOT_KNOWN

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

Обратите внимание, что вызов UnknownVariables следует отложить до тех пор, пока объект Variables не будет применен к construction-окружению с ключевым словом аргумента variables= при вызове Environment: пока это не произойдет, переменные в объекте не будут полностью обработаны.

[Ссылки]

1SCons: руководство пользователя, быстрый старт.
2. SCons: управление целями и результатами сборки.

 

Добавить комментарий


Защитный код
Обновить

Top of Page