× К оглавлению На главную Об авторе

   Дата и время публикации:

Переменные, функции, события и встраиваемые выражения

1. Объявление переменных

Код процедуры получения исходного кода пакета из удаленного репозитария приводится в функции base_do_fetch(), написанной в стили языка Python и показанной в листинге 1.1

Листинг 1.1


...
149 python base_do_fetch() {
150 
151     src_uri = (d.getVar('SRC_URI') or "").split()
152     if len(src_uri) == 0:
153        return
154
155    try:
156        fetcher = bb.fetch2.Fetch(src_uri, d)
157        fetcher.download()
158    except bb.fetch2.BBFetchException as e:
159        bb.fatal(str(e))
160 }
...    

Как видно из строк 151, 156 и 158 Python-модули "bb" и "os" уже импортированы, поэтому их дополнительно импортировать не нужно, как в случае с Python-модулем "time", пример импорта которого показано в листинге 1.2

Листинг 1.2


...
 59 def set_buildtimedata(var, d):
 60     import time
 61     time = time.time()
...

Так же как и глобальная переменная "d", берущая свое название от вместилища или хранилища данных (datastore), всегда предоставляется автоматически, как и вместе с ней такая же глобальная переменная bb, используемая в строках 156, 158 и 159 листинга 1.1 и строке 59 листинга 1.2; при этом указанные глобальные переменные доступны всем основным типам Python-функций, поддерживаемых в BitBake, за исключением обработчиков событий.

Работы с выражениями переменных BitBake, подобных выражению ${X}, не доступно для функций, написанных в стиле языка Python. Это сделано умышленно, чтобы позволить свободно устанавливать значения переменным для расширяемых выражений без преждевременного их разложения. Поэтому для доступа к подобным переменным в функциях Python следует использовать d.getVar("X", True), а для более сложных выражений d.expand(), как показано в Листинге 1.3

Листинг 1.3


...
 88            f.write(d.expand("${PF}: %s\n" % e.task))
...

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

2. Функции Python

2.1 Виды функций

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

2.2 Анонимные функции

В Python, который распознает BitBake, к анонимным функциям относятся такие функции, которые не имеют своего имени, как показано в листинге 2.2.1

Листинг 2.2.1


...
 12 python __anonymous () {
 13         if d.getVar('SOMEVAR', True) == 'value':
 14             d.setVar('ANOTHERVAR', 'value2')
...     }

Подобные функции, показанные в листинге 2.2.1, выполняются BitBake во время лексического разбора своих файлов, имеющие расширения .inc, .bb и .bbclass, в ходе установки значения переменным или для выполнения других программных операций, например как показано в листинге 2.2.2

Листинг 2.2.2 . Фрагмент файла update-rc.d.bbclass слоя meta

0

...
 68 def update_rc_after_parse(d):
 69   if d.getVar('INITSCRIPT_PACKAGES', False) == None:
 70       if d.getVar('INITSCRIPT_NAME', False) == None:
 71           bb.fatal("%s inherits update-rc.d but doesn't set INITSCRIPT_NAME" % d.getVar('FILE', False))
 72   if d.getVar('INITSCRIPT_PARAMS', False) == None:
 73           bb.fatal("%s inherits update-rc.d but doesn't set INITSCRIPT_PARAMS" % d.getVar('FILE', False))
 74
 75 python __anonymous() {
 76    update_rc_after_parse(d)
 77 }   
...

Как показано в листинге 2.2.2, во время лексического разбора, анонимная функция запускает проверку определения переменных INITSCRIPT_PACKAGES, INITSCRIPT_NAME и INITSCRIPT_PARAMS, которую выполняет функция update_rc_after_parse(), запущенная анонимной функцией.

При этом, как видно из листинга 2.2.3, дополнительный квалификатор "__anonymous" является необязательным, потому что имеет функциональный эквивалент.

Листинг 2.2.3


...
 22   python () {
 23         if d.getVar('SOMEVAR', True) == 'value':
 24             d.setVar('ANOTHERVAR', 'value2')
 25     }
...

При этом, как показано в листинге 2.2.3, переменную BitBake хранилища данных "d" необязательно объявлять, т.к. она уже определена на момент начала лексического разбора для всего рецепта. Соответственно, здесь же можно устанавливать значения переменным, значения которых будут подхвачены другими функциями.

2.3 Лямбда функции

В то время как нормальные функции, объявляются с использованием ключевого слова def, анонимные могут распознаваться в BitBake по ключевому слову lambda, которое берет свое название от символа греческого алфавита "лямбда".

Соответственно, от указанного ключевого слова пошло название лямбда функций, которые используются для объявления безымянных функций с коротким периодом жизни, используемые в Python в качестве аргументов функций более высокого порядка, т.е. функции, имеющие аргументы и принимающие другие функции как аргументы. При этом Лямбда функции могут совместно использоваться совместно с встраиваемыми функциями Python, такими как filter(), map() и т.п., подобный пример использования приводится в листинге 2.3.1

Листинг 2.3.1. Фрагмент package_rpm.bbclass слоя meta.


...
 27 def filter_nativesdk_deps(srcname, var):
 28    if var and srcname.startswith("nativesdk-"):
 29        var = filter_deps(var, lambda dep: not dep.startswith('/') and dep != 'perl' and not dep.startswith('perl('))
 30    return var 
