TipsAndTricks/Tinfoil: Difference between revisions

From Yocto Project
Jump to navigationJump to search
No edit summary
No edit summary
Line 6: Line 6:
= Writing simple utility scripts using Tinfoil =
= Writing simple utility scripts using Tinfoil =


The tinfoil API, which was introduced to BitBake a number of years ago and underwent a major rework in the 2.3 (pyro) release, provides a way for scripts to interact with the metadata and call into BitBake/OpenEmbedded code. Some of the scripts that come with BitBake / OpenEmbedded make heavy use of it - <code>bitbake-layers</code>, <code>devtool</code>, <code>recipetool</code> and <code>oe-pkgdata-util</code> are examples.
The tinfoil API, which was introduced to BitBake a number of years ago and underwent a major rework in the 2.3 (pyro) release, provides a way for scripts to interact with the metadata and call into BitBake/OpenEmbedded code. It's intended to be an easier-to-use wrapper around BitBake's internal code, hence the name. Some of the scripts that come with BitBake / OpenEmbedded make heavy use of it - <code>bitbake-layers</code>, <code>devtool</code>, <code>recipetool</code> and <code>oe-pkgdata-util</code> are examples.


== Getting a variable value ==
== Getting a variable value ==
Line 33: Line 33:
</pre>
</pre>


If you are querying multiple variable values this is much more efficient than calling out to "bitbake -e" and parsing its output.
If you are querying multiple variable values this is much more efficient than calling out to "bitbake -e" and parsing its output, which was the old way of getting this kind of information.


== Parsing recipes ==
== Parsing recipes ==
Line 57: Line 57:
     tinfoil.prepare(config_only=False)
     tinfoil.prepare(config_only=False)


    # Parse the gzip recipe
     rd = tinfoil.parse_recipe('gzip')
     rd = tinfoil.parse_recipe('gzip')
     print('gzip SRC_URI = "%s"' % rd.getVar('SRC_URI'))
     print('gzip SRC_URI = "%s"' % rd.getVar('SRC_URI'))


    # Parse the kernel recipe (whichever is selected in your configuration)
     rd = tinfoil.parse_recipe('virtual/kernel')
     rd = tinfoil.parse_recipe('virtual/kernel')
     print('Kernel recipe is %s' % rd.getVar('PN'))
     print('Kernel recipe is %s' % rd.getVar('PN'))
    # Check if the kernel recipe inherits the kernel class - we'd expect True
    print('Kernel recipe inherits kernel class: %s' % bb.data.inherits_class('kernel', rd))
    # Check if the kernel recipe inherits the cmake class - we'd expect False
    print('Kernel recipe inherits cmake class: %s' % bb.utils.inherits_class('cmake', rd))
</pre>
</pre>


Notice that in this second example we pass <code>config_only=False</code> - this is so tinfoil will load the data for all recipes enabled in your configuration, just as bitbake does every time you run a normal build. You don't have to specify <code>config_only=False</code> to parse a recipe file if you know the path to it, but it is required if you want to specify it simply by name and get the preferred version / provider.
Notice that in this second example we pass <code>config_only=False</code> - this tells tinfoil to load the data for all recipes enabled in your configuration, just as bitbake does every time you run a normal build. You don't have to specify <code>config_only=False</code> to parse a recipe file if you know the path to it, but it is required if you want to specify it simply by name and get the preferred version / provider.


The <code>parse_recipe()</code> function will accept recipe names, or providers such as <code>virtual/kernel</code> in the second call above. It returns a datastore (usually seen as <code>d</code> within recipes and other python code within OE).
The <code>parse_recipe()</code> function will accept recipe names, or providers such as <code>virtual/kernel</code> in the second call above. It returns a datastore (usually seen as <code>d</code> within recipes and other python code within OE) which you can then use to get variable values, check inheritance etc.
 
== Enumerating available recipes ==
 
 
<pre>
#!/usr/bin/env python3
 
import os
import sys
 
# Set up sys.path to let us import tinfoil
scripts_path = os.path.dirname(os.path.realpath(__file__))
lib_path = scripts_path + '/lib'
sys.path.insert(0, lib_path)
import scriptpath
scriptpath.add_bitbake_lib_path()
 
import bb.tinfoil
 
with bb.tinfoil.Tinfoil() as tinfoil:
    tinfoil.prepare(config_only=False)
 
    for pn in tinfoil.cooker.recipecaches[""].pkg_pn:
        print(pn)
</pre>
 
A few notes about <code>pkg_pn</code>:
* It's actually a dictionary and so the keys aren't sorted alphabetically
* It contains every target, not just every recipe file - i.e. there will be variants created through <code>BBCLASSEXTEND</code> in there as well.
* There's a corresponding <code>pkg_fn</code> dict that maps files to <code>PN</code> values, but bear in mind this also contains <code>virtual:...</code> entries, again created through <code>BBCLASSEXTEND</code>.
* This doesn't yet work with multiconfig - just the main configuration
 
