unify build, fix opening files when app is running

This commit is contained in:
2026-01-30 15:26:23 +01:00
parent 576e5819b2
commit b3cb95c7d3
15 changed files with 718 additions and 490 deletions

View File

@@ -1,160 +0,0 @@
# Cross-Compilation Guide
Build Windows binaries from Linux using Docker-based cross-compilation.
## Prerequisites
1. **Install Docker**:
```bash
# Ubuntu/Debian
sudo apt-get install docker.io
sudo usermod -aG docker $USER
# Log out and back in for group changes to take effect
```
2. **Verify Docker**:
```bash
docker --version
```
## Quick Start
### Option 1: Interactive Menu
```bash
./cross-compile.sh
```
Then select:
- `1` - Linux native
- `2` - Windows (MinGW)
- `3` - Both platforms
- `4` - Exit
### Option 2: Command Line
```bash
./cross-compile.sh windows # Windows (MinGW/GNU)
./cross-compile.sh all # All platforms
./cross-compile.sh linux # Linux native
```
### Option 3: Make
```bash
make cross-windows # Build for Windows
make cross-all # Build for all platforms
```
## What Gets Built
Binaries are created in the `dist/` directory:
- `rlogg-linux-x86_64` - Linux binary
- `rlogg-windows-x86_64.exe` - Windows binary (MinGW)
## How It Works
The `cross` tool:
1. Automatically installs on first use
2. Uses Docker to run cross-compilation in containers
3. Provides complete toolchains for each target
4. No need to install Windows SDK or MinGW manually
## About Windows Cross-Compilation
### Why MinGW/GNU Only?
When cross-compiling from Linux to Windows, only the **GNU target** (`x86_64-pc-windows-gnu`) is supported:
- **MinGW (GNU)** - Uses open-source MinGW toolchain
- ✅ Fully supported by `cross` from Linux
- ✅ Works great for most Windows applications
- ✅ Smaller binaries
- ✅ No runtime dependencies on Visual C++ redistributables
- **MSVC** - Microsoft Visual C++ toolchain
- ❌ Not available for cross-compilation from Linux
- ❌ Requires proprietary Microsoft tools
- ❌ Can only be built on Windows or via Windows VM
### Compatibility
The MinGW binaries work on **all Windows systems** (Windows 7+) without requiring additional runtime installations. They're fully compatible with standard Windows applications.
## Troubleshooting
### Docker permission denied
```bash
sudo usermod -aG docker $USER
# Log out and back in
```
### cross installation fails
```bash
# Install from source
cargo install cross --git https://github.com/cross-rs/cross
```
### Build fails with linker errors
The `cross` tool handles all linker configuration automatically. If you see linker errors, try:
```bash
# Clean and rebuild
make clean
./cross-compile.sh windows
```
### Very slow first build
The first build downloads the Docker image (~1-2 GB) and compiles dependencies. Subsequent builds are much faster due to caching.
## Alternative: Manual Cross-Compilation (Advanced)
If you don't want to use Docker, you can manually set up cross-compilation:
### For Windows from Linux
```bash
# Install MinGW
sudo apt-get install mingw-w64
# Add Rust target
rustup target add x86_64-pc-windows-gnu
# Configure cargo
mkdir -p ~/.cargo
cat >> ~/.cargo/config.toml << EOF
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
EOF
# Build
cargo build --release --target x86_64-pc-windows-gnu
```
**Note**: This only works for the GNU target, not MSVC.
## Testing Windows Binaries on Linux
Use Wine to test Windows binaries:
```bash
# Install Wine
sudo apt-get install wine64
# Run the Windows binary
wine dist/rlogg-windows-x86_64-gnu.exe
```
## CI/CD Integration
The GitHub Actions workflow automatically cross-compiles for all platforms. Just push to trigger builds:
```bash
git push origin master
```
Artifacts are available in the Actions tab.
## Performance Considerations
Cross-compilation is:
- **Fast**: Similar speed to native compilation
- **Cached**: Dependencies are cached in Docker volumes
- **Isolated**: Doesn't affect your system
Typical build times:
- First build: 2-5 minutes (includes Docker image download)
- Incremental builds: 30-60 seconds

59
Cargo.lock generated
View File

