Browse Source

convert windows project from dotnet to rust (#1087)

* convert windows project from dotnet to rust

* update pr jobs

* pr job fixes

* don't bother building mac app in prs for now

* build windows wrapper with rust
pull/1088/head
Jason Dove 3 years ago committed by GitHub
parent
commit
8e13b07c84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      .github/workflows/artifacts.yml
  2. 74
      .github/workflows/pr.yml
  3. 2
      ErsatzTV-Windows/.gitignore
  4. 1028
      ErsatzTV-Windows/Cargo.lock
  5. 19
      ErsatzTV-Windows/Cargo.toml
  6. 33
      ErsatzTV-Windows/ErsatzTV-Windows.csproj
  7. 14
      ErsatzTV-Windows/Program.cs
  8. 81
      ErsatzTV-Windows/TrayApplicationContext.cs
  9. 5
      ErsatzTV-Windows/build.rs
  10. 2
      ErsatzTV-Windows/ersatztv_windows.rc
  11. 101
      ErsatzTV-Windows/src/main.rs

9
.github/workflows/artifacts.yml

@ -186,6 +186,12 @@ jobs: @@ -186,6 +186,12 @@ jobs:
with:
node-version: '14'
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
if: ${{ matrix.kind == 'windows' }}
- name: Cache NPM dependencies
uses: bahmutov/npm-install@v1.4.5
with:
@ -219,7 +225,8 @@ jobs: @@ -219,7 +225,8 @@ jobs:
# Build Windows launcher
if [ "${{ matrix.kind }}" == "windows" ]; then
dotnet publish ErsatzTV-Windows/ErsatzTV-Windows.csproj --framework net7.0-windows --runtime "${{ matrix.target }}" -c Release -o "$release_name" -p:InformationalVersion="${{ inputs.release_version }}-${{ matrix.target }}" -p:EnableCompressionInSingleFile=true -p:DebugType=Embedded -p:PublishSingleFile=true --self-contained true
cd ErsatzTV-Windows && cargo build --release --all-features
mv ErsatzTV-Windows/target/release/ersatztv_windows.exe "$release_name/ErsatzTV-Windows.exe"
fi
# Download ffmpeg

74
.github/workflows/pr.yml

@ -2,12 +2,8 @@ @@ -2,12 +2,8 @@
on:
pull_request:
jobs:
build_and_test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ windows-latest, ubuntu-latest, macos-latest ]
build_and_test_windows:
runs-on: windows-latest
steps:
- name: Get the sources
uses: actions/checkout@v3
@ -17,6 +13,11 @@ jobs: @@ -17,6 +13,11 @@ jobs:
with:
dotnet-version: 7.0.x
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Clean
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
@ -25,11 +26,64 @@ jobs: @@ -25,11 +26,64 @@ jobs:
- name: Prep project file
run: sed -i '/Scanner/d' ErsatzTV/ErsatzTV.csproj
if: ${{ matrix.os != 'macos-latest' }}
- name: Prep project file Mac
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --no-restore --verbosity normal
- name: Build Windows
run: |
cd ErsatzTV-Windows
cargo build --release --all-features
build_and_test_linux:
runs-on: ubuntu-latest
steps:
- name: Get the sources
uses: actions/checkout@v3
- name: Setup .NET Core
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Clean
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
- name: Install dependencies
run: dotnet restore
- name: Prep project file
run: sed -i '/Scanner/d' ErsatzTV/ErsatzTV.csproj
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --no-restore --verbosity normal
build_and_test_mac:
runs-on: macos-latest
steps:
- name: Get the sources
uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: true
- name: Setup .NET Core
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Clean
run: dotnet clean --configuration Release && dotnet nuget locals all --clear
- name: Install dependencies
run: dotnet restore
- name: Prep project file
run: sed -i '' '/Scanner/d' ErsatzTV/ErsatzTV.csproj
if: ${{ matrix.os == 'macos-latest' }}
- name: Build
run: dotnet build --configuration Release --no-restore

2
ErsatzTV-Windows/.gitignore vendored

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
target/

1028
ErsatzTV-Windows/Cargo.lock generated

File diff suppressed because it is too large Load Diff

19
ErsatzTV-Windows/Cargo.toml

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
[package]
name = "ersatztv_windows"
version = "0.1.0"
edition = "2021"
[dependencies]
tray-item = { git = "https://github.com/olback/tray-item-rs" }
special-folder = { git = "https://github.com/masinc/special-folder-rs" }
process_path = "0.1.4"
[dependencies.windows]
version = "0.43.0"
features = [
"Win32_System_Console",
"Win32_Foundation"
]
[build-dependencies]
windres = "*"

33
ErsatzTV-Windows/ErsatzTV-Windows.csproj

@ -1,33 +0,0 @@ @@ -1,33 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<RootNamespace>ErsatzTV_Windows</RootNamespace>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationIcon>Ersatztv.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Content Include="Ersatztv.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ErsatzTV.Core\ErsatzTV.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Program.cs">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Compile>
</ItemGroup>
</Project>

