Программирование ARM SCons: управление выводом процесса сборки Thu, April 25 2024  

Поделиться

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

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

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

Ключевой аспект создания удобной для работы конфигурации сборки - предоставление полезного вывода процесса сборки, чтобы её пользователи могли легко понять, что делает сборка, и получить информацию о том, как управлять сборкой. SCons предоставляет несколько способов управления выводом из конфигурации сборки, чтобы помочь сделать сборку более удобной и понятной.

Функция Help. Часто очень полезно дать пользователям подсказку, которая описывает определенные target-ы, опции сборки, и т. д., что можно использовать для вашей сборки. SCons предоставляет функцию Help, позволяющую указать текст подсказки:

Help("""Type: 'scons program' to build the production program,
      'scons debug' to build the debug version.
""")

Опционально, при необходимости можно указать флаг добавления append:

Help("""Type: 'scons program' to build the production program,
      'scons debug' to build the debug version.
""", append=True)

Обратите внимание, что в примере выше используется Python-синтаксис тройной кавычки, который удобен для многострочных строк наподобие текста подсказки.

Когда файлы SConstruct или SConscript содержат такой вызов функции Help, указанный текст подсказки будет отображен в ответ на опцию -h:

% scons -h
scons: Reading SConscript files ...
scons: done reading SConscript files.
 
Type: 'scons program' to build the production program,
      'scons debug' to build the debug version.

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

Файлы SConscript могут содержать несколько вызовов функции Help, в таком случае указанные тексты будут при отображении объединены. Это позволяет распределить тексты подсказок между разными несколькими файлами SConscript. В такой ситуации порядок, в каком файлы SConscript вызываются, будет определять порядок, в каком будут вызываться функции Help, что в свою очередь будет определять порядок, в котором отдельные части текста подсказки будут объединены в общий выведенный текст подсказки.

При использовании с AddOption Help("text", append=False) все выходные данные вывода подсказки, связанные с AddOption(), будут скрыты. Чтобы сохранить вывод подсказки из AddOption(), установите append=True.

Другое использование состоит в том, чтобы обусловить текст справки соответствием некоторой переменной. Для примера предположим, что вы только хотите отобразить строку при сборке, но только на Windows-версии программы, которая в реальности запускается на Windows. Следующий файл SConstruct:

env = Environment()
 
Help("\nType: 'scons program' to build the production program.\n")
 
if env['PLATFORM'] == 'win32':
   Help("\nType: 'scons windebug' to build the Windows debug version.\n")

.. отобразит полный текст подсказки на Windows:

C:\>scons -h
scons: Reading SConscript files ...
scons: done reading SConscript files.
 
Type: 'scons program' to build the production program.
 
Type: 'scons windebug' to build the Windows debug version.

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

Однако покажет только соответствующую опцию на системе Linux или UNIX:

% scons -h
scons: Reading SConscript files ...
scons: done reading SConscript files.
Type: 'scons program' to build the production program.

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

Если в файлах SConstruct или SConscript нет текста Help, то SCons откатится к отображению своего стандартного списка, который описывает опции командной строки SCons. Этот список также всегда отображается, когда использовалась опция командной строки -H.

Управление выводом команд сборки: переменные $*COMSTR. Иногда вывод команд, выполняемых для компиляции объектных файлов или или линковки программ (или сборки других target) может быть очень длинным, настолько длинным, что пользователям трудно вычленить сообщения об ошибках или другой важный для них вывод от мусора, генерируемого самими командами. Все переменные по умолчанию $*COM, которые задают строки команд, используемы для сборки target-файлов разных типов, имеют соответствующую переменную $*COMSTR, которая может использоваться как альтернативная строка, которая отобразится, когда выполняется сборка target.

Для примера предположим, что вы хотите, чтобы SCons отображала сообщение "Compiling" всякий раз при компиляции объектного файала, и "Linking", когда линкуется исполняемый код. Вы можете для этого написать файл SConstruct, выглядящий примерно так:

env = Environment(CCCOMSTR = "Compiling $TARGET",
                  LINKCOMSTR = "Linking $TARGET")
env.Program('foo.c')

Тогда вывод компиляции будет следующий:

% scons -Q
Compiling foo.o
Linking foo

SCons производит полную замену переменных на $*COMSTR, так что имеется доступ ко всем стандартным переменным типа $TARGET, $SOURCES, и т. д., как и к любым construction-переменным, которые могут быть сконфигурированы в construction-окружении, используемом для сборки определенного target.

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

Один из способов дать пользователям больше контроля над тем, должна или не должна SCons печатать реальную командную строку при компиляции, или строка вывода должна быть короткой, сконфигурированной сводкой - добавить в свой файл SConstruct поддержку переменной командной строки VERBOSE. Для этого простая конфигурация может выглядеть так:

