-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC — DtSh, shell-like interface with Devicetree #59863
base: main
Are you sure you want to change the base?
Conversation
7057216
to
66b9925
Compare
7f86207
to
b170331
Compare
I think VS Code extension: nRF DeviceTree is a good graphical tool. But it's not open source. Not particularly friendly to non-nRF devices |
b1c9d74
to
b5d6d2c
Compare
Thanks for this. I think it's a great idea and I especially appreciate the attention to detail you put into the RFC as well as volunteering maintenance. Consider me onboard with this idea. I'll give it some testing and review. |
Some unboxing comments:
I can't get tab completion working as I expect using the homebrew python 3.11. From the prompt:
I press TAB and expect commands to be completed. But instead I see a tab character is inserted. Is that expected? Here is my python info (using a virtual environment):
I can The command parser also doesn't seem to be stripping leading whitespace:
this isn't like what you'd expect from a shell in my opinion -- I would expect both of those to quit. |
Rationale: - provide DTSh client code with a single entry-point API for GNU Readline integration - isolate the completion logic (find matches) and view providers (display matches) from the readline hooks machinery The GNU Readline integration with Python is initialized: - with the standalone Python module gnureadline if installed: this is likely necessary on macOS, where readline is typically backed by libedit instead of libreadline - with the readline module of the Python standard library on other POSIX systems On windows, the readline support will likely be disabled. Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Shell-like interface with a devicetree model: - parse command lines into commands, arguments and parameters - walk the devicetree through a hierarchical file-system metaphor (current working branch, relative paths and path references, Devicetree labels) - match nodes with flexible criteria - sort and filter nodes Unit tests and examples: tests/test_dtsh_shell.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Auto-completion logic and base display callbacks for GNU Readline integration. Unit tests and examples: tests/test_dtsh_autocomp.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Flags, arguments and parameters for DTSh commands. Unit tests and examples: tests/test_dtsh_shellutils.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Devicetree shell theme (aka rich styles). A theme is a collection of styles, each of which is consistently used to represent a type of information: e.g. by default compatible strings are always green, and things that behave like symbolic links (e.g. aliases) are all in italic. Theme files are simple INI files: - the bundled theme file which sets the default appearance - an optional user's theme file which customizes the defaults Unit tests and examples: tests/test_dtsh_theme.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Base view definitions compatible with the rich console protocol, __rich__(). Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Factories for rich text views, and miscellaneous text related helpers. Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Stateless factories of base Devicetree model elements. Context-aware views of DT nodes. Signed-off-by: Christophe Dufaza <chris@openmarl.org>
SVG contents format for commands output redirection. Rationale: - the SVG documents we can generate directly with the Textualize's rich library API do not really suit the DTSh use case - we need an additional abstraction layer to properly support the "append" mode Signed-off-by: Christophe Dufaza <chris@openmarl.org>
- base rich VT (colors and styles) - rich VT with batch commands support - rich redirection streams for SVG and HTML Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Rich display callback for GNU readline integration Style candidates based on completion context. Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Command options to configure formatted outputs. Base command with boilerplate code to support formatted output. Unit tests and examples: tests/test_dtsh_rich_shellutils.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Unit tests and examples: tests/test_dtsh_builtin_pwd.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Unit tests and examples: tests/test_dtsh_builtin_cd.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Unit tests and examples: tests/test_dtsh_builtin_ls.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Unit tests and examples: tests/test_dtsh_builtin_tree.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Search for nodes with pre-defined criteria. Unit tests and examples: tests/test_dtsh_builtin_find.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Unit tests and examples: tests/test_dtsh_builtin_alias.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Unit tests and examples: tests/test_dtsh_builtin_chosen.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Concat and output info about a node and its properties. Unit tests and examples: tests/test_dtsh_builtin_cat.py Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Print system information (kernel, board, SoC). Signed-off-by: Christophe Dufaza <chris@openmarl.org>
A session binds a devicetree shell and I/O streams to: - execute batch commands - and/or run an interactive loop Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Extend the base session with a few rich TUI elements and support for SVG and HTML command output redirection formats. Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Devicetree shell CLI. Rationale: - factorize command line parser initialization (options and arguments definitions) - factorize the boilerplate code needed to bootstrap a DTSh session (CLI or batch, loading additional theme and preferences files, etc) - provide an API suitable for both standalone installations (raw CLI) or integration as a West extension The API then defines two distinct simple usages: - a standalone mode (raw CLI entry point): DTShCli is then responsible for initializing a default CLI parser, and actually parsing the command line arguments when entering DTShCli.run() - a passive mode: some external code (e.g. a WestCommand) is responsible for configuring the parser it manages with DTShArgvParser.init(), and actually parsing the command line arguments needed to call DTShCli.run() Signed-off-by: Christophe Dufaza <chris@openmarl.org>
- West command: class DTShell in scripts/west_commands/dtshell.py - dtsh extension to West in west-commands.yml. - support for completion west-completion.{bash,zsh} Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Initial unit test coverage: - core (model, shell, sessions): mostly complete - commands (builtins): basics (options, parameters) for all commands but "uname" - views (rich): actual unit testing would require some framework to compare the created views to the expected output; we at least check that all views build for all nodes in the test model Signed-off-by: Christophe Dufaza <chris@openmarl.org>
Document "west dtsh" in "Additional Zephyr extension commands". Document DTSh itself in the Handbook. Signed-off-by: Christophe Dufaza <chris@openmarl.org>
@MaureenHelm , @decsny , thanks again for taking some time with this. This encouraged me to update the PR (if
Backporting changes from the DTSh project to the PR should be easier now, so that we can review this, introducing changes only in response to the process, and sync contents later on, when we know where we go, or not. A few things that might help you get an idea of the current DTSh status:
I'll update the RFC description asap (minor changes). Thanks. Note: it seems that the CI check "Scancode / Scan code for licenses" is stricter (regarding shell files ?) than |
@dottspina Just stumbled upon this RFC. This is a great contribution and solves a real problem. I love how thoroughly this RFC is written and how carefully you evaluate and argue alternatives and potential issues. I'll try to help as much as I can in reviewing. Is there a way to contact you on Zephyr's Discord if I have questions, so that we can keep comment noise on this RFC focused on results rather than detailed discussions? |
RFC: DTSh: DTS file viewer with a shell-like command line interface
Introduction
Problem description
Prior to Zephyr, I've only approached Devicetree while looking through LKML or The Linux Kernel documentation.
And, quoting @mbolivar-nordic (October 2020):
I can agree that, when learning Zephyr use of Devicetree, I've personally felt the lack of a quick, simple tool to:
References:
Proposed change
The proposed change is a new West command that opens a Devicetree in a shell-like command line interface:
of commands to files
where:
west dtsh
: when invoked without any argument, the Devicetree shell will open the DTS filebuild/zephyr/zephyr.dts
, and retrieve the bindings Zephyr has used at build-time from the CMake cache filebuild/CMakeCache.txt
cd &flash_controller
changes the current working directory from the devicetree's root to the node at/soc/flash-controller@4001e000
, using its DTS labelflash_controller
find
is with no surprise a shell command that will search for devices, bindings, buses, interrupts, etc-E --also-known-as (image|storage).*
will match nodes with a label or alias starting withimage
orstorage
; predicates like--with-reg-size >4k
are also supported--format NKd
: set the node output format to the columns "nodeN
ame,", "AlsoK
nown As" (all labels and aliases), and "d
escription" (D
is the "Depends-on" column)-T
: list found nodes in tree-like format> doc/partitions.svg
to the last command line would save the partitions tree to the filedoc/partitions.svg
, in SVG formatThe considered use cases include:
This proposal started with a Proof of Concept project: source code and documentation for this prototype are still available as the main branch of the DTSh repository.
The new code base in this RFC is also distributed as a stand-alone package you can install from PyPI without messing with your Zephyr wokspaces: for details, take a look the dtsh-next branch which mirrors this PR in the DTSh repository. Note that this version can't provide West integration: just replace
west dtsh
withdtsh
.Please refer to the attached DTSh User Guide (PDF) for the complete documentation of what this RFC implements and examples of use.
Detailed RFC
This RFC includes:
Proposed change (Detailed)
This RFC wouldn't be review-able as a single commit: it is introduced as a series, where each commit adds a new feature or API, with its companion unit tests when they exist.
Once reviewed, we can squash or rearrange the whole thing according to the maintainers' preferences.
DTSh
DTSh is implemented in Python and is a Devicetree tool: it seems its natural location is
zephyr/scripts/dts/dtsh
, sibling topython-devicetree
with which it's tightly coupled.Source files are located in
dtsh/src
, anddtsh
is the root Python package.Unit tests are implemented with pytest, and located in
dtsh/tests
.The
res
sub-directory contains test resource files.Devicetree model
DTSh introduces its own model layer above
edtlib.EDT
.Rationale:
edtlib
API (python-devicetree)dtsh.dts
test_dtsh_dts
dtsh.model
test_dtsh_model
dtsh.modelutils
test_dtsh_modelutils
GNU Readline integration
Responsibilities:
from the Readline hooks machinery
dtsh.rl
dtsh.autocomp
test_dtsh_autocomp
Devicetree shell
Responsibilities:
dtsh.io
test_dtsh_io
dtsh.config
test_dtsh_config
dtsh.shell
test_dtsh_shell
dtsh.shellutils
test_dtsh_shellutils
dtsh.session
rich TUI
DTSh's rich Textual User Interface is built on Texualize's rich library.
dtsh.rich.theme
test_dtsh_theme
dtsh.rich.tui
dtsh.rich.text
dtsh.rich.modelview
dtsh.rich.svg
test_dtsh_rich_svg
dtsh.rich.io
dtsh.rich.autocomp
dtsh.rich.shellutils
test_dtsh_rich_shellutils
dtsh.rich.session
Built-in Commands
Bellow are the currently implemented commands.
dtsh.builtins.cd
cd
: change the current working branchtest_dtsh_builtin_cd
dtsh.builtins.pwd
pwd
: print path of current working branch.test_dtsh_builtin_pwd
dtsh.builtins.ls
ls
: list branch contentstest_dtsh_builtin_ls
dtsh.builtins.tree
tree
: list branch contents in tree-like formattest_dtsh_builtin_tree
dtsh.builtins.find
find
: search branches for nodestest_dtsh_builtin_find
dtsh.builtins.alias
alias
: list aliased nodestest_dtsh_builtin_alias
dtsh.builtins.chosen
chosen
: list chosen nodestest_dtsh_builtin_chosen
West extension
West command implementation:
DTShell
inzephyr/scripts/west_commands/dtshell.py
dtsh
anddevicetree
imported withos.sys.insert()
statementsAdded
dtsh
extension to West inzephyr/scripts/west-commands.yml
.Added DTSh run-time requirements to
zephyr/scripts/requirements-base.txt
:Added support for West completion to
west-completion.bash
andwest-completion.zsh
.Documentation
The proposed change:
zephyr-cmds.rst
to document the West commanddtsh.rst
) in the same directoryDependencies
DTSh should not affect other components or needs changes in other areas.
But that's not true the other way around: changes in the Zephyr's hardware model may require changes in DTSh (see bellow).
Concerns and Unresolved Questions
Maintenance
This is a valid concern:
edtlib
API: this might introduce some additional maintenance for it to consistently follow changesThe everyday maintenance should be low, though:
dtsh.model
encapsulates all uses of theedtlib
API: if breaking changes are introduced, DTSh will fail thereThe above observations come from my own experience (an example of breaking change was identifying top-level bindings with
(compat, bus)
tuples rather than just compatible strings).More significant changes, like "Hardware model v2" or "Better support for multi-core AMP SoCs", could however involve more work, to either fix DTSh or add support for the new model.
Fortunately:
Finally, I'll gladly continue myself contributing support, features and bug fixing in the long run.
Limitations
This RFC is not a complete Devicetree shell:
cat
command, with a syntax likecat soc/timer$max-bit-width
man
command, with manual pages for DTSh itself, but more importantly also for bindings and boardsI've already implemented these when experimenting with the PoC project: I know what to do, and how.
But this RFC is already sizable, and will require quite some work from its reviewers: I'd prefer we first agree on the initial design, implementation and documentation, then I'll push the missing commands with shortest individual PRs. This has been discussed and so far seems to be a consensus.
GNU Realine on Windows
Or the lack of.
For auto-completion, command history and key bindings, DTSh relies on the GNU Readline variant of the standard Python
readline
API.Basically, there's no sane and straight forward way to build and distribute the GNU Readline library on non POSIX systems: on Windows, Python simply gave up and does no longer include the
readline
module with its standard library (see e.g. There's no readline module on Windows Python (cmd.Cmd) for some historical context).The stand-alone module pyrealine3 cited in the above reference has been investigated as a work-around, but its API is incomplete, and its implementation too much coupled with
cmd.Cmd
, which we don't use. The project does not seem actively maintained.To get a cross-platform user experience, I've also considered the pure Python Prompt Toolkit:
Although DTSh may eventually work around this limitation, this is clearly not a priority at the moment.
As a consequence, the GNU Readline integration will very likely be disabled on Windows, resulting in a degraded user experience.
However, I am quite optimistic that this will not be considered a merge blocker: Zephyr seems to admit this kind of little (though very unfortunate here) difference between POSIX and non POSIX systems, e.g.
west
itself also supports auto-completion only with the Bash and Zsh shells.User Guide Integration
The DTSh User Guide (
dtsh.rst
) is quite long, and contains sections, subsections, sub-subsections, etc.Its integration with Zephyr's documentation is almost fine in HTML, and I'm not sure how to do better without messing with the
:toctree:
, which I prefer to avoid.Its integration when generating PDF is more debatable (look at the attached PDF, concerns should be obvious):
The final PDF document is still legible, but I admit it's not optimal: I will gladly try any ideas for improvement.
Correctness
DTSh use cases include educational tools for newcomers to Zephyr Devicetree: if not correct, erroneous results (commands output) will do more harm than good.
Although correctness is the focus of unit tests, and code coverage for the core modules should be almost complete, these tests involve only a small subset of the possible configurations (MCU, SoC, boards, shields, peripherals).
I think we could define a minimum level of correctness as:
Security
The proposed change is not that hazardous:
dtsh
does not evaluate (eval()
) any part of the user inputdtsh
does not pipe commands to the system interpreter (e.g.os.system()
oros.popen()
), nor create any kind of child process (e.g.subprocess.run()
)$XDG_CONFIG_HOME
or%LOCALAPPDATA%
)dtsh
won't override any existing file (including when redirecting commands output)Personal biases
Why a command line application ? Well, when you've done
west build
, you're already at the command line, and I think continuing from there withwest dtsh
, getting a different prompt but the same user interface paradigms and even key bindings (the base ones used by Zsh, GNU Bash, Emacs, GDB), is more ditrsaction-free than opening a GUI.Beyond this personal biases, things like PyGObject or Qt for Python have nonetheless been considered, but:
Alternatives
There doesn't seem to be many DTS file viewers.
Nordic Semiconductor's distributes the nRF DeviceTree extension for VS Code, which looks interesting but does not address the initial problem (as @hongshui3000 also pointed out):