Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ Some simple examples::
- ``-l, --sjg [BOARD]``: Set SJG_LAB (optionally specify board)
- ``-m, --merge``: Create merge request using cover letter from patch series
- ``-p, --pytest [BOARD]``: Enable PYTEST (optionally specify board name)
- ``-r, --remote REMOTE``: Git remote to push to (default: ``ci_remote``
setting or ``ci``)
- ``-s, --suites``: Enable SUITES
- ``-t, --test-spec SPEC``: Override test specification (e.g. "not sleep",
"test_ofplatdata")
Expand Down Expand Up @@ -702,7 +704,9 @@ hooks to PATH.
- ``--find PATTERN``: Find tests matching PATTERN and show full IDs
- ``--force-reconfig``: Force reconfiguration (use with -b)
- ``--fresh``: Delete build dir before building (use with -b)
- ``--bt``: Show backtrace on crash and exit (implies -G)
- ``-g``: Run sandbox under gdbserver at localhost:1234
- ``--gdb-cmd CMD``: GDB command to run after connecting (repeatable; implies -G)
- ``--gdb-phase PHASE``: Debug a specific phase (spl, tpl, vpl)
- ``-G, --gdb``: Launch gdb-multiarch and connect to an existing gdbserver
- ``-j, --jobs JOBS``: Number of parallel jobs (use with -b)
Expand Down Expand Up @@ -863,7 +867,10 @@ without going through pytest. This is faster for quick iteration on C code.
- ``-B, --board BOARD``: Board to build/test (default: sandbox)
- ``-f, --force-reconfig``: Force reconfiguration (use with -b)
- ``-F, --fresh``: Delete build dir before building (use with -b)
- ``--bt``: Show backtrace on crash and exit (implies -g)
- ``--flattree-too``: Run both live-tree and flat-tree tests (default: live-tree only)
- ``-g, --gdb``: Run sandbox under gdb-multiarch
- ``--gdb-cmd CMD``: GDB command to run after the test (repeatable; implies -g)
- ``-j, --jobs JOBS``: Number of parallel jobs (use with -b)
- ``-l, --list``: List available tests
- ``-L, --lto``: Enable LTO when building (use with -b)
Expand All @@ -887,6 +894,10 @@ Config Subcommand
The ``config`` command (alias ``cfg``) provides tools for examining and
modifying U-Boot configuration::

# Find a function's source location
uman config -B sandbox -f do_version
um cfg -f do_mem

# Grep .config for a pattern (case-insensitive regex)
uman config -B sandbox -g VIDEO
um cfg -g DM_TEST
Expand All @@ -905,11 +916,14 @@ for interactive comparison instead of copying.

**Options**:

- ``-b, --build``: Build before running the config action
- ``-B, --board BOARD``: Board name (required; or set ``$b``)
- ``-f, --find FUNC``: Find function in binary and show source file:line
- ``-g, --grep PATTERN``: Grep .config for PATTERN (regex, case-insensitive)
- ``-m, --meld``: Compare defconfig with meld
- ``-s, --sync``: Resync defconfig from .config
- ``--build-dir DIR``: Override build directory
- Plus common build options (``-a``, ``--force-reconfig``, ``-F``, ``-j``,
``-L``, ``-o``, ``-T``, ``--no-trace-early``); use with ``-b``

Build Subcommand
----------------
Expand Down Expand Up @@ -1029,6 +1043,13 @@ Settings are stored in ``~/.uman`` (created on first run)::
# Build directory for U-Boot out-of-tree builds
build_dir = /tmp/b

# Git remote for CI pushes (default: ci); auto-detected from upstream
# ci_remote = ci

# Map upstream remotes to push remotes (comma-separated from:to pairs)
# e.g. if upstream is 'us' but you push to 'dm': ci_remote_map = us:dm
# ci_remote_map = us:dm

# Directory for firmware blobs (OpenSBI, TF-A, etc.)
blobs_dir = ~/dev/blobs

Expand Down
11 changes: 8 additions & 3 deletions uman_pkg/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def get_cmd(args, board, build_dir):
Returns:
list: Full buildman command including 'buildman' as first element
"""
return ['buildman'] + get_buildman_args(args, board, build_dir)
return [get_buildman()] + get_buildman_args(args, board, build_dir)


def base_bm_args(board, build_dir, lto=True):
Expand Down Expand Up @@ -220,7 +220,7 @@ def get_buildman_args(args, board, build_dir):

def build_board(board, dry_run=False, lto=False, adjust_cfg=None,
force_reconfig=False, fresh=False, jobs=None, trace=False,
trace_early=True, output_dir=None):
trace_early=True, output_dir=None, extra_env=None):
"""Build U-Boot for a board

Args:
Expand Down Expand Up @@ -259,11 +259,16 @@ def build_board(board, dry_run=False, lto=False, adjust_cfg=None,
bm_args.extend(['-a', cfg])

env = None
if extra_env or trace:
env = os.environ.copy()
if extra_env:
env.update(extra_env)
if trace:
bm_args.extend(['-a', 'TRACE'])
if trace_early:
bm_args.extend(['-a', 'TRACE_EARLY'])
env = os.environ.copy()
if not env:
env = os.environ.copy()
env['FTRACE'] = '1'

result = buildman(*bm_args, dry_run=dry_run, env=env, capture=False)
Expand Down
9 changes: 9 additions & 0 deletions uman_pkg/cc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1031,8 +1031,10 @@ def run(args): # pylint: disable=too-many-locals,too-many-branches,too-many-sta
sock_path = os.path.join(project_src, EDITOR_SOCK)
try:
if not existed:
tout.progress('Creating container')
create_container(name, base, dry_run)

tout.progress('Setting up mounts')
add_all_mounts(name, project_src, args.mount, args.output,
args.no_output, dry_run)

Expand Down Expand Up @@ -1090,6 +1092,7 @@ def run(args): # pylint: disable=too-many-locals,too-many-branches,too-many-sta
tout.notice(
'Running in privileged mode (device-mapper enabled)')

tout.progress('Starting container')
ensure_running(name, existed, dry_run)

# In privileged mode, uid namespacing is disabled, so the
Expand All @@ -1109,11 +1112,17 @@ def run(args): # pylint: disable=too-many-locals,too-many-branches,too-many-sta
dry_run=dry_run)

