migration start
Some checks failed
CI - Multi-Platform Native / Build iOS (RSSuper) (push) Has been cancelled
CI - Multi-Platform Native / Build macOS (push) Has been cancelled
CI - Multi-Platform Native / Build Android (push) Has been cancelled
CI - Multi-Platform Native / Build Linux (push) Has been cancelled
CI - Multi-Platform Native / Build Summary (push) Has been cancelled

This commit is contained in:
2026-03-29 14:12:17 -04:00
parent af87f9f571
commit d346b527e6
51 changed files with 4476 additions and 69 deletions

422
scripts/build-android.sh Executable file
View File

@@ -0,0 +1,422 @@
#!/bin/bash
# RSSuper Android Build Script
# Native Android build using Gradle
set -e
# Configuration
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
ANDROID_PROJECT_DIR="$PROJECT_ROOT/native-route/android"
# Default values
VARIANT="${1:-debug}"
ACTION="${2:-assemble}"
# Show help
show_help() {
echo "RSSuper Android Build Script"
echo ""
echo "Usage: $0 [VARIANT] [ACTION]"
echo ""
echo "Arguments:"
echo " VARIANT Build variant (debug|release) [Default: debug]"
echo " ACTION Gradle action (assemble|build|test|clean) [Default: assemble]"
echo ""
echo "Examples:"
echo " $0 # Assemble debug APK"
echo " $0 release # Assemble release APK"
echo " $0 debug test # Run tests"
echo " $0 clean # Clean build"
}
# Check prerequisites
check_prerequisites() {
if [ ! -d "$ANDROID_PROJECT_DIR" ]; then
echo "Error: Android project directory not found: $ANDROID_PROJECT_DIR"
echo "Creating basic Android project structure..."
create_android_project
return 0
fi
if [ ! -f "$ANDROID_PROJECT_DIR/build.gradle.kts" ] && [ ! -f "$ANDROID_PROJECT_DIR/build.gradle" ]; then
echo "Warning: No build.gradle found. Creating basic Android project..."
create_android_project
fi
# Check for Java/JDK
if ! command -v java &> /dev/null; then
echo "Error: Java not found. Please install JDK 17."
exit 1
fi
echo "Java version:"
java -version
}
# Create basic Android project structure
create_android_project() {
echo "Creating Android project structure..."
mkdir -p "$ANDROID_PROJECT_DIR/app/src/main/java/com/mikefreno/rssuper"
mkdir -p "$ANDROID_PROJECT_DIR/app/src/main/res/values"
mkdir -p "$ANDROID_PROJECT_DIR/app/src/main/res/mipmap-hdpi"
mkdir -p "$ANDROID_PROJECT_DIR/gradle/wrapper"
# Root build.gradle.kts
cat > "$ANDROID_PROJECT_DIR/build.gradle.kts" << 'EOF'
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.2.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
}
allprojects {
repositories {
google()
mavenCentral()
}
}
task<Delete>("clean") {
delete(rootProject.buildDir)
}
EOF
# settings.gradle.kts
cat > "$ANDROID_PROJECT_DIR/settings.gradle.kts" << 'EOF'
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "RSSuper"
include(":app")
EOF
# app/build.gradle.kts
cat > "$ANDROID_PROJECT_DIR/app/build.gradle.kts" << 'EOF'
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "com.mikefreno.rssuper"
compileSdk = 34
defaultConfig {
applicationId = "com.mikefreno.rssuper"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
}
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
// Navigation
implementation("androidx.navigation:navigation-fragment-ktx:2.7.6")
implementation("androidx.navigation:navigation-ui-ktx:2.7.6")
// Lifecycle
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
// Networking
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// Room Database
implementation("androidx.room:room-runtime:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
EOF
# gradle.properties
cat > "$ANDROID_PROJECT_DIR/gradle.properties" << 'EOF'
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
android.useAndroidX=true
kotlin.code.style=official
android.nonTransitiveRClass=true
EOF
# gradle wrapper properties
cat > "$ANDROID_PROJECT_DIR/gradle/wrapper/gradle-wrapper.properties" << 'EOF'
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
EOF
# Main Activity
cat > "$ANDROID_PROJECT_DIR/app/src/main/java/com/mikefreno/rssuper/MainActivity.kt" << 'EOF'
package com.mikefreno.rssuper
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.mikefreno.rssuper.ui.theme.RSSuperTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
RSSuperTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting("Android")
}
}
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "RSSuper - $name",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
RSSuperTheme {
Greeting("Android")
}
}
EOF
# Theme
mkdir -p "$ANDROID_PROJECT_DIR/app/src/main/java/com/mikefreno/rssuper/ui/theme"
cat > "$ANDROID_PROJECT_DIR/app/src/main/java/com/mikefreno/rssuper/ui/theme/Theme.kt" << 'EOF'
package com.mikefreno.rssuper.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Color(0xFF90CAF9),
secondary = Color(0xFF81D4FA),
tertiary = Color(0xFFA5D6A7)
)
private val LightColorScheme = lightColorScheme(
primary = Color(0xFF1976D2),
secondary = Color(0xFF03A9F4),
tertiary = Color(0xFF4CAF50)
)
@Composable
fun RSSuperTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
content = content
)
}
EOF
# AndroidManifest.xml
cat > "$ANDROID_PROJECT_DIR/app/src/main/AndroidManifest.xml" << 'EOF'
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="RSSuper"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.RSSuper">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.RSSuper">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
EOF
# styles.xml
cat > "$ANDROID_PROJECT_DIR/app/src/main/res/values/styles.xml" << 'EOF'
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.RSSuper" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
EOF
# proguard-rules.pro
cat > "$ANDROID_PROJECT_DIR/app/proguard-rules.pro" << 'EOF'
# Add project specific ProGuard rules here.
-keepattributes Signature
-keepattributes *Annotation*
-keepattributes RuntimeVisibleAnnotations
-keepattributes RuntimeInvisibleAnnotations
EOF
# gradlew script
cat > "$ANDROID_PROJECT_DIR/gradlew" << 'EOF'
#!/bin/bash
# Gradle wrapper placeholder
# In a real project, this would be generated by gradle wrapper
# For now, we'll use system gradle if available
if command -v gradle &> /dev/null; then
exec gradle "$@"
else
echo "Error: Gradle not found. Please install Gradle or use ./gradlew"
exit 1
fi
EOF
chmod +x "$ANDROID_PROJECT_DIR/gradlew"
echo "Android project structure created!"
}
# Build the project
build_project() {
echo "Building Android $VARIANT..."
cd "$ANDROID_PROJECT_DIR"
# Determine gradle task based on variant
local TASK="${ACTION}${VARIANT}""
if [ "$VARIANT" = "release" ]; then
TASK="${ACTION}Release"
fi
echo "Running: ./gradlew $TASK"
./gradlew $TASK
}
# Run tests
run_tests() {
echo "Running Android tests..."
cd "$ANDROID_PROJECT_DIR"
./gradlew test
}
# Clean build
clean_build() {
echo "Cleaning Android build..."
cd "$ANDROID_PROJECT_DIR"
./gradlew clean
}
# Main
case "$ACTION" in
clean)
check_prerequisites
clean_build
;;
test)
check_prerequisites
run_tests
;;
assemble|build|*)
check_prerequisites
build_project
;;
esac

