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
4 changes: 2 additions & 2 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
git gperf automake libtool pkg-config build-essential gperf genromfs \
libgmp-dev libmpc-dev libmpfr-dev libisl-dev binutils-dev libelf-dev \
libexpat1-dev gcc-multilib g++-multilib picocom u-boot-tools util-linux \
kconfig-frontends
kconfig-frontends cmake
- name: Install package dependencies
run: |
python -m pip install --upgrade pip
Expand All @@ -43,4 +43,4 @@ jobs:
pre-commit run --all-files
- name: Test with pytest
run: |
pytest
pytest -x
88 changes: 55 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

**NuttX Build System Assistant** - A Python tool for managing and building NuttX RTOS projects with ease.

ntxbuild is simply a wrapper around the many tools available in the NuttX repository. It wraps around tools
ntxbuild is a wrapper around the many tools available in the NuttX repository. It wraps around utilities
such as make, kconfig-tweak, menuconfig and most used bash scripts (such as configure.sh).

Also, it provides different features, such as downloading required toolchains through the CLI.

This tool provides a command line interface that supports NuttX configuration and building,
while also providing a Python API that allows you to script your builds.

Expand All @@ -18,9 +20,8 @@ while also providing a Python API that allows you to script your builds.
- **Parallel Builds**: Support for multi-threaded builds with isolated workspaces
- **Real-time Output**: Live build progress with proper ANSI escape sequence handling
- **Configuration Management**: Kconfig integration for easy system configuration
- **Build Cleanup**: Automated artifact management and cleanup
- **Persistent Settings**: Environment configuration saved in `.ntxenv` files
- **Interactive Tools**: Support for curses-based tools like menuconfig
- **Toolchin Support**: Download and use your required toolchain automatically through the CLI

## Requirements

Expand All @@ -29,36 +30,6 @@ while also providing a Python API that allows you to script your builds.
- **Make** and standard build tools required by NuttX RTOS
- **CMake** supported but optional

## Installation

### Using pip

As an user, you can install this tool using pip:
```bash
pip install ntxbuild
```

### From Source
If you are a developer or simply wants to install from source, you can clone
this repository and install using `pip install -e <repo>`

```bash
git clone <repository-url>
cd ntxbuild
pip install -e .
```

Use the `dev` configuration to install development tools and `docs` to install
documentation tools.

```bash
pip install -e ".[dev]"
```
```bash
pip install -e ".[docs]"
```


## Quick Start

### 1. Initialize Your NuttX Environment
Expand Down Expand Up @@ -92,6 +63,57 @@ ntxbuild kconfig --set-value CONFIG_DEBUG=y
ntxbuild kconfig --set-str CONFIG_APP_NAME="MyApp"
```

### Alternative Usage
Alternatively, you can automate your builds using a Python script instead of the CLI.

```python
from pathlib import Path
from ntxbuild.build import MakeBuilder

current_dir = Path.cwd()

# Use the Makefile-based builder
builder = MakeBuilder(current_dir, "nuttx", "nuttx-apps")
# Initialize the board/defconfig
setup_result = builder.initialize("sim", "nsh")

# Execute the build with 10 parallel jobs
builder.build(parallel=10)

# You can now clean the environment if needed
builder.distclean()
```


## Installation

### Using pip

As an user, you can install this tool using pip:
```bash
pip install ntxbuild
```

### From Source
If you are a developer or simply wants to install from source, you can clone
this repository and install using `pip install -e <repo>`

```bash
git clone <repository-url>
cd ntxbuild
pip install -e .
```

Use the `dev` configuration to install development tools and `docs` to install
documentation tools.

```bash
pip install -e ".[dev]"
```
```bash
pip install -e ".[docs]"
```

## Contributing
Contributions are always welcome but will be subject to review and approval.
Basic rules:
Expand Down
5 changes: 5 additions & 0 deletions docs/source/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
- Menuconfig available and also kconfig options modification directly from command line
- Full Python API support

## Toolchain Management
- The toolchain module provides a quick way to download toolchains for supported architectures.
- Installed on home directory for general use when needed even outside `ntxbuild` CLI.
- Toolchains are organized by NuttX release and default to latest version.

## CMake Support
The use of Makefile is the default state of `ntxbuild`. To build using CMake, pass
`--use-cmake` to the `ntxbuild start` command. This will setup the directory to CMake
Expand Down
10 changes: 10 additions & 0 deletions docs/source/ntxbuild.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ downloading the NuttX source code.
:show-inheritance:
:undoc-members:

Toolchains Module
---------------------

This contains helper funtions to manage toolchains, such as installing and listing them.

.. automodule:: ntxbuild.toolchains
:members:
:show-inheritance:
:undoc-members:

Utilities Module
---------------------

Expand Down
50 changes: 50 additions & 0 deletions docs/source/quick_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,53 @@ ntxbuild menuconfig
ntxbuild kconfig --set-value CONFIG_DEBUG=y
ntxbuild kconfig --set-str CONFIG_APP_NAME="MyApp"
```