# Wait for user and set up (idempotent operations)
tout.progress('Waiting for container to be ready')
wait_for_user(name, dry_run)
tout.progress('Configuring container')
setup_container(name, dry_run)
tout.progress('Installing packages')
install_tools(name, packages, dry_run)
tout.progress('Installing Claude Code')
install_claude(name, dry_run)
tout.progress('Setting up uman')
setup_uman(name, uboot_tools, dry_run)
tout.clear_progress()

# Check X11 access for clipboard (image paste)
if not dry_run and os.path.isdir('/tmp/.X11-unix'):
Expand Down
102 changes: 99 additions & 3 deletions uman_pkg/cmdconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import shutil

# pylint: disable=import-error
from u_boot_pylib import command
from u_boot_pylib import tout

from uman_pkg import build as build_mod
Expand All @@ -36,6 +37,84 @@ def get_config_path(board, build_dir=None):
return os.path.join(build_dir, '.config')


def strip_src_prefix(loc, src_dir):
"""Strip the source-tree prefix from an addr2line location

The binary may have been built in a different directory (e.g. a
container), so the DWARF paths won't match the local source tree.
Walk the path components to find the longest relative suffix that
exists as a file in src_dir.

Args:
loc (str): Location string from addr2line (e.g.
'/home/ubuntu/project/cmd/version.c:18')
src_dir (str or None): Local source directory, or None

Returns:
str: Relative path if found, otherwise the original loc
"""
if not src_dir or ':' not in loc:
return loc

# Split off the :line suffix
path, sep, line = loc.rpartition(':')
# Try progressively shorter prefixes
parts = path.split('/')
for i in range(1, len(parts)):
rel = '/'.join(parts[i:])
if os.path.exists(os.path.join(src_dir, rel)):
return f'{rel}{sep}{line}'
return loc


def do_find(args):
"""Find a function in the binary and show its source file and line

Uses nm to look up function symbols matching the pattern, then
addr2line to resolve each to a source location.

Args:
args (argparse.Namespace): Arguments from cmdline

Returns:
int: Exit code (0 for success, non-zero for failure)
"""
board = args.board or os.environ.get('b')
if not board:
tout.error('Board is required: use -B BOARD or set $b')
return 1

build_dir = args.output_dir or build_mod.get_dir(board)
binary = os.path.join(build_dir, 'u-boot')
if not os.path.exists(binary):
tout.error(f'Binary not found: {binary}')
tout.error(f'Build the board first: um b {board}')
return 1

pattern = args.find
result = command.run_one('nm', binary, capture=True)
matches = []
for line in result.stdout.splitlines():
parts = line.split()
if len(parts) == 3 and parts[1] in 'TtWw':
if pattern in parts[2]:
matches.append((parts[0], parts[2]))

if not matches:
tout.error(f'No functions matching: {pattern}')
return 1

addrs = [addr for addr, _ in matches]
result = command.run_one('addr2line', '-e', binary, *addrs,
capture=True)
src_dir = get_uboot_dir()
lines = result.stdout.strip().splitlines()
for (_, name), loc in zip(matches, lines):
print(f'{name}: {strip_src_prefix(loc, src_dir)}')

return 0


def do_grep(args):
"""Grep the .config file for a pattern

Expand All @@ -50,7 +129,7 @@ def do_grep(args):
tout.error('Board is required: use -B BOARD or set $b')
return 1

config_path = get_config_path(board, args.build_dir)
config_path = get_config_path(board, args.output_dir)
if not os.path.exists(config_path):
tout.error(f'Config file not found: {config_path}')
tout.error(f'Build the board first: um b {board}')
Expand Down Expand Up @@ -94,7 +173,7 @@ def do_sync(args, use_meld=False):
tout.error('Not in a U-Boot tree and $USRC not set')
return 1

build_dir = args.build_dir or build_mod.get_dir(board)
build_dir = args.output_dir or build_mod.get_dir(board)
defconfig_path = os.path.join(uboot_dir, 'configs', f'{board}_defconfig')

# Change to U-Boot directory for make
Expand Down Expand Up @@ -156,6 +235,23 @@ def run(args):
Returns:
int: Exit code
"""
if args.build:
board = args.board or os.environ.get('b')
if not board:
tout.error('Board is required: use -B BOARD or set $b')
return 1
if not build_mod.build_board(
board, args.dry_run, lto=args.lto,
adjust_cfg=args.adjust_cfg,
force_reconfig=args.force_reconfig, fresh=args.fresh,
jobs=args.jobs, trace=args.trace,
trace_early=not args.no_trace_early,
output_dir=args.output_dir):
return 1

if args.find:
return do_find(args)

if args.grep:
return do_grep(args)

Expand All @@ -165,5 +261,5 @@ def run(args):
if args.sync:
return do_sync(args)

tout.error('No action specified (use -g PATTERN, -m, or -s)')
tout.error('No action specified (use -f FUNC, -g PATTERN, -m, or -s)')
return 1
Loading
Loading