174
scripts/build-ios.sh Executable file
View File

@@ -0,0 +1,174 @@
#!/bin/bash
# RSSuper iOS/macOS Build Script
# Adapted from Nessa build scripts
set -e
# Configuration
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
IOS_PROJECT_DIR="$PROJECT_ROOT/native-route/ios/RSSuper"
IOS_PROJECT="$IOS_PROJECT_DIR/RSSuper.xcodeproj"
IOS_SCHEME="RSSuper"
# Default values
CONFIGURATION="${1:-Debug}"
PLATFORM="${2:-iOS}"
DESTINATION="${3:-generic/platform=iOS}"
ACTION="${4:-build}"
# Show help
show_help() {
echo "RSSuper iOS/macOS Build Script"
echo ""
echo "Usage: $0 [CONFIGURATION] [PLATFORM] [DESTINATION] [ACTION]"
echo ""
echo "Arguments:"
echo " CONFIGURATION Build configuration (Debug|Release) [Default: Debug]"
echo " PLATFORM Target platform (iOS|macOS) [Default: iOS]"
echo " DESTINATION xcodebuild destination [Default: generic/platform=iOS]"
echo " ACTION Action to perform (build|test|clean) [Default: build]"
echo ""
echo "Examples:"
echo " $0 # Build Debug for iOS"
echo " $0 Release # Build Release for iOS"
echo " $0 Debug iOS 'platform=iOS Simulator,name=iPhone 15' # Build for simulator"
echo " $0 clean # Clean build"
}
# Check if xcodebuild is available
check_prerequisites() {
if ! command -v xcodebuild &> /dev/null; then
echo "Error: xcodebuild not found. Please install Xcode."
exit 1
fi
if [ ! -d "$IOS_PROJECT_DIR" ]; then
echo "Error: iOS project directory not found: $IOS_PROJECT_DIR"
exit 1
fi
if [ ! -f "$IOS_PROJECT/project.pbxproj" ]; then
echo "Error: Xcode project not found: $IOS_PROJECT"
exit 1
fi
}
# Select appropriate Xcode version
select_xcode() {
echo "Checking Xcode installation..."
echo "=== Current xcode-select path ==="
xcode-select -p 2>/dev/null || echo "xcode-select failed"
echo "=== Current Xcode version ==="
xcodebuild -version 2>/dev/null || echo "xcodebuild failed"
# Try common Xcode paths in order of preference
for path in \
"/Applications/Xcode_16.3.app/Contents/Developer" \
"/Applications/Xcode_16.2.app/Contents/Developer" \
"/Applications/Xcode_16.1.app/Contents/Developer" \
"/Applications/Xcode_16.0.app/Contents/Developer" \
"/Applications/Xcode_15.4.app/Contents/Developer" \
"/Applications/Xcode_15.3.app/Contents/Developer" \
"/Applications/Xcode_15.2.app/Contents/Developer" \
"/Applications/Xcode_15.1.app/Contents/Developer" \
"/Applications/Xcode_15.0.app/Contents/Developer" \
"/Applications/Xcode.app/Contents/Developer"
do
if [ -d "$path" ]; then
CURRENT_PATH=$(xcode-select -p 2>/dev/null || echo "")
if [ "$CURRENT_PATH" != "$path" ]; then
echo "Switching Xcode to: $path"
xcode-select -s "$path" || true
else
echo "Xcode already selected at: $path"
fi
break
fi
done
echo "=== Selected Xcode ==="
xcodebuild -version
}
# Clean build artifacts
clean_build() {
echo "Cleaning build artifacts..."
cd "$IOS_PROJECT_DIR"
xcodebuild clean \
-project "$IOS_PROJECT" \
-scheme "$IOS_SCHEME" \
-configuration "$CONFIGURATION" \
-destination "$DESTINATION"
echo "Clean completed"
}
# Build the project
build_project() {
echo "Building $PLATFORM $CONFIGURATION..."
cd "$IOS_PROJECT_DIR"
# Capture build output
BUILD_LOG=$(mktemp)
set +e
xcodebuild $ACTION \
-project "$IOS_PROJECT" \
-scheme "$IOS_SCHEME" \
-configuration "$CONFIGURATION" \
-destination "$DESTINATION" \
> "$BUILD_LOG" 2>&1
BUILD_EXIT=$?
set -e
# Show build output
cat "$BUILD_LOG"
if [ "$BUILD_EXIT" = "0" ]; then
echo "Build completed successfully!"
return 0
else
echo "Build failed with exit code: $BUILD_EXIT"
# Extract and show errors
echo ""
echo "Errors found:"
grep -E "\.swift:[0-9]+:[0-9]+: error:" "$BUILD_LOG" | head -10 || true
return $BUILD_EXIT
fi
}
# Run tests
run_tests() {
echo "Running tests for $PLATFORM..."
cd "$IOS_PROJECT_DIR"
xcodebuild test \
-project "$IOS_PROJECT" \
-scheme "$IOS_SCHEME" \
-configuration "$CONFIGURATION" \
-destination "$DESTINATION" \
ONLY_ACTIVE_ARCH=NO
}
# Main
case "$ACTION" in
clean)
check_prerequisites
select_xcode
clean_build
;;
test)
check_prerequisites
select_xcode
run_tests
;;
build|*)
check_prerequisites
select_xcode
build_project
;;
esac

