Skip to content
Open
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
109 changes: 109 additions & 0 deletions RISCV_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Netcatty RISC-V Build Guide

This document describes how to build and package Netcatty for the RISC-V (riscv64) architecture. Since Electron does not officially support RISC-V yet, we rely on community builds and manual packaging.

## Prerequisites

### 1. Node.js (riscv64)
Official Node.js builds for RISC-V are not always available or up-to-date. We recommend using the unofficial builds which are tested on actual RISC-V hardware.

* **Download Source**: [gounthar/unofficial-builds](https://github.com/gounthar/unofficial-builds/releases)
* **Recommendation**: Download the latest `.deb` package (e.g., v22.x or v24.x) and install it:
```bash
wget https://github.com/gounthar/unofficial-builds/releases/download/v24.12.0/nodejs-unofficial_24.12.0-1_riscv64.deb
sudo dpkg -i nodejs-unofficial_*.deb
```

### 2. Electron Binary (riscv64)
You need a pre-built Electron binary for RISC-V.
* **Download Source**: [riscv-forks/electron-riscv-releases](https://github.com/riscv-forks/electron-riscv-releases/releases)
* **Version**: Ensure you download a version matching our `package.json` (currently **v39.2.7** or compatible v39.x).
* **Setup**:
1. Download `electron-v39.2.7-linux-riscv64.zip`.
2. Extract it to a known location (e.g., `~/electron-riscv`).
3. This folder will serve as the "host" for our application.

### 3. System Dependencies
Install build tools required for compiling native modules (like `node-pty`, `serialport`) and runtime libraries.

```bash
sudo apt update
sudo apt install build-essential python3 pkg-config libsecret-1-dev
```

*Note: Building native modules requires significant RAM. If you have <8GB RAM, we strongly recommend setting up a 4GB Swap file to avoid compilation crashes.*

## Building & Packaging

We provide scripts to automate the build and packaging process.

### 1. Build and Generate `.deb` Packages (Recommended)
This method generates ready-to-install Debian packages.

1. **Clone & Install Dependencies**:
```bash
git clone https://github.com/your-repo/netcatty.git
cd netcatty
npm install
```

2. **Set Environment Variable**:
Tell the script where your Electron binary is located.
```bash
export ELECTRON_RISCV_DIST="$HOME/electron-riscv"
```

3. **Run the Build Script**:
```bash
./scripts/pack-riscv.sh # Generates app.asar
./scripts/mkdeb-riscv.sh # Generates .deb packages
```

This will produce two packages in the `release/` directory:
* `netcatty_x.x.x_riscv64.deb`: Standard version.
* `netcatty-debug_x.x.x_riscv64.deb`: **Safe Mode version** (runs with `--disable-gpu --no-sandbox`).

### 2. Installation
Install the package suitable for your board.

* **For most RISC-V boards (Spacemit K1, Lichee Pi 4A, etc.)**:
The GPU drivers might be unstable with Electron. Use the **debug** package if you encounter rendering issues (black screen, flickering).
```bash
sudo dpkg -i release/netcatty-debug_*.deb
```

* **For boards with working GPU acceleration**:
```bash
sudo dpkg -i release/netcatty_*.deb
```

## Troubleshooting

### "Bus error" or Compilation Crashes
This usually means you ran out of memory during `npm install` or `npm rebuild`.
**Fix**: Add Swap space.
```bash
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
```

### "Cannot find module .../pty.node"
This happens if the native modules were compiled for the system Node.js version instead of Electron's internal Node.js version.
**Fix**: Rebuild native modules for Electron.
```bash
# Inside netcatty directory
export npm_config_runtime=electron
export npm_config_target=39.2.7 # Match your Electron version
export npm_config_disturl=https://electronjs.org/headers
npm rebuild @serialport/bindings-cpp node-pty --build-from-source
```

### Graphical Glitches / Black Window
Electron often has trouble with current RISC-V GPU drivers (PowerVR, Imagination, etc.).
**Fix**: Run with GPU disabled.
```bash
netcatty --disable-gpu --no-sandbox
```
*Note: The `netcatty-debug` package applies these flags automatically.*
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"pack:win": "npm run build && cross-env NODE_OPTIONS=--disable-warning=DEP0190 electron-builder --config electron-builder.json --win --publish=never",
"pack:mac": "npm run build && cross-env NODE_OPTIONS=--disable-warning=DEP0190 electron-builder --config electron-builder.json --mac --publish=never",
"pack:linux": "npm run build && cross-env NODE_OPTIONS=--disable-warning=DEP0190 electron-builder --config electron-builder.json --linux --publish=never",
"pack:riscv": "./scripts/pack-riscv.sh",
"postinstall": "electron-builder install-app-deps",
"rebuild": "electron-builder install-app-deps",
"lint": "eslint .",
Expand Down
31 changes: 31 additions & 0 deletions scripts/get-config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const fs = require('fs');
const path = require('path');

const pkgPath = path.resolve(__dirname, '../package.json');
const builderPath = path.resolve(__dirname, '../electron-builder.json');

try {
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
let builder = {};
if (fs.existsSync(builderPath)) {
builder = JSON.parse(fs.readFileSync(builderPath, 'utf8'));
}

const safe = (str) => (str || '').replace(/"/g, '\\"');

console.log(`APP_NAME="${safe(pkg.name)}"`);
console.log(`VERSION="${safe(pkg.version)}"`);
console.log(`PRODUCT_NAME="${safe(builder.productName || pkg.name)}"`);
console.log(`APP_ID="${safe(builder.appId || 'com.example.app')}"`);
console.log(`DESCRIPTION="${safe(pkg.description || '')}"`);
console.log(`AUTHOR="${safe(pkg.author || '')}"`);
console.log(`MAINTAINER="${safe(pkg.author || '')}"`);

// Linux specific
const linux = builder.linux || {};
console.log(`CATEGORY="${safe(linux.category || 'Utility')}"`);

} catch (e) {
console.error('Error reading config:', e);
process.exit(1);
}
208 changes: 208 additions & 0 deletions scripts/pack-riscv.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#!/bin/bash
set -e

# Load configuration
SCRIPT_DIR=$(dirname "$0")
PROJECT_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)

# Load metadata
if [ -f "$PROJECT_ROOT/scripts/get-config.cjs" ]; then
eval $(node "$PROJECT_ROOT/scripts/get-config.cjs")
else
echo "Warning: Config script not found, using defaults."
APP_NAME="netcatty"
VERSION="0.0.0"
PRODUCT_NAME="Netcatty"
MAINTAINER="binaricat"
DESCRIPTION="Netcatty SSH Client"
CATEGORY="Development"
fi

# Configuration
ARCH="riscv64"
DIST_DIR="$PROJECT_ROOT/release/riscv_build"
OUTPUT_ROOT="$PROJECT_ROOT/release"
ELECTRON_BINARY_DIR="${ELECTRON_PATH:-$HOME/electron-riscv}"

echo "----------------------------------------------------------------"
echo "Netcatty RISC-V Build & Pack"
echo "----------------------------------------------------------------"
echo "Product: $PRODUCT_NAME ($APP_NAME)"
echo "Version: $VERSION"
echo "Target Dir: $DIST_DIR"
echo "Electron Source: $ELECTRON_BINARY_DIR"

# Check dependencies
if ! command -v npm >/dev/null 2>&1; then
echo "Error: npm is not installed."
exit 1
fi

# ==============================================================================
# Phase 1: Build & Assembly (Assembly)
# ==============================================================================

if [ "$SKIP_ASSEMBLY" != "true" ]; then
# 1. Build Frontend
if [ "$SKIP_BUILD" != "true" ]; then
echo "Building frontend..."
cd "$PROJECT_ROOT"
npm run build
else
echo "Skipping build step (SKIP_BUILD=true)..."
fi

# 2. Prepare Directory Structure
echo "Cleaning distribution directory..."
rm -rf "$DIST_DIR"
mkdir -p "$DIST_DIR/resources/app"

# 3. Copy Electron Binary
if [ -f "$ELECTRON_BINARY_DIR/electron" ]; then
echo "Copying Electron binary and resources from $ELECTRON_BINARY_DIR..."
rsync -av --exclude 'resources/app' "$ELECTRON_BINARY_DIR/" "$DIST_DIR/"
else
echo "Warning: Electron binary not found at $ELECTRON_BINARY_DIR."
echo "You will need to manually place the 'electron' binary in $DIST_DIR before packaging."
fi

# 4. Copy Application Files (No ASAR)
echo "Copying application files to resources/app..."
APP_DIR="$DIST_DIR/resources/app"

cp "$PROJECT_ROOT/package.json" "$APP_DIR/"
cp -r "$PROJECT_ROOT/electron" "$APP_DIR/"
cp -r "$PROJECT_ROOT/dist" "$APP_DIR/"

# 5. Handle node_modules (Clean & Production)
echo "Handling node_modules..."
if [ -d "$PROJECT_ROOT/node_modules" ]; then
echo "Copying node_modules..."
cp -r "$PROJECT_ROOT/node_modules" "$APP_DIR/"

if [ -f "$APP_DIR/package.json" ]; then
echo "Pruning development dependencies..."
cd "$APP_DIR"
npm prune --production
cd "$PROJECT_ROOT"
fi
else
echo "Warning: node_modules not found. Skipping."
fi
else
echo "Skipping assembly phase (SKIP_ASSEMBLY=true)..."
fi

# ==============================================================================
# Phase 2: Debian Packaging
# ==============================================================================

if [ "$SKIP_DEB" == "true" ]; then
echo "Skipping Debian package generation (SKIP_DEB=true)."
echo "Assembly completed at: $DIST_DIR"
exit 0
fi

SOURCE_DIR="$DIST_DIR"

if [ ! -d "$SOURCE_DIR" ]; then
echo "Error: Source directory $SOURCE_DIR does not exist."
exit 1
fi

# Function to build a package
build_package() {
local PKG_NAME="$1"
local PKG_SUFFIX="$2" # e.g., "-debug" or empty
local EXTRA_FLAGS="$3"

local DEB_DIR="$OUTPUT_ROOT/deb_dist_${PKG_NAME}"
local INSTALL_DIR="/opt/$PKG_NAME"

echo "----------------------------------------------------------------"
echo "Building package: $PKG_NAME"
echo "Flags: $EXTRA_FLAGS"
echo "----------------------------------------------------------------"

# Clean up previous build
rm -rf "$DEB_DIR"
mkdir -p "$DEB_DIR/DEBIAN"
mkdir -p "$DEB_DIR$INSTALL_DIR"
mkdir -p "$DEB_DIR/usr/bin"
mkdir -p "$DEB_DIR/usr/share/applications"
mkdir -p "$DEB_DIR/usr/share/icons/hicolor/512x512/apps"

# Copy application files
echo "Copying application files..."
cp -r "$SOURCE_DIR/"* "$DEB_DIR$INSTALL_DIR/"
chmod -R 755 "$DEB_DIR$INSTALL_DIR"

# Create wrapper script
echo "Creating wrapper script..."
if [ -f "$DEB_DIR$INSTALL_DIR/netcatty" ]; then
BIN_NAME="netcatty"
elif [ -f "$DEB_DIR$INSTALL_DIR/electron" ]; then
BIN_NAME="electron"
else
BIN_NAME=$(find "$DEB_DIR$INSTALL_DIR" -maxdepth 1 -type f -executable -printf "%s\t%p\n" | sort -n | tail -1 | awk '{print $2}' | xargs basename)
fi

mv "$DEB_DIR$INSTALL_DIR/$BIN_NAME" "$DEB_DIR$INSTALL_DIR/$PKG_NAME-bin"

cat > "$DEB_DIR$INSTALL_DIR/$PKG_NAME" <<EOF
#!/bin/bash
exec $INSTALL_DIR/$PKG_NAME-bin $EXTRA_FLAGS "\$@"
EOF
chmod +x "$DEB_DIR$INSTALL_DIR/$PKG_NAME"

# Create symlink
ln -s "$INSTALL_DIR/$PKG_NAME" "$DEB_DIR/usr/bin/$PKG_NAME"

# Copy icon
if [ -f "$PROJECT_ROOT/public/icon.png" ]; then
cp "$PROJECT_ROOT/public/icon.png" "$DEB_DIR/usr/share/icons/hicolor/512x512/apps/$PKG_NAME.png"
fi

# Create desktop entry
echo "Creating desktop entry..."
cat > "$DEB_DIR/usr/share/applications/$PKG_NAME.desktop" <<EOF
[Desktop Entry]
Name=$PRODUCT_NAME${PKG_SUFFIX}
Exec=$INSTALL_DIR/$PKG_NAME %U
Terminal=false
Type=Application
Icon=$PKG_NAME
StartupWMClass=$PKG_NAME
Comment=$DESCRIPTION
Categories=$CATEGORY;
EOF

# Create control file
echo "Creating control file..."
cat > "$DEB_DIR/DEBIAN/control" <<EOF
Package: $PKG_NAME
Version: $VERSION
Architecture: $ARCH
Maintainer: $MAINTAINER
Description: $DESCRIPTION${PKG_SUFFIX}
EOF

# Build .deb
echo "Building .deb..."
mkdir -p "$OUTPUT_ROOT"
dpkg-deb --build "$DEB_DIR" "$OUTPUT_ROOT/${PKG_NAME}_${VERSION}_${ARCH}.deb"

# Clean up temp dir
rm -rf "$DEB_DIR"

echo "Package created: $OUTPUT_ROOT/${PKG_NAME}_${VERSION}_${ARCH}.deb"
}

# Build Normal Package (Standard)
build_package "$APP_NAME" "" "--no-sandbox"

# Build Debug Package (Disable GPU)
build_package "${APP_NAME}-debug" " (Debug)" "--disable-gpu --no-sandbox"

echo "----------------------------------------------------------------"
echo "All builds completed successfully!"