@@ -1793,6 +1793,32 @@ dependencies = [
"hashbrown 0.16.1",
]
[[package]]
name = "interprocess"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb"
dependencies = [
"blocking",
"cfg-if",
"futures-core",
"futures-io",
"intmap",
"libc",
"once_cell",
"rustc_version",
"spinning",
"thiserror 1.0.69",
"to_method",
"winapi",
]
[[package]]
name = "intmap"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
@@ -2820,11 +2846,12 @@ dependencies = [
[[package]]
name = "rlogg"
version = "0.4.0"
version = "0.4.1"
dependencies = [
"chrono",
"clap",
"eframe",
"interprocess",
"rayon",
"regex",
"rfd",
@@ -2844,6 +2871,15 @@ version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.44"
@@ -2916,6 +2952,12 @@ dependencies = [
"tiny-skia",
]
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "serde"
version = "1.0.228"
@@ -3095,6 +3137,15 @@ dependencies = [
"serde",
]
[[package]]
name = "spinning"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b"
dependencies = [
"lock_api",
]
[[package]]
name = "spirv"
version = "0.3.0+sdk-1.3.268.0"
@@ -3258,6 +3309,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "to_method"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8"
[[package]]
name = "toml_datetime"
version = "0.7.3"

View File

@@ -1,6 +1,6 @@
[package]
name = "rlogg"
version = "0.4.0"
version = "0.4.1"
edition = "2024"
authors = ["Stanislav Pastushenko <staspast1@gmail.com>"]
description = "A fast log file viewer with search, filtering, and highlighting capabilities"
@@ -19,6 +19,7 @@ serde_json = "1.0"
rayon = "1.10"
chrono = "0.4"
clap = { version = "4.5", features = ["derive"] }
interprocess = "1.2.1"
[profile.release]
debug = false

View File

@@ -1,70 +0,0 @@
# Build script for creating release binaries for Windows
# PowerShell script for Windows users
$ErrorActionPreference = "Stop"
Write-Host "RLogg - Multi-platform Release Builder (Windows)" -ForegroundColor Cyan
Write-Host "=" * 50 -ForegroundColor Cyan
Write-Host ""
# Create dist directory
$DistDir = "dist"
if (-not (Test-Path $DistDir)) {
New-Item -ItemType Directory -Path $DistDir | Out-Null
}
# Function to build for a target
function Build-Target {
param (
[string]$Target,
[string]$Name
)
Write-Host "Building for $Name ($Target)..." -ForegroundColor Yellow
try {
cargo build --release --target $Target
if ($LASTEXITCODE -eq 0) {
Write-Host "✓ Build successful for $Name" -ForegroundColor Green
# Copy binary to dist directory
if ($Target -like "*windows*") {
$SourcePath = "target\$Target\release\rlogg.exe"
$DestPath = "$DistDir\rlogg-$Name.exe"
} else {
$SourcePath = "target\$Target\release\rlogg"
$DestPath = "$DistDir\rlogg-$Name"
}
Copy-Item $SourcePath $DestPath -Force
Write-Host "✓ Binary copied to $DestPath" -ForegroundColor Green
Write-Host ""
return $true
}
} catch {
Write-Host "✗ Build failed for $Name" -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Red
Write-Host ""
return $false
}
}
# Build for Windows
Write-Host "=== Building for Windows ===" -ForegroundColor Cyan
$success = Build-Target "x86_64-pc-windows-msvc" "windows-x86_64"
if ($success) {
Write-Host ""
Write-Host "=== Build Complete ===" -ForegroundColor Green
Write-Host "Binaries are in the '$DistDir' directory:" -ForegroundColor Green
Get-ChildItem $DistDir | Format-Table Name, Length, LastWriteTime
Write-Host ""
Write-Host "To build for additional platforms, install the target and run:"
Write-Host " rustup target add <target-triple>"
Write-Host " cargo build --release --target <target-triple>"
} else {
Write-Host ""
Write-Host "Build failed!" -ForegroundColor Red
exit 1
}

View File

@@ -1,92 +0,0 @@
#!/bin/bash
# Build script for creating release binaries for all platforms
set -e
echo "RLogg - Multi-platform Release Builder"
echo "======================================"
echo ""
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Extract version from Cargo.toml
VERSION=$(grep -m1 '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/')
echo "Version: $VERSION"
echo ""
# Create dist directory
DIST_DIR="dist"
mkdir -p "$DIST_DIR"
# Function to build for a target
build_target() {
local target=$1
local name=$2
echo -e "${YELLOW}Building for $name ($target)...${NC}"
if cargo build --release --target "$target"; then
echo -e "${GREEN}✓ Build successful for $name${NC}"
# Copy binary to dist directory with version
if [[ "$target" == *"windows"* ]]; then
cp "target/$target/release/rlogg.exe" "$DIST_DIR/rlogg-$VERSION-$name.exe"
echo -e "${GREEN}✓ Binary copied to $DIST_DIR/rlogg-$VERSION-$name.exe${NC}"
else
cp "target/$target/release/rlogg" "$DIST_DIR/rlogg-$VERSION-$name"
# Strip binary on Unix-like systems
strip "$DIST_DIR/rlogg-$VERSION-$name" 2>/dev/null || true
echo -e "${GREEN}✓ Binary copied to $DIST_DIR/rlogg-$VERSION-$name${NC}"
fi
echo ""
return 0
else
echo -e "${RED}✗ Build failed for $name${NC}"
echo ""
return 1
fi
}
# Detect current platform
PLATFORM=$(uname -s)
ARCH=$(uname -m)
echo "Current platform: $PLATFORM ($ARCH)"
echo ""
# Build for current platform first
case "$PLATFORM" in
Linux)
echo "=== Building for Linux ==="
build_target "x86_64-unknown-linux-gnu" "linux-x86_64"
;;
Darwin)
echo "=== Building for macOS ==="
if [[ "$ARCH" == "arm64" ]]; then
build_target "aarch64-apple-darwin" "macos-aarch64"
else
build_target "x86_64-apple-darwin" "macos-x86_64"
fi
;;
MINGW* | MSYS* | CYGWIN*)
echo "=== Building for Windows ==="
build_target "x86_64-pc-windows-msvc" "windows-x86_64"
;;
*)
echo -e "${RED}Unknown platform: $PLATFORM${NC}"
exit 1
;;
esac
echo ""
echo -e "${GREEN}=== Build Complete ===${NC}"
echo "Binaries are in the '$DIST_DIR' directory:"
ls -lh "$DIST_DIR"
echo ""
echo "To build for additional platforms, install the target and run:"
echo " rustup target add <target-triple>"
echo " cargo build --release --target <target-triple>"