== Other useful functions ==
 
The bitbake/lib/bb/utils.py, meta/lib/oe/utils.py, and meta/lib/oe/recipeutils.py files all contain useful functions that will help you perform various tasks that might be interesting from a script that uses tinfoil.

Revision as of 02:46, 1 June 2017

NOTE: This is currently a draft, needs additional content & editing - PaulEggleton (talk) 18:22, 30 May 2017 (PDT)

Writing simple utility scripts using Tinfoil

The tinfoil API, which was introduced to BitBake a number of years ago and underwent a major rework in the 2.3 (pyro) release, provides a way for scripts to interact with the metadata and call into BitBake/OpenEmbedded code. It's intended to be an easier-to-use wrapper around BitBake's internal code, hence the name. Some of the scripts that come with BitBake / OpenEmbedded make heavy use of it - bitbake-layers, devtool, recipetool and oe-pkgdata-util are examples.

Getting a variable value

Let's take a look at a simple example which reads a configuration-level variable value - TMPDIR to be specific. This script needs to be placed in the scripts subdirectory to work as-is, although you can modify the import code to make it work from anywhere:

#!/usr/bin/env python3

import os
import sys

# Set up sys.path to let us import tinfoil
scripts_path = os.path.dirname(os.path.realpath(__file__))
lib_path = scripts_path + '/lib'
sys.path.insert(0, lib_path)
import scriptpath
scriptpath.add_bitbake_lib_path()

import bb.tinfoil

with bb.tinfoil.Tinfoil() as tinfoil:
    tinfoil.prepare(config_only=True)
    tmpdir = tinfoil.config_data.getVar('TMPDIR')
    print('TMPDIR is "%s"' % tmpdir)

If you are querying multiple variable values this is much more efficient than calling out to "bitbake -e" and parsing its output, which was the old way of getting this kind of information.

Parsing recipes

Here's another example where we parse a couple of recipes:

#!/usr/bin/env python3

import os
import sys

# Set up sys.path to let us import tinfoil
scripts_path = os.path.dirname(os.path.realpath(__file__))
lib_path = scripts_path + '/lib'
sys.path.insert(0, lib_path)
import scriptpath
scriptpath.add_bitbake_lib_path()

import bb.tinfoil

with bb.tinfoil.Tinfoil() as tinfoil:
    tinfoil.prepare(config_only=False)

    # Parse the gzip recipe
    rd = tinfoil.parse_recipe('gzip')
    print('gzip SRC_URI = "%s"' % rd.getVar('SRC_URI'))

    # Parse the kernel recipe (whichever is selected in your configuration)
    rd = tinfoil.parse_recipe('virtual/kernel')
    print('Kernel recipe is %s' % rd.getVar('PN'))
    # Check if the kernel recipe inherits the kernel class - we'd expect True
    print('Kernel recipe inherits kernel class: %s' % bb.data.inherits_class('kernel', rd))
    # Check if the kernel recipe inherits the cmake class - we'd expect False
    print('Kernel recipe inherits cmake class: %s' % bb.utils.inherits_class('cmake', rd))

Notice that in this second example we pass config_only=False - this tells tinfoil to load the data for all recipes enabled in your configuration, just as bitbake does every time you run a normal build. You don't have to specify config_only=False to parse a recipe file if you know the path to it, but it is required if you want to specify it simply by name and get the preferred version / provider.

The parse_recipe() function will accept recipe names, or providers such as virtual/kernel in the second call above. It returns a datastore (usually seen as d within recipes and other python code within OE) which you can then use to get variable values, check inheritance etc.

Enumerating available recipes

#!/usr/bin/env python3

import os
import sys

# Set up sys.path to let us import tinfoil
scripts_path = os.path.dirname(os.path.realpath(__file__))
lib_path = scripts_path + '/lib'
sys.path.insert(0, lib_path)
import scriptpath
scriptpath.add_bitbake_lib_path()

import bb.tinfoil

with bb.tinfoil.Tinfoil() as tinfoil:
    tinfoil.prepare(config_only=False)

    for pn in tinfoil.cooker.recipecaches[""].pkg_pn:
        print(pn)

A few notes about pkg_pn:

  • It's actually a dictionary and so the keys aren't sorted alphabetically
  • It contains every target, not just every recipe file - i.e. there will be variants created through BBCLASSEXTEND in there as well.
  • There's a corresponding pkg_fn dict that maps files to PN values, but bear in mind this also contains virtual:... entries, again created through BBCLASSEXTEND.
  • This doesn't yet work with multiconfig - just the main configuration

Other useful functions

The bitbake/lib/bb/utils.py, meta/lib/oe/utils.py, and meta/lib/oe/recipeutils.py files all contain useful functions that will help you perform various tasks that might be interesting from a script that uses tinfoil.