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
187 changes: 187 additions & 0 deletions .github/workflows/windows-installer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
name: Build Windows Installer

on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
version:
description: 'Version number for the installer'
required: false
default: '4.0'

jobs:
build-windows-installer:
runs-on: windows-latest
permissions:
contents: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: MSYS
update: true
install: >-
base-devel
gcc
make
cmake
pkg-config
python
python-pip
python-setuptools
git
sqlite3

- name: Cache MSYS2 packages
uses: actions/cache@v4
with:
path: D:\a\_temp\msys64\var\cache\pacman\pkg
key: msys2-packages-${{ runner.os }}-${{ hashFiles('.github/workflows/windows-installer.yml') }}
restore-keys: |
msys2-packages-${{ runner.os }}-

- name: Install OpenPLC Runtime using install.sh
shell: msys2 {0}
run: |
echo "=========================================="
echo "Installing OpenPLC Runtime via install.sh"
echo "=========================================="

# Run the install script which handles all dependencies and build
./install.sh

echo "Installation complete!"

- name: Prepare installer payload
shell: pwsh
run: |
Write-Host "Preparing installer payload..."

# Create payload directory structure
New-Item -ItemType Directory -Force -Path "windows\payload\msys64"
New-Item -ItemType Directory -Force -Path "windows\payload\openplc-runtime"

# Copy MSYS2 installation
Write-Host "Copying MSYS2 installation (this may take a while)..."
$msys2Path = "D:\a\_temp\msys64"
if (Test-Path $msys2Path) {
Copy-Item -Path "$msys2Path\*" -Destination "windows\payload\msys64" -Recurse -Force
} else {
Write-Host "MSYS2 path not found at $msys2Path, trying C:\msys64..."
Copy-Item -Path "C:\msys64\*" -Destination "windows\payload\msys64" -Recurse -Force
}

# Copy OpenPLC Runtime (excluding unnecessary files)
Write-Host "Copying OpenPLC Runtime..."
$excludeDirs = @('.git', '.github', '.mypy_cache', '.ruff_cache', '.vscode', 'tests', '__pycache__')

Get-ChildItem -Path "." -Exclude $excludeDirs | Where-Object {
$_.Name -ne "windows" -and $_.Name -ne ".git"
} | ForEach-Object {
if ($_.PSIsContainer) {
Copy-Item -Path $_.FullName -Destination "windows\payload\openplc-runtime\$($_.Name)" -Recurse -Force
} else {
Copy-Item -Path $_.FullName -Destination "windows\payload\openplc-runtime\" -Force
}
}

# Clean up to reduce size
Write-Host "Cleaning up payload to reduce size..."

# Remove pacman cache
$pacmanCache = "windows\payload\msys64\var\cache\pacman\pkg"
if (Test-Path $pacmanCache) {
Remove-Item -Path "$pacmanCache\*" -Force -Recurse -ErrorAction SilentlyContinue
}

# Remove unnecessary MSYS2 directories
$msys2Cleanup = @(
"windows\payload\msys64\var\log",
"windows\payload\msys64\tmp"
)
foreach ($dir in $msys2Cleanup) {
if (Test-Path $dir) {
Remove-Item -Path "$dir\*" -Force -Recurse -ErrorAction SilentlyContinue
}
}