## Using Python
Alternatively, you can automate your builds using a Python script instead of the CLI.

```python
from pathlib import Path
from ntxbuild.build import MakeBuilder

current_dir = Path.cwd()

# Use the Makefile-based builder
builder = MakeBuilder(current_dir, "nuttx", "nuttx-apps")
# Initialize the board/defconfig
setup_result = builder.initialize("sim", "nsh")

# Execute the build with 10 parallel jobs
builder.build(parallel=10)

# You can now clean the environment if needed
builder.distclean()
```

## Downloading Toolchains
To visualize currently available toolchains, execute the `toolchain list` command:

```bash
$ ntxbuild toolchain list
Available toolchains:
- clang-arm-none-eabi
- gcc-aarch64-none-elf
- gcc-arm-none-eabi
- xtensa-esp-elf
- riscv-none-elf
Installed toolchains:
- xtensa-esp-elf
```

To install, execute the `toolchain install` command using any of the toolchains from the list above.

```bash
$ ntxbuild toolchain install gcc-arm-none-eabi
Installing toolchain gcc-arm-none-eabi for NuttX v12.12.0
✅ Toolchain gcc-arm-none-eabi installed successfully
Installation directory: /home/fdcavalcanti/ntxenv/toolchains
Note: Toolchains are sourced automatically during build.
```

> **_NOTE:_** Toolchains are automatically appended to PATH when building from the CLI.

> **_NOTE:_** Toolchains are installed to `~/ntxenv/toolchains`.
5 changes: 1 addition & 4 deletions ntxbuild/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import logging
import sys
from pathlib import Path

from . import build, config, utils

Expand All @@ -17,10 +16,9 @@
def _setup_logging():
"""Setup logging configuration for the ntxbuild library."""
# Create logs directory if it doesn't exist
log_dir = Path.home() / ".ntxbuild" / "logs"
log_dir = utils.NTXBUILD_DEFAULT_USER_DIR / "logs"
log_dir.mkdir(parents=True, exist_ok=True)

# Configure logging.
logging.basicConfig(
level=logging.WARNING,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
Expand All @@ -29,7 +27,6 @@ def _setup_logging():
],
)

# Set our library logger level
logger = logging.getLogger("ntxbuild")
logger.setLevel(logging.WARNING)

Expand Down
85 changes: 79 additions & 6 deletions ntxbuild/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@

from .build import BuildTool, nuttx_builder
from .config import ConfigManager
from .env_data import clear_ntx_env, load_ntx_env, save_ntx_env
from .env_data import clear_ntx_env, create_base_env_file, load_ntx_env
from .setup import download_nuttx_apps_repo, download_nuttx_repo
from .toolchains import ManagePath, ToolchainInstaller
from .utils import NUTTX_APPS_DEFAULT_DIR_NAME, NUTTX_DEFAULT_DIR_NAME, find_nuttx_root