env = Environment()
if ARGUMENTS.get('VERBOSE') != '1':
    env['CCCOMSTR'] = "Compiling $TARGET"
    env['LINKCOMSTR'] = "Linking $TARGET"
env.Program('foo.c')

Путем установки соответствующих переменных $ * COMSTR только в том случае, если пользователь указывает в командной строке VERBOSE = 1, пользователь может управлять тем, как SCons отображает эти конкретные командные строки:

% scons -Q
Compiling foo.o
Linking foo
% scons -Q -c
Removed foo.o
Removed foo
% scons -Q VERBOSE=1
cc -o foo.o -c foo.c
cc -o foo foo.o

Небольшое напоминание: многие команды для сборки приходят парами, в зависимости от того, нужно ли собирать объект для использования в shared-библиотеке, или нет. Строки команд отражают это, поэтому может потребоваться кроме установки CCCOMSTR также установить и SHCCCOMSTR, чтобы получить желаемые результаты.

Предоставление вывода информации прогресса сборки: функция Progress. Другой аспект предоставления хорошего вывода процесса сборки - дать пользователю обратную связь о том, что делает SCons, даже когда в настоящий момент ничего не собирается. Это может быть особенно верным для больших сборок, когда когда большинство target-файлов уже в актуальном состоянии (up-to-date, т. е. уже корректно собраны). Из-за того, что SCons может потратить много времени на проверку адекватности каждого объектного файла (проверяется его текущая сигнатура) по отношению к большому числу файлов зависимостей, пользователи могут (особенно на слабых машинах) ошибочно предположить, что SCons зависла или произошла какая-то проблема при сборке.

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

Progress('Evaluating $TARGET\n')
Program('f1.c')
Program('f2.c')

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

% scons -Q
Evaluating SConstruct
Evaluating f1.c
Evaluating f1.o
cc -o f1.o -c f1.c
Evaluating f1
cc -o f1 f1.o
Evaluating f2.c
Evaluating f2.o
cc -o f2.o -c f2.c
Evaluating f2
cc -o f2 f2.o
Evaluating .

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

Progress('$TARGET\r',
         file=open('/dev/tty', 'w'),
         overwrite=True)
Program('f1.c')
Program('f2.c')

Обратите внимание, что мы также указали для функции Progress аргумент overwrite=True, который заставляет SCons "вычистить" предыдущую выведенную строку символами пробела перед печатью новой строки Progress. Без аргумента overwrite=True более короткие выводимые имена файлов не перезапишут все символы ранее выведенных имен файлов, которые были длиннее, что сделает вывод неудобочитаемым. Также обратите внимание, что мы открыли файл /dev/tty для прямого доступа (на POSIX) к экрану пользователя. На Windows эквивалентом может быть имя файла con:.

Также важно знать, что хотя мы можем использовать $TARGET для подстановки имени объекта в строку, функция Progress не выполняет подстановку общих переменных (например потому что при оценке объекта наподобие файла исходного кода не обязательно вовлекается construction-окружение).

Вы можете также указать список строк для функции Progress, в таком случае SCons будет печатать каждую строку по очереди. Это может использоваться для реализации "spinner" посредством цикла SCons по последовательности строк:

Progress(['-\r', '\\\r', '|\r', '/\r'], interval=5)
Program('f1.c')
Program('f2.c')

Обратите внимание, что здесь мы используем аргумент с ключевым словом interval=, чтобы SCons печатала новую строку "spinner" только через 5 обработанных объектов. Использование interval=count, даже со строками $TARGET как в примерах выше, может быть хорошим способом уменьшить затраты ресурсов, которые SCons тратит на печать строк функции Progress, все еще давая пользователю обратную связь о том, что SCons все еще работает над анализом элементов сборки.

И наконец, вы можете получить прямой контроль над тем, что печатается на каждом оцениваемом файле путем передачи вашей Python-функции (или другого вызываемого объекта Python) в функцию Progress. Ваша функция будет вызвана для каждого оцениваемого объекта, позволяя реализовать более продвинутую логику наподобие добавления счетчика прогресса:

screen = open('/dev/tty', 'w')
count = 0
def progress_function(node)
   count += 1
   screen.write('Node %4d: %s\r' % (count, node))
 
Progress(progress_function)

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

Здесь может возникнуть резонный вопрос: поскольку отображается прогресс сборки, как можно узнать общее количество обрабатываемых объектов, чтобы дать понять пользователю, насколько сборка близка к завершению? К сожалению в общем случае нет хорошего способа реализации такого функционала прогресса, за исключением того, чтобы SCons оценивала её граф зависимостей дважды, первый раз для подсчета общего количества, и второй раз при выполнении реальной сборки target-ов. Это было бы необходимо, потому что вы не можете знать заранее, какие target реально были запрошены для сборки. Например, вся сборка может состоять из тысячей Node, однако пользователь возможно пользователь специально запросил сборку только одного объектного файла.