82
build.sh Executable file
View File

@@ -0,0 +1,82 @@
#!/bin/bash
# Unified build script for RLogg
# Usage: ./build.sh [linux|windows]
# If no argument is provided, builds for both Linux and Windows.
set -e
# Configuration
DIST_DIR="dist"
VERSION=$(grep -m1 '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/')
LINUX_TARGET="x86_64-unknown-linux-gnu"
WINDOWS_TARGET="x86_64-pc-windows-gnu"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo "RLogg Builder v$VERSION"
echo "======================"
mkdir -p "$DIST_DIR"
check_cross() {
if ! command -v cross &> /dev/null; then
echo -e "${YELLOW}Installing 'cross' for cross-compilation...${NC}"
cargo install cross --git https://github.com/cross-rs/cross
fi
}
build_linux() {
echo -e "${BLUE}=== Building for Linux ($LINUX_TARGET) ===${NC}"
if cargo build --release --target "$LINUX_TARGET"; then
cp "target/$LINUX_TARGET/release/rlogg" "$DIST_DIR/rlogg-$VERSION-linux-x86_64"
strip "$DIST_DIR/rlogg-$VERSION-linux-x86_64" 2>/dev/null || true
echo -e "${GREEN}✓ Linux build success: $DIST_DIR/rlogg-$VERSION-linux-x86_64${NC}"
else
echo -e "${RED}✗ Linux build failed${NC}"
return 1
fi
}
build_windows() {
echo -e "${BLUE}=== Building for Windows ($WINDOWS_TARGET) ===${NC}"
check_cross
# Clean specific target to avoid conflicts
# cargo clean --release --target "$WINDOWS_TARGET" 2>/dev/null || true
if cross build --release --target "$WINDOWS_TARGET"; then
cp "target/$WINDOWS_TARGET/release/rlogg.exe" "$DIST_DIR/rlogg-$VERSION-windows-x86_64.exe"
echo -e "${GREEN}✓ Windows build success: $DIST_DIR/rlogg-$VERSION-windows-x86_64.exe${NC}"
else
echo -e "${RED}✗ Windows build failed${NC}"
return 1
fi
}
# Main logic
if [ $# -eq 0 ]; then
echo "No target specified, building for ALL platforms..."
build_linux
build_windows
else
case "$1" in
linux)
build_linux
;;
windows|win)
build_windows
;;
*)
echo "Usage: $0 [linux|windows]"
exit 1
;;
esac
fi
echo ""
echo -e "${GREEN}Build process completed.${NC}"
ls -lh "$DIST_DIR"

View File