449
scripts/build-linux.sh Executable file
View File

@@ -0,0 +1,449 @@
#!/bin/bash
# RSSuper Linux Build Script
# Native Linux build using meson/ninja with GTK4
set -e
# Configuration
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
LINUX_PROJECT_DIR="$PROJECT_ROOT/native-route/linux"
# Default values
BUILD_TYPE="${1:-debug}"
ACTION="${2:-build}"
# Show help
show_help() {
echo "RSSuper Linux Build Script"
echo ""
echo "Usage: $0 [BUILD_TYPE] [ACTION]"
echo ""
echo "Arguments:"
echo " BUILD_TYPE Build type (debug|release) [Default: debug]"
echo " ACTION Action (build|install|test|clean|setup) [Default: build]"
echo ""
echo "Examples:"
echo " $0 # Build debug"
echo " $0 release # Build release"
echo " $0 debug setup # Setup build environment"
echo " $0 debug test # Run tests"
echo " $0 clean # Clean build"
}
# Check prerequisites
check_prerequisites() {
local MISSING_DEPS=()
# Check for required tools
for cmd in meson ninja pkg-config; do
if ! command -v $cmd &> /dev/null; then
MISSING_DEPS+=("$cmd")
fi
done
if [ ${#MISSING_DEPS[@]} -gt 0 ]; then
echo "Error: Missing build tools: ${MISSING_DEPS[*]}"
echo ""
echo "On Ubuntu/Debian:"
echo " sudo apt install meson ninja-build pkg-config"
echo ""
echo "On Fedora:"
echo " sudo dnf install meson ninja-build pkgconf-pkg-config"
echo ""
echo "On Arch:"
echo " sudo pacman -S meson ninja pkgconf"
exit 1
fi
echo "Build tools check passed"
}
# Install development dependencies
install_dependencies() {
echo "Installing Linux development dependencies..."
local DISTRO=$(lsb_release -i -s 2>/dev/null || echo "unknown")
case "$DISTRO" in
Ubuntu|Debian)
sudo apt-get update
sudo apt-get install -y \
build-essential \
meson \
ninja-build \
pkg-config \
libgtk-4-dev \
libadwaita-1-dev \
libsqlite3-dev \
libxml2-dev \
libcurl4-openssl-dev \
libsoup-3-dev \
valac
;;
Fedora)
sudo dnf install -y \
gcc \
gcc-c++ \
meson \
ninja-build \
pkgconf-pkg-config \
gtk4-devel \
libadwaita-1-devel \
sqlite-devel \
libxml2-devel \
libcurl-devel \
libsoup3-devel \
vala
;;
arch)
sudo pacman -S --noconfirm \
base-devel \
meson \
ninja \
pkgconf \
gtk4 \
libadwaita \
sqlite \
libxml2 \
curl \
libsoup3 \
vala
;;
*)
echo "Warning: Unknown distribution ($DISTRO). Please install dependencies manually:"
echo " - meson, ninja-build, pkg-config"
echo " - GTK4 development files"
echo " - SQLite3 development files"
echo " - libxml2 development files"
echo " - curl development files"
;;
esac
echo "Dependencies installation complete"
}
# Create Linux project structure
create_linux_project() {
echo "Creating Linux project structure..."
mkdir -p "$LINUX_PROJECT_DIR/src/main"
mkdir -p "$LINUX_PROJECT_DIR/src/models"
mkdir -p "$LINUX_PROJECT_DIR/src/services"
mkdir -p "$LINUX_PROJECT_DIR/src/ui"
mkdir -p "$LINUX_PROJECT_DIR/data"
mkdir -p "$LINUX_PROJECT_DIR/build"
# meson.build (root)
cat > "$LINUX_PROJECT_DIR/meson.build" << 'EOF'
project('rssuper', 'c', 'vala',
version: '1.0.0',
meson_version: '>= 1.0.0',
license: 'MIT',
default_options: [
'warning_level=1',
'werror=false'
]
)
# Dependencies
gtk4 = dependency('gtk4', version: '>= 4.0')
adwaita = dependency('libadwaita-1', version: '>= 1.0')
sqlite = dependency('sqlite3')
xml2 = dependency('libxml-2.0')
curl = dependency('libcurl')
soup = dependency('libsoup-3.0')
glib = dependency('glib-2.0', version: '>= 2.70')
gobject = dependency('gobject-2.0')
# Common compiler and linker arguments
c_args = ['-D_GLIBCXX_USE_C99=1']
l_args = []
# Sources
main_sources = files(
'src/main/rssuper.c',
'src/main/window.c',
)
model_sources = files(
'src/models/feed.vala',
'src/models/item.vala',
'src/models/database.vala',
)
service_sources = files(
'src/services/rss-parser.vala',
'src/services/feed-fetcher.vala',
)
ui_sources = files(
'src/ui/feed-list.vala',
'src/ui/item-list.vala',
'src/ui/item-view.vala',
)
# Executable
rssuper_exe = executable(
'rssuper',
main_sources,
model_sources,
service_sources,
ui_sources,
dependencies: [
gtk4,
adwaita,
sqlite,
xml2,
curl,
soup,
glib,
gobject,
],
c_args: c_args,
link_args: l_args,
install: true,
)
# Data files
install_data(
'data/rssuper.desktop',
install_dir: get_option('datadir') + '/share/applications'
)
install_data(
'data/rssuper.svg',
install_dir: get_option('datadir') + '/share/icons/hicolor/scalable/apps'
)
# Configuration
configure_file(
input: 'config.h.in',
output: 'config.h',
configuration: meson.current_source_dir() / 'config.cfg'
)
EOF
# Main C file
cat > "$LINUX_PROJECT_DIR/src/main/rssuper.c" << 'EOF'
#include <gtk/gtk.h>
#include <adwaita.h>
#include "config.h"
static GtkApplication *app;
static void
activate(GtkApplication *application)
{
AdwWindow *window;
window = g_object_new(ADW_TYPE_WINDOW,
"title", "RSSuper",
"default-width", 1200,
"default-height", 800,
"show-mnemonic-label", false,
NULL);
gtk_application_add_window(GTK_APPLICATION(application), GTK_WINDOW(window));
gtk_widget_realize(GTK_WIDGET(window));
gtk_widget_show(GTK_WIDGET(window));
}
int
main(int argc, char **argv)
{
app = gtk_application_new("com.mikefreno.RSSuper",
G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect(app, "activate",
G_CALLBACK(activate), NULL);
int status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
EOF
# Desktop file
cat > "$LINUX_PROJECT_DIR/data/rssuper.desktop" << 'EOF'
[Desktop Entry]
Name=RSSuper
Comment=A native RSS reader for Linux
Exec=rssuper
Terminal=false
Type=Application
Icon=rssuper
Categories=Network;Reader;
Keywords=rss;feed;reader;
EOF
# SVG Icon placeholder
cat > "$LINUX_PROJECT_DIR/data/rssuper.svg" << 'EOF'
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256">
<rect width="256" height="256" fill="#1976D2" rx="32"/>
<text x="50%" y="50%" font-size="128" text-anchor="middle"
dominant-baseline="central" fill="white" font-family="sans-serif">
R
</text>
</svg>
EOF
# Config template
cat > "$LINUX_PROJECT_DIR/config.h.in" << 'EOF'
#ifndef CONFIG_H
#define CONFIG_H
#define RSSUPER_VERSION "@PROJECT_VERSION@"
#define RSSUPER_APP_ID "com.mikefreno.RSSuper"
#endif /* CONFIG_H */
EOF
echo "Linux project structure created!"
}
# Setup build directory
setup_build() {
echo "Setting up build directory..."
cd "$LINUX_PROJECT_DIR"
local BUILD_DIR="build"
local BUILD_TYPE_FLAG=""
if [ "$BUILD_TYPE" = "release" ]; then
BUILD_TYPE_FLAG="--buildtype=release"
else
BUILD_TYPE_FLAG="--buildtype=debug"
fi
# Clean existing build directory
if [ -d "$BUILD_DIR" ]; then
rm -rf "$BUILD_DIR"
fi
meson setup "$BUILD_DIR" $BUILD_TYPE_FLAG
echo "Build directory setup complete"
}
# Build the project
build_project() {
echo "Building Linux $BUILD_TYPE..."
cd "$LINUX_PROJECT_DIR"
# Check if build directory exists
if [ ! -d "build" ]; then
echo "Build directory not found. Running setup first..."
setup_build
fi
meson compile -C build
echo "Build complete! Executable: $LINUX_PROJECT_DIR/build/rssuper"
}
# Install the application
install_app() {
echo "Installing RSSuper..."
cd "$LINUX_PROJECT_DIR"
if [ ! -d "build" ]; then
echo "Error: Build directory not found. Run build first."
exit 1
fi
sudo meson install -C build
echo "Installation complete!"
}
# Run tests
run_tests() {
echo "Running Linux tests..."
cd "$LINUX_PROJECT_DIR"
if [ ! -d "build" ]; then
echo "Build directory not found. Running setup first..."
setup_build
fi
meson test -C build
}
# Clean build
clean_build() {
echo "Cleaning Linux build..."
cd "$LINUX_PROJECT_DIR"
if [ -d "build" ]; then
rm -rf build
echo "Build directory removed"
fi
}
# Run the application
run_app() {
echo "Running RSSuper..."
cd "$LINUX_PROJECT_DIR"
if [ ! -f "build/rssuper" ]; then
echo "Executable not found. Building first..."
build_project
fi
./build/rssuper
}
# Main
case "$ACTION" in
setup)
check_prerequisites
if [ ! -d "$LINUX_PROJECT_DIR/src" ]; then
create_linux_project
fi
setup_build
;;
install-deps)
install_dependencies
;;
clean)
clean_build
;;
test)
check_prerequisites
if [ ! -d "$LINUX_PROJECT_DIR/src" ]; then
create_linux_project
fi
setup_build
run_tests
;;
run)
check_prerequisites
if [ ! -d "$LINUX_PROJECT_DIR/src" ]; then
create_linux_project
fi
setup_build
build_project
run_app
;;
install)
check_prerequisites
build_project
install_app
;;
build|*)
check_prerequisites
if [ ! -d "$LINUX_PROJECT_DIR/src" ]; then
create_linux_project
fi
setup_build
build_project
;;
esac