Печать уточненного состояния сборки: функция GetBuildFailures. SCons, как и большинство инструментов сборки, вернет в shell состояние 0 в случае успеха, и ненулевое состояние в случае ошибки. Иногда полезно давать больше информации о статусе сборки в конце запуска, например для печати информативных сообщений, отправки email, и т. п.

SCons предоставляет метод GetBuildFailures, который вы можете использовать в Python-функции atexit, чтобы получить список объектов, описывающих действия, которые потерпели неудачу при попытке выполнить сборку target-файлов. Их может быть больше одного, если вы использовали -j (опция, которая разрешает одновременно N задач). Вот простой пример:

import atexit
 
def print_build_failures():
    from SCons.Script import GetBuildFailures
    for bf in GetBuildFailures():
        print("%s failed: %s" % (bf.node, bf.errstr))
atexit.register(print_build_failures)

Вызов atexit.register регистрирует print_build_failures как callback-функцию atexit, чтобы она была вызвана перед выходом SCons. Когда эта функция вызывается, она вызывает GetBuildFailures для извлечения списка сбойных объектов. См. страничку документации man для подробного содержимого возвращаемых объектов; некоторые более полезные атрибуты это .node, .errstr, .filename и .command. Здесь filename необязательно такое же, как у node; здесь node это target, который собирался, когда произошла ошибка, хотя filename это файл или директория, которая фактически вызвала ошибку. Замечание: вызывайте GetBuildFailures только в конце сборки; вызов в другой момент даст неопределенные результаты.

Более полный пример, показывающий как превратить каждый элемент результата GetBuildFailures в строку:

# Сделаем ошибку при сборке, если передать fail=1 в командной строке
if ARGUMENTS.get('fail', 0):
    Command('target', 'source', ['/bin/false'])
 
def bf_to_str(bf):
    """Удобным способом преобразование элемента GetBuildFailures() в строку."""
    import SCons.Errors
    if bf is None: # неизвестные target-ы дают в списке None
        return '(unknown tgt)'
    elif isinstance(bf, SCons.Errors.StopError):
        return str(bf)
    elif bf.node:
        return str(bf.node) + ': ' + bf.errstr
    elif bf.filename:
        return bf.filename + ': ' + bf.errstr
    return 'unknown failure: ' + bf.errstr
import atexit
 
def build_status():
    """Преобразование статуса блоки в 2-кортеж, (status, msg)."""
    from SCons.Script import GetBuildFailures
    bf = GetBuildFailures()
    if bf:
        # bf обычно это список отказов сборки; если элемент None,
        # то причина в том, что это target, о которой scons ничего не знает.
        status = 'failed'
        failures_message = "\n".join(["Failed building %s" % bf_to_str(x)
                           for x in bf if x is not None])
    else:
        # Если bf == None, то сборка завершилась успешно.
        status = 'ok'
        failures_message = ''
    return (status, failures_message)
 
def display_build_status():
    """Отображение статуса сборки. Вызывается из atexit.
    Здесь вы должны сделать все сложные вещи."""
    status, failures_message = build_status()
    if status == 'failed':
       print("FAILED!!!!")  # предупреждение на экране, звуковой сигнал, и т. п.
    elif status == 'ok':
       print("Build succeeded.")
    print(failures_message)
 
atexit.register(display_build_status)

При запуске это приведет к соответствующему выводу:

% scons -Q
scons: `.' is up to date.
Build succeeded.
% scons -Q fail=1
scons: *** [target] Source `source' not found, needed by target `target'.
FAILED!!!!
Failed building target: Source `source' not found, needed by target `target'.

[Управление сборкой из командной строки]

SCons предоставляет несколько способов как для автора файлов SConscript, чтобы дать вам (и вашим пользователям) возможность управлять выполнением сборки. Аргументы, которые можно указать в командной строке, делятся на 3 типа:

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

Переменные. Любой аргумент, содержащий = (знак равенства) считается установкой переменной в форме переменная=значение. SCons предоставляет прямой доступ ко всем установкам переменных командной строки, возможность применить установки переменных командной строки к construction-окружениям, и функции для конфигурирования специальных типов переменных (значения Boolean, имена путей, и т. д.) с автоматической проверкой корректности указанных значений. См. далее секцию "Переменные сборки: variable=value".

Target. Любой аргумент командной строки, который не опция и не установка переменной (т. е. не начинается на дефис и не содержит в себе символа =) считается целью сборки (target) которую вы указываете для сборки. SCons предоставляет список для доступа к указанным целям, как и способы установить список целей по умолчанию из файлов SConscript. См. главу "Цели сборки, указанные в командной строке" [].

[Ссылки]

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

 

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


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

Top of Page