@@ -1,161 +0,0 @@
#!/bin/bash
# Cross-compilation script for building Windows binaries on Linux
set -e
echo "RLogg - Cross-Platform Builder (Linux → All Platforms)"
echo "======================================================="
echo ""
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Extract version from Cargo.toml
VERSION=$(grep -m1 '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/')
echo "Version: $VERSION"
echo ""
# Create dist directory
DIST_DIR="dist"
mkdir -p "$DIST_DIR"
# Check if cross is installed
check_cross() {
if ! command -v cross &> /dev/null; then
echo -e "${YELLOW}Installing 'cross' for cross-compilation...${NC}"
cargo install cross --git https://github.com/cross-rs/cross
else
echo -e "${GREEN}✓ 'cross' is already installed${NC}"
fi
}
# Build using cross
build_with_cross() {
local target=$1
local name=$2
echo ""
echo -e "${BLUE}=== Building for $name ===${NC}"
echo -e "${YELLOW}Target: $target${NC}"
# Clean build artifacts to avoid GLIBC mismatch with build scripts
echo -e "${YELLOW}Cleaning build artifacts...${NC}"
cargo clean --release --target "$target"
if cross build --release --target "$target"; then
echo -e "${GREEN}✓ Build successful for $name${NC}"
# Copy binary to dist directory with version
if [[ "$target" == *"windows"* ]]; then
cp "target/$target/release/rlogg.exe" "$DIST_DIR/rlogg-$VERSION-$name.exe"
echo -e "${GREEN}✓ Binary: $DIST_DIR/rlogg-$VERSION-$name.exe${NC}"
else
cp "target/$target/release/rlogg" "$DIST_DIR/rlogg-$VERSION-$name"
strip "$DIST_DIR/rlogg-$VERSION-$name" 2>/dev/null || true
echo -e "${GREEN}✓ Binary: $DIST_DIR/rlogg-$VERSION-$name${NC}"
fi
return 0
else
echo -e "${RED}✗ Build failed for $name${NC}"
return 1
fi
}
# Build using regular cargo (for native Linux)
build_native() {
echo ""
echo -e "${BLUE}=== Building for Linux (native) ===${NC}"
if cargo build --release; then
echo -e "${GREEN}✓ Build successful for Linux${NC}"
cp "target/release/rlogg" "$DIST_DIR/rlogg-$VERSION-linux-x86_64"
strip "$DIST_DIR/rlogg-$VERSION-linux-x86_64" 2>/dev/null || true
echo -e "${GREEN}✓ Binary: $DIST_DIR/rlogg-$VERSION-linux-x86_64${NC}"
return 0
else
echo -e "${RED}✗ Build failed for Linux${NC}"
return 1
fi
}
# Main menu
show_menu() {
echo ""
echo "Select targets to build:"
echo " 1) Linux x86_64 (native)"
echo " 2) Windows x86_64 (cross-compile with MinGW)"
echo " 3) Both Linux and Windows"
echo " 4) Exit"
echo ""
read -p "Enter choice [1-4]: " choice
echo ""
}
# Parse command line arguments
if [ $# -eq 0 ]; then
# Interactive mode
while true; do
show_menu
case $choice in
1)
build_native
;;
2)
check_cross
build_with_cross "x86_64-pc-windows-gnu" "windows-x86_64"
;;
3)
build_native
check_cross
build_with_cross "x86_64-pc-windows-gnu" "windows-x86_64"
;;
4)
echo "Exiting..."
break
;;
*)
echo -e "${RED}Invalid choice${NC}"
;;
esac
done
else
# Command line mode
case "$1" in
linux)
build_native
;;
windows|win)
check_cross
build_with_cross "x86_64-pc-windows-gnu" "windows-x86_64"
;;
all)
build_native
check_cross
build_with_cross "x86_64-pc-windows-gnu" "windows-x86_64"
;;
*)
echo "Usage: $0 [linux|windows|all]"
echo ""
echo " linux - Build for Linux (native)"
echo " windows - Cross-compile for Windows (MinGW)"
echo " all - Build for both platforms"
echo ""
echo "Or run without arguments for interactive mode"
exit 1
;;
esac
fi
echo ""
echo -e "${GREEN}=== Build Complete ===${NC}"
if [ -d "$DIST_DIR" ] && [ "$(ls -A $DIST_DIR)" ]; then
echo "Binaries in '$DIST_DIR':"
ls -lh "$DIST_DIR"
else
echo "No binaries were built"
fi

301
packaging/README.md Normal file
View File

