All of lore.kernel.org
 help / color / mirror / Atom feed
From: Trevor Woerner <twoerner@gmail.com>
To: docs@lists.yoctoproject.org
Cc: Trevor Woerner <trevor.woerner@amd.com>
Subject: [RFC PATCH 1/2] add a "bitbake" pygments lexer
Date: Tue, 21 Apr 2026 13:24:15 -0400	[thread overview]
Message-ID: <20260421172416.1801567-2-twoerner@gmail.com> (raw)
In-Reply-To: <20260421172416.1801567-1-twoerner@gmail.com>

From: Trevor Woerner <trevor.woerner@amd.com>

Highlighting renders correctly across the BitBake constructs from
the upstream vim syntax:
  comments
  all assignment operators (=, ?=, +=, =+, :=, ??=, .=, =.)
  ${VAR} and ${@...} interpolation
  OE override chains (FILES:${PN}-doc, :append:class-target)
  varflags (do_install[depends])
  inherit/include/require
  addtask ... after ...  before ...
  EXPORT_FUNCTIONS
  shell function bodies (delegated to BashLexer)
  python/fakeroot python task bodies and top-level def blocks (delegated
    to PythonLexer).

Changes

  documentation/sphinx/bitbake.py is a new local Sphinx extension that
    defines BitbakeLexer (a Pygments RegexLexer) and registers it with
    Sphinx via sphinx.highlighting.lexers under the aliases bitbake and bb.
    Token rules mirror contrib/vim/syntax/bitbake.vim from the bitbake repo,
    and inner shell/python regions are dispatched to the existing BashLexer
    / PythonLexer using Pygments' using() helper.

  documentation/conf.py appends 'bitbake' to extensions so the lexer is
    loaded on every build.

Usage

  In any .rst file you can now write:

  .. code-block:: bitbake

     SUMMARY = "Hello"
     inherit autotools
     do_install:append() {
         install -d ${D}${bindir}
     }

  bb works as an alias if shorter is preferred, and the same applies to
  .. highlight:: bitbake.

AI-Generated: codex/claude-opus 4.7 (xhigh)
Signed-off-by: Trevor Woerner <trevor.woerner@amd.com>
---
 documentation/conf.py           |   3 +-
 documentation/sphinx/bitbake.py | 195 ++++++++++++++++++++++++++++++++
 2 files changed, 197 insertions(+), 1 deletion(-)
 create mode 100644 documentation/sphinx/bitbake.py

diff --git a/documentation/conf.py b/documentation/conf.py
index e5e3a8a89abd..aa2ec8d45ed3 100644
--- a/documentation/conf.py
+++ b/documentation/conf.py
@@ -68,7 +68,8 @@ extensions = [
     'sphinx.ext.intersphinx',
     'sphinx_copybutton',
     'sphinxcontrib.rsvgconverter',
-    'yocto-vars'
+    'yocto-vars',
+    'bitbake',
 ]
 autosectionlabel_prefix_document = True
 