279
scripts/build.sh Executable file
View File

@@ -0,0 +1,279 @@
#!/bin/bash
# RSSuper Multi-Platform Build Script
# Main entry point for building all platforms
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Source common utilities
source "$SCRIPT_DIR/common.sh"
# Default values
PLATFORMS="all"
BUILD_TYPE="debug"
ACTION="build"
RUN_TESTS=false
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Show help
show_help() {
cat << EOF
RSSuper Multi-Platform Build System
Usage: $0 [OPTIONS]
Options:
-p, --platforms PLATFORMS Comma-separated platforms (ios,android,linux) or 'all' [Default: all]
-t, --type TYPE Build type (debug|release) [Default: debug]
-a, --action ACTION Action to perform (build|test|clean|install) [Default: build]
--test Run tests after build
-h, --help Show this help message
Platforms:
ios iOS/macOS (requires macOS)
android Android
linux Linux (GTK4)
Examples:
$0 # Build all platforms in debug mode
$0 -p ios,android -t release # Build iOS and Android in release mode
$0 -p linux --test # Build and test Linux version
$0 -a clean # Clean all platforms
$0 -p ios -a install # Install iOS app to simulator
EOF
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-p|--platforms)
PLATFORMS="$2"
shift 2
;;
-t|--type)
BUILD_TYPE="$2"
shift 2
;;
-a|--action)
ACTION="$2"
shift 2
;;
--test)
RUN_TESTS=true
shift
;;
-h|--help)
show_help
exit 0
;;
*)
echo "Unknown option: $1"
show_help
exit 1
;;
esac
done
# Track build results
declare -A BUILD_RESULTS
TOTAL_PLATFORMS=0
SUCCESSFUL_BUILDS=0
# Build iOS/macOS
build_ios() {
log_info "Building iOS/macOS..."
if [[ "$OSTYPE" != "darwin"* ]]; then
log_warning "iOS/macOS build requires macOS. Skipping."
BUILD_RESULTS["ios"]="skipped"
return 0
fi
cd "$PROJECT_ROOT"
case "$ACTION" in
clean)
"$SCRIPT_DIR/build-ios.sh" clean
;;
test)
"$SCRIPT_DIR/build-ios.sh" "$BUILD_TYPE" ios "generic/platform=iOS" test
;;
build|*)
if "$SCRIPT_DIR/build-ios.sh" "$BUILD_TYPE" ios "generic/platform=iOS" build; then
BUILD_RESULTS["ios"]="success"
((SUCCESSFUL_BUILDS++)) || true
else
BUILD_RESULTS["ios"]="failed"
fi
if [ "$RUN_TESTS" = true ]; then
log_info "Running iOS tests..."
"$SCRIPT_DIR/build-ios.sh" "$BUILD_TYPE" ios "generic/platform=iOS" test || true
fi
;;
esac
}
# Build Android
build_android() {
log_info "Building Android..."
cd "$PROJECT_ROOT"
case "$ACTION" in
clean)
"$SCRIPT_DIR/build-android.sh" "$BUILD_TYPE" clean
;;
test)
"$SCRIPT_DIR/build-android.sh" "$BUILD_TYPE" test
;;
build|*)
if "$SCRIPT_DIR/build-android.sh" "$BUILD_TYPE" assemble; then
BUILD_RESULTS["android"]="success"
((SUCCESSFUL_BUILDS++)) || true
else
BUILD_RESULTS["android"]="failed"
fi
if [ "$RUN_TESTS" = true ]; then
log_info "Running Android tests..."
"$SCRIPT_DIR/build-android.sh" "$BUILD_TYPE" test || true
fi
;;
esac
}
# Build Linux
build_linux() {
log_info "Building Linux..."
cd "$PROJECT_ROOT"
case "$ACTION" in
clean)
"$SCRIPT_DIR/build-linux.sh" "$BUILD_TYPE" clean
;;
test)
"$SCRIPT_DIR/build-linux.sh" "$BUILD_TYPE" test
;;
build|*)
if "$SCRIPT_DIR/build-linux.sh" "$BUILD_TYPE" build; then
BUILD_RESULTS["linux"]="success"
((SUCCESSFUL_BUILDS++)) || true
else
BUILD_RESULTS["linux"]="failed"
fi
if [ "$RUN_TESTS" = true ]; then
log_info "Running Linux tests..."
"$SCRIPT_DIR/build-linux.sh" "$BUILD_TYPE" test || true
fi
;;
esac
}
# Print build summary
print_summary() {
echo ""
log_info "=== Build Summary ==="
echo ""
printf "%-12s | %-10s | %s\n" "Platform" "Status" "Details"
printf "%-12s-+-%-10s-+-%s\n" "------------" "----------" "-------"
for platform in "${!BUILD_RESULTS[@]}"; do
local status="${BUILD_RESULTS[$platform]}"
local status_symbol=""
case "$status" in
success)
status_symbol="✅"
;;
failed)
status_symbol="❌"
;;
skipped)
status_symbol="⏭️"
;;
esac
printf "%-12s | %-10s | %s\n" "$platform" "$status" "$status_symbol"
done
echo ""
echo "Total: $SUCCESSFUL_BUILDS / ${#BUILD_RESULTS[@]} platforms built successfully"
echo ""
}
# Main
echo "========================================"
echo " RSSuper Multi-Platform Build System"
echo "========================================"
echo ""
echo "Configuration:"
echo " Platforms: $PLATFORMS"
echo " Build Type: $BUILD_TYPE"
echo " Action: $ACTION"
echo " Run Tests: $RUN_TESTS"
echo ""
# Convert platforms string to array
IFS=',' read -ra PLATFORM_ARRAY <<< "$PLATFORMS"
# Build each platform
for platform in "${PLATFORM_ARRAY[@]}"; do
platform=$(echo "$platform" | xargs) # Trim whitespace
((TOTAL_PLATFORMS++)) || true
case "$platform" in
all)
build_ios
build_android
build_linux
((TOTAL_PLATFORMS+=2)) || true # Add extra platforms
;;
ios)
build_ios
;;
android)
build_android
;;
linux)
build_linux
;;
*)
log_error "Unknown platform: $platform"
exit 1
;;
esac
done
# Print summary
print_summary
# Exit with error if any builds failed
if [ "$SUCCESSFUL_BUILDS" -lt "${#BUILD_RESULTS[@]}" ]; then
# Filter out skipped builds
local actual_builds=0
for result in "${BUILD_RESULTS[@]}"; do
if [ "$result" != "skipped" ]; then
((actual_builds++)) || true
fi
done
if [ "$SUCCESSFUL_BUILDS" -lt "$actual_builds" ]; then
log_error "Some builds failed"
exit 1
fi
fi
log_success "All builds completed successfully!"