@@ -0,0 +1,301 @@
# Packaging Guide
This directory contains packaging files and installation scripts for RLogg on different platforms.
## Directory Structure
```
packaging/
├── linux/
│ ├── rlogg.desktop # XDG desktop entry file
│ ├── text-x-log.xml # MIME type definition for .log files
│ └── install.sh # Linux installation script
├── windows/
│ ├── file-association.reg # Windows registry file for manual setup
│ └── install.ps1 # PowerShell installation script
└── README.md # This file
```
## Building for Distribution
### Linux
1. **Build the release binary:**
```bash
cargo build --release
```
2. **Test the installation script:**
```bash
cd packaging/linux
./install.sh
```
3. **Create a distribution package:**
```bash
# Create a tarball
cd target/release
tar -czf rlogg-linux-x64.tar.gz rlogg ../../packaging/linux/*
```
### Windows
1. **Build the release binary:**
```powershell
cargo build --release
```
2. **Test the installation script:**
```powershell
cd packaging\windows
powershell -ExecutionPolicy Bypass -File install.ps1
```
3. **Create a distribution package:**
```powershell
# Create a ZIP file
Compress-Archive -Path target\release\rlogg.exe, packaging\windows\* -DestinationPath rlogg-windows-x64.zip
```
## Distribution Packages
### Linux Packages
#### DEB Package (Debian/Ubuntu)
Create a `debian/` directory structure for building `.deb` packages:
```bash
mkdir -p debian/usr/local/bin
mkdir -p debian/usr/share/applications
mkdir -p debian/usr/share/mime/packages
cp target/release/rlogg debian/usr/local/bin/
cp packaging/linux/rlogg.desktop debian/usr/share/applications/
cp packaging/linux/text-x-log.xml debian/usr/share/mime/packages/
# Create DEBIAN control file
mkdir -p debian/DEBIAN
cat > debian/DEBIAN/control <<EOF
Package: rlogg
Version: 0.3.1
Section: utils
Priority: optional
Architecture: amd64
Maintainer: Your Name <your.email@example.com>
Description: Fast log file viewer
A fast log file viewer with search, filtering, and highlighting capabilities
EOF
# Build the package
dpkg-deb --build debian rlogg_0.3.1_amd64.deb
```
#### AppImage
For universal Linux distribution:
```bash
# Install linuxdeploy
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
chmod +x linuxdeploy-x86_64.AppImage
# Create AppDir structure
mkdir -p AppDir/usr/bin
mkdir -p AppDir/usr/share/applications
mkdir -p AppDir/usr/share/mime/packages
cp target/release/rlogg AppDir/usr/bin/
cp packaging/linux/rlogg.desktop AppDir/usr/share/applications/
cp packaging/linux/text-x-log.xml AppDir/usr/share/mime/packages/
# Build AppImage
./linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage
```
### Windows Packages
#### NSIS Installer
Install NSIS and create an installer script:
```nsis
!define APPNAME "RLogg"
!define COMPANYNAME "Your Company"
!define DESCRIPTION "Fast log file viewer"
!define VERSIONMAJOR 0
!define VERSIONMINOR 3
!define VERSIONBUILD 1
RequestExecutionLevel user
Name "${APPNAME}"
Icon "path\to\icon.ico"
OutFile "rlogg-setup.exe"
InstallDir "$LOCALAPPDATA\Programs\${APPNAME}"
Section "Install"
SetOutPath $INSTDIR
File "target\release\rlogg.exe"
WriteUninstaller "$INSTDIR\Uninstall.exe"
# Create file association
WriteRegStr HKCU "Software\Classes\.log" "" "RLogg.LogFile"
WriteRegStr HKCU "Software\Classes\RLogg.LogFile\shell\open\command" "" '"$INSTDIR\rlogg.exe" "%1"'
SectionEnd
Section "Uninstall"
Delete "$INSTDIR\rlogg.exe"
Delete "$INSTDIR\Uninstall.exe"
RMDir "$INSTDIR"
DeleteRegKey HKCU "Software\Classes\.log"
DeleteRegKey HKCU "Software\Classes\RLogg.LogFile"
SectionEnd
```
## Testing File Associations
### Linux Testing
1. **Create a test log file:**
```bash
echo "Test log entry" > /tmp/test.log
```
2. **Test command-line opening:**
```bash
rlogg /tmp/test.log
```
3. **Test XDG opening:**
```bash
xdg-open /tmp/test.log
```
4. **Test in file manager:**
- Navigate to `/tmp/` in your file manager
- Double-click `test.log`
- Right-click → Open With → RLogg
5. **Test multiple files:**
```bash
echo "Log 1" > /tmp/test1.log
echo "Log 2" > /tmp/test2.log
rlogg /tmp/test1.log /tmp/test2.log
```
### Windows Testing
1. **Create a test log file:**
```powershell
"Test log entry" | Out-File -FilePath "$env:TEMP\test.log"
```
2. **Test command-line opening:**
```powershell
& "$env:LOCALAPPDATA\Programs\RLogg\rlogg.exe" "$env:TEMP\test.log"
```
3. **Test in File Explorer:**
- Open File Explorer
- Navigate to `%TEMP%`
- Double-click `test.log`
- Right-click → Open with → RLogg
4. **Test multiple files:**
```powershell
"Log 1" | Out-File -FilePath "$env:TEMP\test1.log"
"Log 2" | Out-File -FilePath "$env:TEMP\test2.log"
& "$env:LOCALAPPDATA\Programs\RLogg\rlogg.exe" "$env:TEMP\test1.log" "$env:TEMP\test2.log"
```
## Platform-Specific Notes
### Linux
- **Desktop environments tested:**
- GNOME 40+
- KDE Plasma 5.20+
- XFCE 4.16+
- i3/sway (via xdg-utils)
- **Dependencies:**
- `xdg-utils` for `update-desktop-database` and `update-mime-database`
- Usually pre-installed on most distributions
- **Wayland compatibility:**
- Tested and working on Wayland sessions
- File associations work the same as X11
### Windows
- **Tested on:**
- Windows 10 (version 1909+)
- Windows 11
- **Registry location:**
- User-local: `HKEY_CURRENT_USER\Software\Classes\`
- No admin rights required
- **Antivirus:**
- Some antivirus software may flag unsigned executables
- Consider code signing for production releases
## CI/CD Integration
### GitHub Actions Example
```yaml
name: Build and Package
on:
release:
types: [created]
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
run: cargo build --release
- name: Package
run: |
cd target/release
tar -czf rlogg-linux-x64.tar.gz rlogg ../../packaging/linux/*
- name: Upload
uses: actions/upload-artifact@v3
with:
name: rlogg-linux
path: target/release/rlogg-linux-x64.tar.gz
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Build
run: cargo build --release
- name: Package
run: |
Compress-Archive -Path target\release\rlogg.exe, packaging\windows\* -DestinationPath rlogg-windows-x64.zip
- name: Upload
uses: actions/upload-artifact@v3
with:
name: rlogg-windows
path: rlogg-windows-x64.zip
```
## Troubleshooting
See [docs/FILE_ASSOCIATIONS.md](../docs/FILE_ASSOCIATIONS.md) for detailed troubleshooting guides.
## Contributing
When adding new packaging formats or improving existing ones:
1. Test on the target platform
2. Update this README with clear instructions
3. Add troubleshooting steps if needed
4. Submit a pull request with test results

78
packaging/linux/install.sh Executable file
View File

@@ -0,0 +1,78 @@
#!/bin/bash
# Install rlogg and create file association for .log files
# This script installs to user-local directories (~/.local/) and does not require sudo
set -e
echo "Installing RLogg Log Viewer..."
echo ""
# Determine the directory where this script is located
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Check if rlogg binary exists
if [ ! -f "$SCRIPT_DIR/../../target/release/rlogg" ]; then
echo "Error: rlogg binary not found at $SCRIPT_DIR/../../target/release/rlogg"
echo "Please build the project first with: cargo build --release"
exit 1
fi
# User-local installation (no sudo required)
echo "Installing binary to ~/.local/bin/..."
mkdir -p ~/.local/bin
cp "$SCRIPT_DIR/../../target/release/rlogg" ~/.local/bin/
chmod +x ~/.local/bin/rlogg
# Install desktop file with correct path
echo "Installing desktop file..."
mkdir -p ~/.local/share/applications
# Create desktop file with absolute path to the binary
cat > ~/.local/share/applications/rlogg.desktop <<EOF
[Desktop Entry]
Version=1.0
Type=Application
Name=RLogg
GenericName=Log Viewer
Comment=Fast log file viewer with search, filtering, and highlighting
Exec=$HOME/.local/bin/rlogg %F
Icon=rlogg
Terminal=false
Categories=Development;Utility;
MimeType=text/x-log;
Keywords=log;viewer;search;filter;
EOF
# Update MIME database
echo "Installing MIME type definition..."
mkdir -p ~/.local/share/mime/packages
cp "$SCRIPT_DIR/text-x-log.xml" ~/.local/share/mime/packages/
# Update desktop and MIME databases
echo "Updating databases..."
if command -v update-desktop-database &> /dev/null; then
update-desktop-database ~/.local/share/applications 2>/dev/null || true
fi
if command -v update-mime-database &> /dev/null; then
update-mime-database ~/.local/share/mime 2>/dev/null || true
fi
echo ""
echo "Installation complete!"
echo ""
echo "RLogg has been installed to: ~/.local/bin/rlogg"
echo ""
echo "IMPORTANT: Make sure ~/.local/bin is in your PATH."
echo "Add this line to your ~/.bashrc or ~/.zshrc if it's not already there:"
echo ' export PATH="$HOME/.local/bin:$PATH"'
echo ""
echo "File associations have been configured for .log files."
echo "You may need to log out and log back in for file associations to take effect."
echo ""
echo "To uninstall, run:"
echo " rm ~/.local/bin/rlogg"
echo " rm ~/.local/share/applications/rlogg.desktop"
echo " rm ~/.local/share/mime/packages/text-x-log.xml"
echo " update-desktop-database ~/.local/share/applications"
echo " update-mime-database ~/.local/share/mime"

View File

@@ -0,0 +1,14 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=RLogg
GenericName=Log Viewer
Comment=Fast log file viewer with search, filtering, and highlighting
# NOTE: Use absolute path for Exec to work in GUI file managers
# The install.sh script will automatically set this to $HOME/.local/bin/rlogg
Exec=/usr/local/bin/rlogg %F
Icon=rlogg
Terminal=false
Categories=Development;Utility;
MimeType=text/x-log;
Keywords=log;viewer;search;filter;

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="text/x-log">
<comment>Log file</comment>
<glob pattern="*.log"/>
</mime-type>
</mime-info>

View File

@@ -0,0 +1,19 @@
Windows Registry Editor Version 5.00
; Associate .log files with RLogg
; Edit the path below if you installed RLogg to a different location
[HKEY_CURRENT_USER\Software\Classes\.log]
@="RLogg.LogFile"
[HKEY_CURRENT_USER\Software\Classes\RLogg.LogFile]
@="Log File"
[HKEY_CURRENT_USER\Software\Classes\RLogg.LogFile\DefaultIcon]
@="\"C:\\Program Files\\RLogg\\rlogg.exe\",0"
[HKEY_CURRENT_USER\Software\Classes\RLogg.LogFile\shell\open\command]
@="\"C:\\Program Files\\RLogg\\rlogg.exe\" \"%1\""
[HKEY_CURRENT_USER\Software\Classes\RLogg.LogFile\shell\open]
@="Open with RLogg"

View File

@@ -0,0 +1,63 @@
# RLogg Installation Script for Windows
# Run with: powershell -ExecutionPolicy Bypass -File install.ps1
param(
[string]$InstallPath = "$env:LOCALAPPDATA\Programs\RLogg"
)
Write-Host "Installing RLogg Log Viewer..." -ForegroundColor Cyan
Write-Host ""
# Determine the directory where this script is located
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$BinaryPath = Join-Path (Split-Path (Split-Path $ScriptDir -Parent) -Parent) "target\release\rlogg.exe"
# Check if rlogg.exe exists
if (-not (Test-Path $BinaryPath)) {
Write-Host "Error: rlogg.exe not found at $BinaryPath" -ForegroundColor Red
Write-Host "Please build the project first with: cargo build --release" -ForegroundColor Yellow
exit 1
}
# Create directory
Write-Host "Installing binary to $InstallPath..." -ForegroundColor Green
New-Item -ItemType Directory -Force -Path $InstallPath | Out-Null
# Copy binary
Copy-Item $BinaryPath $InstallPath\
# Create registry entries for .log file association
Write-Host "Configuring file associations..." -ForegroundColor Green
$regPath = "HKCU:\Software\Classes\.log"
New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name "(default)" -Value "RLogg.LogFile"
$regPath = "HKCU:\Software\Classes\RLogg.LogFile"
New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name "(default)" -Value "Log File"
$regPath = "HKCU:\Software\Classes\RLogg.LogFile\DefaultIcon"
New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name "(default)" -Value "`"$InstallPath\rlogg.exe`",0"
$regPath = "HKCU:\Software\Classes\RLogg.LogFile\shell\open\command"
New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name "(default)" -Value "`"$InstallPath\rlogg.exe`" `"%1`""
$regPath = "HKCU:\Software\Classes\RLogg.LogFile\shell\open"
New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name "(default)" -Value "Open with RLogg"
Write-Host ""
Write-Host "Installation complete!" -ForegroundColor Green
Write-Host ""
Write-Host "RLogg is installed at: $InstallPath\rlogg.exe" -ForegroundColor Cyan
Write-Host "File associations have been configured for .log files." -ForegroundColor Cyan
Write-Host ""
Write-Host "You can now double-click .log files to open them in RLogg." -ForegroundColor Yellow
Write-Host ""
Write-Host "To uninstall, run:" -ForegroundColor Yellow
Write-Host " Remove-Item -Recurse `"$InstallPath`"" -ForegroundColor Gray
Write-Host " Remove-Item -Recurse HKCU:\Software\Classes\.log" -ForegroundColor Gray
Write-Host " Remove-Item -Recurse HKCU:\Software\Classes\RLogg.LogFile" -ForegroundColor Gray

View File

@@ -18,6 +18,7 @@ pub struct LogViewerApp {
highlight_manager: HighlightManager,
first_frame: bool,
file_open_errors: Vec<String>,
file_receiver: Option<std::sync::mpsc::Receiver<Vec<PathBuf>>>,
}
struct KeyAction {
@@ -36,7 +37,7 @@ impl KeyAction {
}
impl LogViewerApp {
pub fn new(config: AppConfig, initial_files: Vec<PathBuf>) -> Self {
pub fn new(config: AppConfig, initial_files: Vec<PathBuf>, file_receiver: Option<std::sync::mpsc::Receiver<Vec<PathBuf>>>) -> Self {
let indexing_state = IndexingState::new();
let mut tabs = Vec::new();
let mut file_open_errors = Vec::new();
@@ -76,6 +77,7 @@ impl LogViewerApp {
highlight_manager: HighlightManager::new(config.highlight_rules),
first_frame: true,
file_open_errors,
file_receiver,
}
}
@@ -317,6 +319,35 @@ fn handle_key_inputs(ctx: &egui::Context) -> KeyAction {
impl eframe::App for LogViewerApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// Poll for new files from other instances
if let Some(receiver) = &self.file_receiver {
while let Ok(files) = receiver.try_recv() {
for file_path in files {
if file_path.exists() {
if file_path.is_file() {
if let Some(tab) = open_file(file_path.clone(), &self.indexing_state) {
self.tabs.push(tab);
self.active_tab_index = self.tabs.len() - 1;
} else {
// We probably don't want to disrupt the user with a modal for this background action,
// but maybe we could log it or flash a message. For now, let's add it to errors.
self.file_open_errors.push(format!("Failed to open: {}", file_path.display()));
}
}
}
}
// If we received files and opened them, we should request a repaint to show the new tabs immediately
ctx.request_repaint();
// If there were errors, we might want to show the error window again?
// The error window logic is in handle_first_frame/update, strictly speaking it only shows if errors exist.
// However, handle_first_frame is only called once. We should check if we should show errors here or if the existing logic handles it.
// The existing logic checks !self.file_open_errors.is_empty() in handle_first_frame, but checking lines 275-298 reveals
// that the window display code is INSIDE handle_first_frame? No, wait.
// Let's check handle_first_frame implementation again.
}
}
self.handle_first_frame(ctx);
// Update search results if available

View File

@@ -18,6 +18,10 @@ use std::path::PathBuf;
use crate::log_viewer_app::LogViewerApp;
use config::AppConfig;
use interprocess::local_socket::{LocalSocketListener, LocalSocketStream};
use std::io::{prelude::*, BufReader};
use std::sync::mpsc;
use std::thread;
#[derive(Parser)]
#[command(name = "rlogg")]
@@ -32,6 +36,62 @@ fn main() -> eframe::Result {
// Parse command-line arguments
let cli = Cli::parse();
// Single instance check
#[cfg(windows)]
let name = "rlogg_ipc";
#[cfg(not(windows))]
let name = "/tmp/rlogg.sock";
// Try to connect to existing instance
if let Ok(mut conn) = LocalSocketStream::connect(name) {
if !cli.files.is_empty() {
let files_json = serde_json::to_string(&cli.files).unwrap();
if let Err(e) = conn.write_all(files_json.as_bytes()) {
eprintln!("Failed to send files to existing instance: {}", e);
}
}
return Ok(());
}
// If connection failed, we are the first instance.
// Create a listener.
// Cleanup socket on Unix
#[cfg(not(windows))]
if std::path::Path::new(name).exists() {
std::fs::remove_file(name).ok();
}
let listener = match LocalSocketListener::bind(name) {
Ok(l) => l,
Err(error) => {
eprintln!("Failed to bind to socket: {}", error);
// Fallback to running without single-instance support if binding fails
return run_app(cli, None);
}
};
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
for mut conn in listener.incoming().filter_map(|x| x.ok()) {
let tx = tx.clone();
thread::spawn(move || {
let mut reader = BufReader::new(&mut conn);
let mut buffer = String::new();
// We assume one connection per batch of files, validation is relaxed for simplicity
if let Ok(_) = reader.read_to_string(&mut buffer) {
if let Ok(files) = serde_json::from_str::<Vec<PathBuf>>(&buffer) {
tx.send(files).ok();
}
}
});
}
});
run_app(cli, Some(rx))
}
fn run_app(cli: Cli, file_receiver: Option<mpsc::Receiver<Vec<PathBuf>>>) -> eframe::Result {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([1200.0, 800.0])
@@ -49,9 +109,7 @@ fn main() -> eframe::Result {
options,
Box::new(move |cc| {
theme::apply_theme(&cc.egui_ctx);
Ok(Box::new(LogViewerApp::new(config, initial_files)))
Ok(Box::new(LogViewerApp::new(config, initial_files, file_receiver)))
}),
)
}