# Calculate payload size
$payloadSize = (Get-ChildItem -Path "windows\payload" -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB
Write-Host "Payload size: $([math]::Round($payloadSize, 2)) MB"

- name: Install Inno Setup
shell: pwsh
run: |
Write-Host "Installing Inno Setup..."
choco install innosetup -y --no-progress

# Add Inno Setup to PATH
$env:PATH = "C:\Program Files (x86)\Inno Setup 6;$env:PATH"
[Environment]::SetEnvironmentVariable("PATH", $env:PATH, "Process")

- name: Create placeholder icon
shell: pwsh
run: |
# Create a simple placeholder icon if one doesn't exist
# In production, you should include a proper icon file
if (-not (Test-Path "windows\icon.ico")) {
Write-Host "Creating placeholder icon..."
# Download a generic icon or create one
# For now, we'll modify the setup.iss to not require an icon
}

- name: Build installer with Inno Setup
shell: pwsh
run: |
Write-Host "Building installer..."

# Set version from input or tag
$version = "${{ github.event.inputs.version }}"
if ([string]::IsNullOrEmpty($version)) {
$version = "${{ github.ref_name }}".TrimStart('v')
if ([string]::IsNullOrEmpty($version) -or $version -eq "${{ github.ref_name }}") {
$version = "4.0"
}
}
Write-Host "Building version: $version"

# Create output directory
New-Item -ItemType Directory -Force -Path "windows\output"

# Run Inno Setup compiler
& "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" `
/DMyAppVersion="$version" `
/O"windows\output" `
/F"OpenPLC_Runtime_$($version)_Setup" `
"windows\setup.iss"

if ($LASTEXITCODE -ne 0) {
Write-Error "Inno Setup compilation failed with exit code $LASTEXITCODE"
exit 1
}

Write-Host "Installer built successfully!"
Get-ChildItem "windows\output"

- name: Upload installer artifact
uses: actions/upload-artifact@v4
with:
name: windows-installer
path: windows/output/*.exe
retention-days: 30

- name: Create GitHub Release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1
with:
files: windows/output/*.exe
draft: false
prerelease: ${{ contains(github.ref, 'beta') || contains(github.ref, 'alpha') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
13 changes: 10 additions & 3 deletions core/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ add_compile_options(-Wall -Werror -Wextra -fstack-protector-strong
-D_FORTIFY_SOURCE=2 -O2 -Wformat
-Werror=format-security -fPIC -fPIE)

# On Cygwin/MSYS2, disable treating warnings as errors due to header conflicts
# The _POSIX_C_SOURCE redefinition warning is caused by Python headers vs system headers
# Detection: CMAKE_SYSTEM_NAME is "CYGWIN" or "MSYS" on these platforms
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN|MSYS")
# Remove -Werror but keep all other warnings and specific -Werror=format-security
add_compile_options(-Wno-error)
endif()

# Step 3: Build the executable and link against the shared library
add_executable(plc_main
Expand All @@ -37,9 +44,9 @@ add_executable(plc_main
)

# Link against shared library
target_link_libraries(plc_main
dl
pthread
target_link_libraries(plc_main
dl
pthread
${PYTHON_LIBRARIES}
)

Expand Down
22 changes: 22 additions & 0 deletions core/src/drivers/plugin_driver.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
#define PY_SSIZE_T_CLEAN

// Suppress _POSIX_C_SOURCE redefinition warning from Python.h on MSYS2/Cygwin
// Python.h defines _POSIX_C_SOURCE to 200809L which conflicts with system headers
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcpp"
#endif

#include <Python.h>

#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

#include "../plc_app/image_tables.h"
#include "../plc_app/utils/log.h"
#include "plugin_config.h"
Expand Down Expand Up @@ -41,10 +53,12 @@ plugin_driver_t *plugin_driver_create(void)
return NULL;
}

#if !defined(__CYGWIN__) && !defined(__MSYS__)
// Initialize mutex with priority inheritance to prevent priority inversion
// This ensures that when a lower-priority plugin thread holds the mutex,
// it temporarily inherits the priority of any higher-priority thread
// (like the PLC scan cycle thread) waiting for the mutex.
// Note: Priority inheritance is not available on MSYS2/Cygwin
pthread_mutexattr_t mutex_attr;
if (pthread_mutexattr_init(&mutex_attr) != 0)
{
Expand All @@ -67,6 +81,14 @@ plugin_driver_t *plugin_driver_create(void)
}

pthread_mutexattr_destroy(&mutex_attr);
#else
// On MSYS2/Cygwin, use a regular mutex without priority inheritance
if (pthread_mutex_init(&driver->buffer_mutex, NULL) != 0)
{
free(driver);
return NULL;
}
#endif

return driver;
}
Expand Down
12 changes: 12 additions & 0 deletions core/src/drivers/python_plugin_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,20 @@
#define __PYTHON_PLUGIN_BRIDGE_H

#define PY_SSIZE_T_CLEAN

// Suppress _POSIX_C_SOURCE redefinition warning from Python.h on MSYS2/Cygwin
// Python.h defines _POSIX_C_SOURCE to 200809L which conflicts with system headers
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcpp"
#endif

#include <Python.h>

#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

// Forward declaration
struct plugin_instance_s;

Expand Down
9 changes: 8 additions & 1 deletion core/src/plc_app/scan_cycle_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
#include "scan_cycle_manager.h"
#include "utils/utils.h"

// CLOCK_MONOTONIC_RAW is Linux-specific, use CLOCK_MONOTONIC on other platforms
#if defined(__CYGWIN__) || defined(__MSYS__) || !defined(CLOCK_MONOTONIC_RAW)
#define OPENPLC_CLOCK CLOCK_MONOTONIC
#else
#define OPENPLC_CLOCK CLOCK_MONOTONIC_RAW
#endif

static uint64_t expected_start_us = 0;
static uint64_t last_start_us = 0;
static pthread_mutex_t stats_mutex = PTHREAD_MUTEX_INITIALIZER;
Expand All @@ -23,7 +30,7 @@ plc_timing_stats_t plc_timing_stats = {.scan_time_min = INT64_MAX,
static uint64_t ts_now_us(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
clock_gettime(OPENPLC_CLOCK, &ts);
return (uint64_t)ts.tv_sec * 1000000ull + ts.tv_nsec / 1000;
}

Expand Down
Loading