14
ErsatzTV-Windows/Program.cs

@ -1,14 +0,0 @@ @@ -1,14 +0,0 @@
namespace ErsatzTV_Windows;
public static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
public static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new TrayApplicationContext());
}
}

81
ErsatzTV-Windows/TrayApplicationContext.cs

@ -1,81 +0,0 @@ @@ -1,81 +0,0 @@
using ErsatzTV.Core;
using System.Diagnostics;
using CliWrap;
namespace ErsatzTV_Windows;
public class TrayApplicationContext : ApplicationContext
{
private readonly NotifyIcon _trayIcon;
private readonly CancellationTokenSource _tokenSource;
public TrayApplicationContext()
{
_trayIcon = new NotifyIcon
{
Icon = new Icon("./Ersatztv.ico"),
ContextMenuStrip = new ContextMenuStrip(),
Visible = true
};
_tokenSource = new CancellationTokenSource();
AddMenuItem("Launch Web UI", LaunchWebUI);
AddMenuItem("Show Logs", ShowLogs);
_trayIcon.ContextMenuStrip.Items.Add(new ToolStripSeparator());
AddMenuItem("Exit", Exit);
string folder = AppContext.BaseDirectory;
string exe = Path.Combine(folder, "ErsatzTV.exe");
if (File.Exists(exe))
{
Cli.Wrap(exe)
.WithWorkingDirectory(folder)
.WithValidation(CommandResultValidation.None)
.ExecuteAsync(_tokenSource.Token);
}
}
private void AddMenuItem(string name, EventHandler action)
{
var item = new ToolStripMenuItem(name);
item.Click += action;
_trayIcon.ContextMenuStrip.Items.Add(item);
}
private void LaunchWebUI(object? sender, EventArgs e)
{
var process = new Process();
process.StartInfo.UseShellExecute = true;
process.StartInfo.FileName = "http://localhost:8409";
process.Start();
}
private void ShowLogs(object? sender, EventArgs e)
{
if (!Directory.Exists(FileSystemLayout.LogsFolder))
{
Directory.CreateDirectory(FileSystemLayout.LogsFolder);
}
var process = new Process();
process.StartInfo.UseShellExecute = true;
process.StartInfo.FileName = FileSystemLayout.LogsFolder;
process.Start();
}
protected override void Dispose(bool disposing)
{
_tokenSource?.Cancel();
base.Dispose(disposing);
}
private void Exit(object? sender, EventArgs e)
{
// Hide tray icon, otherwise it will remain shown until user mouses over it
_trayIcon.Visible = false;
Application.Exit();
}
}

5
ErsatzTV-Windows/build.rs

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
use windres::Build;
fn main() {
Build::new().compile("ersatztv_windows.rc").unwrap();
}

2
ErsatzTV-Windows/ersatztv_windows.rc

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
id ICON "ersatztv.ico"
ersatztv-icon ICON "ersatztv.ico"

101
ErsatzTV-Windows/src/main.rs

@ -0,0 +1,101 @@ @@ -0,0 +1,101 @@
use special_folder::SpecialFolder;
use std::fs;
use std::process::Child;
use std::process::Command;
use std::process::Stdio;
use {std::sync::mpsc, tray_item::TrayItem};
use windows::Win32::System::Console;
enum Message {
Exit,
}
fn main() {
let mut tray = TrayItem::new("ErsatzTV", "ersatztv-icon").unwrap();
let (tx, rx) = mpsc::channel();
tray.add_menu_item("Launch Web UI", || {
let _ = Command::new("cmd")
.arg("/C")
.arg("start")
.arg("http://localhost:8409")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn();
})
.unwrap();
tray.add_menu_item("Show Logs", || {
let path = SpecialFolder::LocalApplicationData
.get()
.unwrap()
.join("ersatztv")
.join("logs");
match path.to_str() {
None => {}
Some(folder) => {
fs::create_dir_all(folder).unwrap();
let _ = Command::new("cmd")
.arg("/C")
.arg("start")
.arg(folder)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn();
}
}
})
.unwrap();
tray.inner_mut().add_separator().unwrap();
tray.add_menu_item("Exit", move || {
tx.send(Message::Exit).unwrap();
})
.unwrap();
let path = process_path::get_executable_path();
let mut child: Option<Child> = None;
match path {
None => {}
Some(path) => {
let etv = path.parent().unwrap().join("ErsatzTV.exe");
if etv.exists() {
match etv.to_str() {
None => {}
Some(etv) => {
child = Some(
Command::new(etv)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.unwrap(),
);
}
}
}
}
}
loop {
match rx.recv() {
Ok(Message::Exit) => {
match child {
None => {}
Some(mut child) => {
unsafe {
Console::GenerateConsoleCtrlEvent(Console::CTRL_C_EVENT, 0);
}
child.wait().unwrap();
}
}
break;
}
_ => {}
}
}
}
Loading…
Cancel
Save