diff --git a/documentation/sphinx/bitbake.py b/documentation/sphinx/bitbake.py
new file mode 100644
index 000000000000..c521340c391f
--- /dev/null
+++ b/documentation/sphinx/bitbake.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+#
+# SPDX-License-Identifier: CC-BY-SA-2.0-UK
+#
+# Pygments lexer for BitBake metadata files (``.bb``, ``.bbclass``,
+# ``.bbappend``, ``.inc``, ``.conf``) and inline ``code-block:: bitbake``
+# snippets used throughout the Yocto Project documentation.
+#
+# Pygments does not ship a BitBake lexer, so this Sphinx extension provides
+# one. The token rules below mirror the upstream BitBake vim syntax shipped
+# in ``contrib/vim/syntax/bitbake.vim`` of the bitbake repository:
+# https://git.openembedded.org/bitbake/tree/contrib/vim/syntax/bitbake.vim
+#
+# Highlights:
+#   - comments, line continuations, varflags
+#   - ``${VAR}`` and ``${@python_expr}`` interpolations
+#   - assignment operators (``=``, ``:=``, ``+=``, ``=+``, ``.=``, ``=.``,
+#     ``?=``, ``??=``) with optional ``export`` prefix and OE override
+#     suffixes (``VAR:append``, ``FILES:${PN}-doc``...)
+#   - ``inherit``, ``include``, ``require`` directives
+#   - ``addtask`` / ``deltask`` / ``addhandler`` / ``EXPORT_FUNCTIONS``
+#     statements (with the ``after`` / ``before`` keywords)
+#   - shell task bodies (``do_install() { ... }``) delegated to ``BashLexer``
+#   - ``python``, ``fakeroot python``, anonymous ``python() { ... }`` task
+#     bodies and top-level ``def`` blocks delegated to ``PythonLexer``
+
+import re
+
+from pygments.lexer import RegexLexer, bygroups, include, using, words
+from pygments.lexers.python import PythonLexer
+from pygments.lexers.shell import BashLexer
+from pygments.token import (
+    Comment,
+    Keyword,
+    Name,
+    Operator,
+    Punctuation,
+    String,
+    Text,
+    Whitespace,
+)
+
+__version__ = '1.0'
+
+# A bare BitBake identifier (variable, function or flag name). Allows the
+# characters used by OE-Core variable names (digits, ``-``, ``.``, ``+``).
+_IDENT = r'[A-Za-z_][A-Za-z0-9_\-.+]*'
+
+# Optional OE override chain such as ``:append``, ``:remove``, ``:class-target``
+# or ``:${PN}-doc``. Anchored so it only consumes ``:foo`` runs and never
+# eats the leading ``:`` of the ``:=`` assignment operator.
+_OVERRIDE = r'(?::[A-Za-z0-9_\-.+${}]+)*'
+
+# All BitBake variable assignment operators, ordered so that the longer
+# operators win the regex alternation.
+_ASSIGN = r'(?:\?\?=|\?=|:=|\+=|=\+|\.=|=\.|=)'
+
+
+class BitbakeLexer(RegexLexer):
+    """Lexer for BitBake recipes, classes, includes and configuration."""
+
+    name = 'BitBake'
+    aliases = ['bitbake', 'bb']
+    filenames = ['*.bb', '*.bbclass', '*.bbappend', '*.inc', '*.conf']
+    mimetypes = ['text/x-bitbake']
+
+    flags = re.MULTILINE
+
+    tokens = {
+        'root': [
+            (r'[ \t]+', Whitespace),
+            (r'\n', Whitespace),
+            (r'#.*$', Comment.Single),
+
+            # ``python [name]() { ... }`` blocks (also ``fakeroot python``).
+            # Must be tried before the generic shell function rule so the
+            # ``python`` keyword is not mistaken for a shell function name.
+            (r'(^(?:fakeroot[ \t]+)?)(python)((?:[ \t]+' + _IDENT + r')?)'
+             r'([ \t]*\([ \t]*\)[ \t]*)(\{[ \t]*\n)'
+             r'((?:.*\n)*?)'
+             r'(^\}[ \t]*$)',
+             bygroups(Keyword.Type, Keyword, Name.Function, Text,
+                      Punctuation, using(PythonLexer), Punctuation)),
+
+            # Shell task bodies: ``[fakeroot ]name[:override]() { ... }``.
+            (r'(^(?:fakeroot[ \t]+)?)(' + _IDENT + r')(' + _OVERRIDE + r')'
+             r'([ \t]*\([ \t]*\)[ \t]*)(\{[ \t]*\n)'
+             r'((?:.*\n)*?)'
+             r'(^\}[ \t]*$)',
+             bygroups(Keyword.Type, Name.Function, Name.Decorator, Text,
+                      Punctuation, using(BashLexer), Punctuation)),
+
+            # Top-level python ``def`` blocks; the body is any run of
+            # indented or blank lines following the signature.
+            (r'^def[ \t]+' + _IDENT + r'[ \t]*\([^)]*\)[ \t]*:[ \t]*\n'
+             r'(?:[ \t]+.*\n|\n)+',
+             using(PythonLexer)),
+
+            # ``inherit`` / ``include`` / ``require`` directives.
+            (r'^(inherit|include|require)\b',
+             Keyword.Namespace, 'include-line'),
+
+            # ``addtask`` / ``deltask`` / ``addhandler`` / ``EXPORT_FUNCTIONS``.
+            (r'^(addtask|deltask|addhandler|EXPORT_FUNCTIONS)\b',
+             Keyword, 'statement'),
+
+            # ``VAR[flag] = "value"`` (varflag assignment).
+            (r'^(' + _IDENT + r')(\[)(' + _IDENT + r')(\])([ \t]*)('
+             + _ASSIGN + r')',
+             bygroups(Name.Variable, Punctuation, Name.Attribute,
+                      Punctuation, Whitespace, Operator),
+             'value'),
+
+            # ``[export ]VAR[:override...] OP "value"`` assignments.
+            (r'^(export[ \t]+)?(' + _IDENT + r')(' + _OVERRIDE + r')'
+             r'([ \t]*)(' + _ASSIGN + r')',
+             bygroups(Keyword.Type, Name.Variable, Name.Decorator,
+                      Whitespace, Operator),
+             'value'),
+
+            # Anything else: fall through one character at a time.
+            (r'.', Text),
+        ],
+
+        'include-line': [
+            (r'[ \t]+', Whitespace),
+            (r'\\\n', Text),
+            (r'\n', Whitespace, '#pop'),
+            include('interp'),
+            (r'[^\s$]+', String),
+        ],
+
+        'statement': [
+            (r'[ \t]+', Whitespace),
+            (r'\\\n', Text),
+            (r'\n', Whitespace, '#pop'),
+            (words(('after', 'before'), suffix=r'\b'), Keyword),
+            include('interp'),
+            (r'[^\s$\\]+', Name),
+        ],
+
+        'value': [
+            (r'[ \t]+', Whitespace),
+            (r'\\\n', String.Escape),
+            (r'\n', Whitespace, '#pop'),
+            (r'"', String.Double, 'string-double'),
+            (r"'", String.Single, 'string-single'),
+            include('interp'),
+            (r'[^\s"\'$\\]+', String),
+        ],
+
+        'string-double': [
+            (r'\\\n', String.Escape),
+            (r'\\.', String.Escape),
+            (r'"', String.Double, '#pop'),
+            include('interp'),
+            (r'[^"\\$]+', String.Double),
+        ],
+
+        'string-single': [
+            (r'\\\n', String.Escape),
+            (r'\\.', String.Escape),
+            (r"'", String.Single, '#pop'),
+            include('interp'),
+            (r"[^'\\$]+", String.Single),
+        ],
+
+        'interp': [
+            # ``${@ python expression }`` evaluated by BitBake at parse time.
+            (r'\$\{@', String.Interpol, 'py-interp'),
+            # ``${VAR}`` variable expansion.
+            (r'(\$\{)([A-Za-z0-9_\-:.+/]+)(\})',
+             bygroups(String.Interpol, Name.Variable, String.Interpol)),
+        ],
+
+        'py-interp': [
+            (r'\}', String.Interpol, '#pop'),
+            (r'[^}]+', using(PythonLexer)),
+        ],
+    }
+
+
+def setup(app):
+    """Register the BitBake lexer with Sphinx/Pygments."""
+    from sphinx.highlighting import lexers
+
+    bb_lexer = BitbakeLexer()
+    lexers['bitbake'] = bb_lexer
+    lexers['bb'] = bb_lexer
+
+    return {
+        'version': __version__,
+        'parallel_read_safe': True,
+        'parallel_write_safe': True,
+    }
-- 
2.50.0.173.g8b6f19ccfc3a



  reply	other threads:[~2026-04-21 17:24 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-21 17:24 [RFC PATCH 0/2] support bitbake blocks Trevor Woerner
2026-04-21 17:24 ` Trevor Woerner [this message]
2026-04-22  6:55   ` [docs] [RFC PATCH 1/2] add a "bitbake" pygments lexer Antonin Godard
2026-04-22 17:15     ` Trevor Woerner
2026-04-22 19:33       ` Trevor Woerner
2026-04-23  7:39         ` Antonin Godard
2026-04-21 17:24 ` [RFC PATCH 2/2] BSP dev guide: update to use bitbake lexer Trevor Woerner
2026-04-21 19:36 ` [RFC PATCH 0/2] support bitbake blocks Trevor Woerner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260421172416.1801567-2-twoerner@gmail.com \
    --to=twoerner@gmail.com \
    --cc=docs@lists.yoctoproject.org \
    --cc=trevor.woerner@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.