logger = logging.getLogger("ntxbuild.cli")
Expand Down Expand Up @@ -57,7 +58,7 @@ def prepare_env(
# This validates the directory structure
nuttxspace = find_nuttx_root(current_dir, nuttx_dir, apps_dir)

save_ntx_env(nuttxspace, nuttx_dir, apps_dir, build_tool)
create_base_env_file(nuttxspace, nuttx_dir, apps_dir, build_tool)
env = load_ntx_env(nuttxspace)
return env["general"]

Expand Down Expand Up @@ -134,14 +135,12 @@ def main(log_level):


@main.command()
def install():
"""Install NuttX and Apps repositories.
def download():
"""Download NuttX and Apps repositories.

Downloads the NuttX OS and Apps repositories if they don't already
exist in the current directory. If the repositories are already
present, this command will verify their existence.

Exits with code 0 on success.
"""
current_dir = Path.cwd()
click.echo("🚀 Downloading NuttX and Apps repositories...")
Expand All @@ -160,6 +159,75 @@ def install():
sys.exit(0)


@main.group()
def toolchain():
"""Manage NuttX toolchains.

Provides commands to install and list available toolchains for NuttX builds.
"""
pass


@toolchain.command()
@click.argument("toolchain_name", nargs=1, required=True)
@click.argument("nuttx_version", nargs=1, required=False)
def install(toolchain_name, nuttx_version):
"""Install a toolchain for a specific NuttX version.

Downloads and installs a toolchain from the URLs specified in toolchains.ini.
The toolchain will be installed to ~/ntxenv/toolchains/<toolchain-name>/.

The optional argument NUTTX_VERSION (x.y.z) is the NuttX release version.
Defaults to the latest stable version.

You can also call 'ntxbuild toolchain list' to see available toolchains.
"""
try:
tinstaller = ToolchainInstaller(toolchain_name, nuttx_version)
except AssertionError:
click.echo(
f"❌ Toolchain {toolchain_name} not found. "
"Make sure this toolchain is available to download.\n"
"Call 'ntxbuild toolchain list' to see available toolchains."
)
sys.exit(1)

click.echo(
f"Installing toolchain {tinstaller.toolchain_name} "
f"for NuttX v{tinstaller.nuttx_version}"
)

try:
toolchain_path = tinstaller.install()
except FileExistsError:
click.echo(f"Toolchain {tinstaller.toolchain_name} already installed")
sys.exit(0)

click.echo(f"✅ Toolchain {toolchain_name} installed successfully")
click.echo(f"Installation directory: {toolchain_path}")
click.echo("Note: Toolchains are sourced automatically during build.")
sys.exit(0)


@toolchain.command()
def list():
"""List available toolchains.

Displays all toolchains that can be installed for NuttX builds.
"""
toolm = ManagePath()
supported = toolm.supported_toolchains
installed = toolm.installed_toolchains

click.echo("Available toolchains:")
for toolchain in supported:
click.echo(f" - {toolchain}")
click.echo("Installed toolchains:")
for toolchain in installed:
click.echo(f" - {toolchain}")
sys.exit(0)


@main.command()
@click.option(
"--apps-dir", "-a", help="Apps directory", default=NUTTX_APPS_DEFAULT_DIR_NAME
Expand Down Expand Up @@ -305,6 +373,7 @@ def build(parallel):
Exits with code 0 on success, 1 on error, or the build exit code
on build failure.
"""
ManagePath().add_all_toolchains_to_path()
try:
builder = get_builder()
result = builder.build(parallel)
Expand All @@ -324,6 +393,7 @@ def distclean():
Exits with code 0 on success.
"""
click.echo("🧹 Resetting NuttX environment...")
ManagePath().add_all_toolchains_to_path()
builder = get_builder()
builder.distclean()
clear_ntx_env(builder.nuttxspace_path)
Expand All @@ -340,6 +410,7 @@ def clean():
Exits with code 0 on success, 1 on error.
"""
click.echo("🧹 Cleaning build artifacts...")
ManagePath().add_all_toolchains_to_path()
try:
builder = get_builder()
builder.clean()
Expand Down Expand Up @@ -370,6 +441,7 @@ def make(ctx):
"""
command = " ".join(tuple(ctx.args))
click.echo(f"🧹 Running make {command}")
ManagePath().add_all_toolchains_to_path()
builder = get_builder()

if builder.build_tool == BuildTool.CMAKE:
Expand Down Expand Up @@ -397,6 +469,7 @@ def cmake(ctx):
"""
command = " ".join(tuple(ctx.args))
click.echo(f"🧹 Running cmake {command}")
ManagePath().add_all_toolchains_to_path()
builder = get_builder()

if builder.build_tool == BuildTool.MAKE:
Expand Down
Loading