169
scripts/common.sh Executable file
View File

@@ -0,0 +1,169 @@
#!/bin/bash
# RSSuper Common Build Utilities
# Shared functions used across all platform build scripts
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging functions
log_info() {
echo -e "${BLUE} $1${NC}"
}
log_success() {
echo -e "${GREEN}$1${NC}"
}
log_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
log_error() {
echo -e "${RED}$1${NC}"
}
# Get project root directory
get_project_root() {
echo "$(cd "$(dirname "$0")/.." && pwd)"
}
# Get current timestamp in ISO format
get_timestamp() {
date -u "+%Y-%m-%dT%H:%M:%SZ"
}
# Get human-readable timestamp
get_timestamp_human() {
date -u "+%Y-%m-%d %H:%M:%S UTC"
}
# Get current git commit hash
get_git_commit() {
git rev-parse HEAD 2>/dev/null || echo "unknown"
}
# Get short git commit hash
get_git_commit_short() {
local commit=$(get_git_commit)
echo ${commit:0:7}
}
# Get current git branch
get_git_branch() {
git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown"
}
# Get git status (clean or modified)
get_git_status() {
if git diff --quiet 2>/dev/null && git diff --cached --quiet 2>/dev/null; then
echo "clean"
else
echo "modified"
fi
}
# Check if running in CI
is_ci() {
[ -n "$CI" ]
}
# Check if command exists
command_exists() {
command -v "$1" &> /dev/null
}
# Create a temporary file with cleanup trap
create_temp_file() {
local temp_file=$(mktemp)
trap "rm -f '$temp_file'" EXIT
echo "$temp_file"
}
# Parse command line arguments
# Usage: parse_args "option1:value1 option2:value2"
# Sets variables OPTION1, OPTION2 with values
parse_args() {
local options="$1"
shift
for opt in $options; do
local key=${opt%%:*}
local value=${opt#*:}
export "$key=$value"
done
}
# Show spinner while command runs (for nice UX)
run_with_spinner() {
local message="$1"
shift
echo -e "${BLUE}$message...${NC}"
"$@"
}
# Generate a build report
generate_build_report() {
local platform="$1"
local status="$2"
local details="$3"
local report_file="${platform}-build-report.md"
local timestamp=$(get_timestamp_human)
local commit=$(get_git_commit_short)
local branch=$(get_git_branch)
{
echo "# $platform Build Report"
echo ""
echo "| | |"
echo "|---|---|"
echo "| **Status** | $status |"
echo "| **Commit** | \`$commit\` |"
echo "| **Branch** | \`$branch\` |"
echo "| **Time** | $timestamp |"
echo ""
if [ "$status" = "FAILED" ]; then
echo "## Errors"
echo ""
echo "$details"
else
echo "## Result"
echo ""
echo "$platform build completed successfully."
fi
} > "$report_file"
echo "Build report written to: $report_file"
}
# Wait for user input (useful for debugging)
wait_for_input() {
echo "Press Enter to continue..."
read
}
# Export for use in other scripts
export -f log_info
export -f log_success
export -f log_warning
export -f log_error
export -f get_project_root
export -f get_timestamp
export -f get_timestamp_human
export -f get_git_commit
export -f get_git_commit_short
export -f get_git_branch
export -f get_git_status
export -f is_ci
export -f command_exists
export -f create_temp_file
export -f parse_args
export -f run_with_spinner
export -f generate_build_report
export -f wait_for_input