...

В сроке 29, выделена жирным, производится фильтрация текстовой строки dep, которая содержит абсолютный путь, начинающийся на символ (/), и не содержащая никаких искомых фраз "perl" или "perl)" .

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

В свою очередь ключевые функции высшего порядка могут быть:

В примере, приводимым в листинге 2.3.2 взятым из файла package_ipk.bbclass из слоя meta, показано использование встраиваемой функции sorted() .

Листинге 2.3.2. Фрагмент файла package_ipk.bbclass из слоя meta.


...
 43 def ipk_write_pkg(pkg, d):
...
 47      import collections
...
186      rprovides = dict.fromkeys(bb.utils.explode_dep_versions2(localdata.getVar("RPROVIDES") or ""), [])
187      rprovides = collections.OrderedDict(sorted(rprovides.items(), key=lambda x: x[0]))
188      debian_cmp_remap(rprovides)
...

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

2.4 Нормальные функций

Функции написаны в стиле Python и встраиваемые в дальнейшем другой код, написанном в стиле Python. Ниже, в листинге 2.4.1 приводится пример такой функции.

Листинге 2.4.1

...
 45def get_depends(var,d):
 46         if d.getVar('SOMECONDITION', True):
 47             return "dependencywithcond"
 48         else:
 49             return "dependency"
...
 50SOMECONDITION = "1"
 51DEPENDS = "${@get_depends('SOMECONDITION',d)}"
...

Как видно из листинга 2.4.1, в строке 45 get_depends() является функцией высшего порядка, т.к. она принимает два аргумента – текстовой имя Bitbake переменной и знакомую нам глобальную Python переменную "d" . При этом указанная функция используется для получения некоторых зависимостей и их условий "dependencywithcond", которые вернет в строке 47, если Bitbake переменная "SOMECONDITION" будет иметь значение,"1", как показано в строке 50, или вернет только зависимости "dependency", как показано в строке 49, в случае обратного значения указанной Bitbake переменная.

При этом нужно помнить, что переменная хранилища данных "d" не является глобально объявляемой в недрах BitBake, а живет только в функции, написанной в стиле Python. Поэтому её нужно локально объявлять в параметрах рассматриваемой функции, как показано в строке 45 листинга 2.4.1, а так же при декларации самой функции get_depends(), в начале той же строки, использовать встроенное в Bitbake расширение Python.

2.5 Встраиваемое в BitBake расширение Python

Как было уже ранее показано в листинге 2.4.1, нечто подобное использовалось в функции высшего порядка get_depends() для автоматического присвоения текстового значения переменной DEPENDS, назначаемой окружением BitBake для собираемого пакета .

Точно также можно произвести присвоение текущей даты значению BitBake переменной DATE, как показано в листинге 2.5.1

Листинг 2.5.1

 
...
 62  DATE = "${@time.strftime('%Y-%m-%d',time.gmtime())}"
...

В результате выполнения встраиваемого Python выражения "${@}" переменной DEPENDS окружения BitBake будет присвоено значение текущей даты.

2.6 Cобытия

BitBake позволяет включать (объявлять) обработчики событий в файлах рецептов и их классов. События срабатывают в определенных точках выполнения операции, таких как её начало по отношению к данному файла-рецепта сборки пакета с расширением .bb, запуск задачи, завершение задачи с отказом, нормальное завершение задачи и так далее. С целью сделать легким нечто такое, подобное формированию и отправки извещения по электронной почте, сформированного и отправленного в результате выявленных отказов во время сборки.

Ниже приводится пример объявления обработчика события, в дампе 2.6.1

Дамп 2.6.1

 
...
 75addhandler myclass_eventhandler
...
 85python myclass_eventhandler() {
 86       from bb.event import getName
 87       from bb import data
 88       print("The name of the Event is %s" % getName(e))
 89         print("The file we run for is %s" % data.getVar('FILE', e.data, True))
 89 }
...

Как показано в дампе 2.6.1, обработчик событий myclass_eventhandler() выводит на консоль имя обрабатываемого события и содержимое переменной FILE окружения BitBake каждый раз, когда событие случается. При этом используется уже определенная глобальная переменная "e" и "e.data", которую содержит экземпляр "bb.data", как показано в строках 86-89, с помощью метода getName(e) становится возможным получить имя сработанного события.

Во время выполнения стандартной сборки следующие общие события могут случаться:

А также могут случать события при выполнении запросов к серверу:

2.7 Основные операции с переменной хранилища данных

Как уже упоминалось выше доступ к переменным окружения BitBake, которые находятся в общем хранилище данных доступно функциям Python. Для чего в BitBake определены следующие функции:

 

3. Библиография

3.1 BitBake user manual. The Variable SRC_URI

3.2 BitBake user manual. The bb fetchers

3.3 Yocto Project Quick Start (Mega manual)

3.4 BitBake user manual. The Variable DL_DIR

3.5 BitBake user manual. The Variable FILESPATH

3.6 How to Use Python Lambda Functions

3.7 [yocto] Default variables in a python task/function

3.8 Stackoverflow.Com.Python: array and print() format

3.9 BitBake user manual. Anonymous Python Functions

3.10 Absolute vs Relative Imports in Python