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
120 changes: 120 additions & 0 deletions .github/workflows/build_windows_bin.yml
Comment thread
vidplace7 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: Build Windows Binary

on:
workflow_call:
inputs:
windows_ver:
required: false
default: "2025"
type: string

permissions:
contents: read

jobs:
build-Windows:
runs-on: windows-${{ inputs.windows_ver }}
defaults:
run:
# UCRT64 is the MinGW-w64 environment native-windows targets; the `msys`
# environment would link msys-2.0.dll and produce a Cygwin-style binary.
shell: msys2 {0}
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
submodules: recursive
Comment thread
coderabbitai[bot] marked this conversation as resolved.
# Keep the token out of .git/config so later steps and artifacts can't leak it.
persist-credentials: false

- name: Setup MSYS2 / UCRT64
id: msys2
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
# argp is not packaged for the mingw environments and is built below.
# Python is omitted too: MSYS2's reports a `mingw` platform tag no wheel matches.
install: >-
mingw-w64-ucrt-x86_64-gcc
mingw-w64-ucrt-x86_64-pkgconf
mingw-w64-ucrt-x86_64-yaml-cpp
mingw-w64-ucrt-x86_64-libuv
mingw-w64-ucrt-x86_64-jsoncpp
mingw-w64-ucrt-x86_64-openssl
mingw-w64-ucrt-x86_64-libusb
mingw-w64-ucrt-x86_64-cmake
mingw-w64-ucrt-x86_64-ninja
git

- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.13"

# framework-portduino calls argp_parse(); MSYS2 packages argp only for the
# msys runtime, which cannot link into a native binary. No install() rules.
- name: Build and install argp-standalone
run: |
git clone --depth 1 https://github.com/tom42/argp-standalone /tmp/argp
cd /tmp/argp
cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Release .
cmake --build build
cp include/argp-standalone/argp.h /ucrt64/include/argp.h
cp build/src/libargp-standalone.a /ucrt64/lib/libargp.a

- name: Install PlatformIO
shell: pwsh
run: |
python -m pip install --upgrade pip
pip install platformio

- name: Get release version string
shell: pwsh
id: version
run: echo "long=$(python ./bin/buildinfo.py long)" >> $env:GITHUB_OUTPUT

# Runs outside the MSYS2 shell so PlatformIO stays on the runner's
# CPython; the UCRT64 toolchain is reached through PATH instead.
- name: Build for Windows
shell: pwsh
run: |
$env:PATH = "${{ steps.msys2.outputs.msys2-location }}\ucrt64\bin;$env:PATH"
platformio run -e native-windows
env:
PKG_VERSION: ${{ steps.version.outputs.long }}

- name: List output files
run: ls -lah .pio/build/native-windows/

# The env links statically, so only Windows system DLLs should appear here.
# A third-party DLL means the static link regressed.
- name: Verify the binary is self-contained
run: |
set -euo pipefail
bin=.pio/build/native-windows/meshtasticd.exe
test -f "$bin"
# `|| true` keeps grep's no-match exit out of `set -e`; an empty import
# table is caught by the test below instead of passing as "no deps".
deps=$(objdump -p "$bin" | grep -i 'DLL Name' || true)
test -n "$deps"
extra=$(printf '%s\n' "$deps" \
| grep -viE 'api-ms-win|KERNEL32|WS2_32|ADVAPI32|USER32|msvcrt|ucrtbase|bcrypt|IPHLPAPI|SHELL32|ole32|CRYPT32|SETUPAPI|CFGMGR32|WINMM|dbghelp' || true)
if [ -n "$extra" ]; then
printf '%s\n' "$extra"
echo "::error::meshtasticd.exe has non-system DLL dependencies (static link regressed)"
exit 1
fi
echo "OK: no third-party DLL dependencies"

- name: Smoke test the binary
run: |
.pio/build/native-windows/meshtasticd.exe --version

- name: Store binaries as an artifact
uses: actions/upload-artifact@v7
with:
name: firmware-windows-${{ inputs.windows_ver }}-${{ steps.version.outputs.long }}
overwrite: true
path: |
.pio/build/native-windows/meshtasticd.exe
12 changes: 12 additions & 0 deletions .github/workflows/main_matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,18 @@ jobs:
macos_ver: ${{ matrix.macos_ver }}
# secrets: inherit

Windows:
if: ${{ github.event_name != 'schedule' && github.event.inputs.nightly != 'true' }}
strategy:
fail-fast: false
matrix:
windows_ver:
- "2025" # x86_64
uses: ./.github/workflows/build_windows_bin.yml
with:
windows_ver: ${{ matrix.windows_ver }}
# secrets: inherit

package-pio-deps-native-tft:
if: ${{ github.repository == 'meshtastic/firmware' && github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/package_pio_deps.yml
Expand Down
16 changes: 16 additions & 0 deletions extra_scripts/windows_link_flags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env python3
# trunk-ignore-all(ruff/F821)
# trunk-ignore-all(flake8/F821): For SConstruct imports
#
# PlatformIO routes build_flags to the compile step only, so the static link
# flags are appended here, as wasm_link_flags.py does for [env:native-wasm].
Import("env")

if env["PIOENV"].startswith("native-windows"):
env.Append(
LINKFLAGS=[
"-static",
"-static-libgcc",
"-static-libstdc++",
]
)
15 changes: 12 additions & 3 deletions src/gps/RTC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,10 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
#else
rtc.initI2C();
#endif
tm *t = gmtime(&tv->tv_sec);
// tv_sec is a long, which is not time_t everywhere: on Windows
// time_t is 64-bit while long is 32-bit. Copy before taking &.
time_t setSecs = tv->tv_sec;
tm *t = gmtime(&setSecs);
rtc.setTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_wday, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
LOG_DEBUG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
Expand All @@ -341,7 +344,10 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
#else
rtc.begin(Wire);
#endif
tm *t = gmtime(&tv->tv_sec);
// tv_sec is a long, which is not time_t everywhere: on Windows
// time_t is 64-bit while long is 32-bit. Copy before taking &.
time_t setSecs = tv->tv_sec;
tm *t = gmtime(&setSecs);
rtc.setDateTime(*t);
LOG_DEBUG("%s setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", rtc.getChipName(), t->tm_year + 1900, t->tm_mon + 1,
t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
Expand All @@ -355,7 +361,10 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
#else
ArtronShop_RX8130CE rtc(&Wire);
#endif
tm *t = gmtime(&tv->tv_sec);
// tv_sec is a long, which is not time_t everywhere: on Windows
// time_t is 64-bit while long is 32-bit. Copy before taking &.
time_t setSecs = tv->tv_sec;
tm *t = gmtime(&setSecs);
if (rtc.setTime(*t)) {
LOG_DEBUG("RX8130CE setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1,
t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, printableEpoch);
Expand Down
4 changes: 4 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,10 +428,14 @@ void setup()
#if ARCH_PORTDUINO
RTCQuality ourQuality = RTCQualityDevice;

#ifdef __linux__
// timedatectl is systemd-only, so macOS, Windows and WASM stay at
// RTCQualityDevice rather than claim NTP quality we have not verified.
std::string timeCommandResult = exec("timedatectl status | grep synchronized | grep yes -c");
if (timeCommandResult[0] == '1') {
ourQuality = RTCQualityNTP;
}
#endif

struct timeval tv;
tv.tv_sec = time(NULL);
Expand Down
12 changes: 12 additions & 0 deletions src/mesh/HardwareRNG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ extern Adafruit_nRFCrypto nRFCrypto;
#include <unistd.h>
#ifdef __linux__
#include <sys/random.h> // getrandom()
#elif defined(_WIN32)
// Order is load-bearing, hence the blank line: bcrypt.h uses LONG/ULONG from
// windows.h and does not include it itself.
#include <windows.h>

#include <bcrypt.h> // BCryptGenRandom()
#else
#include <stdlib.h> // arc4random_buf() on Darwin/BSD
#endif
Expand Down Expand Up @@ -128,6 +134,12 @@ bool fill(uint8_t *buffer, size_t length, bool useRadioEntropy)
if (generated == static_cast<ssize_t>(length)) {
filled = true;
}
#elif defined(_WIN32)
// No getrandom/arc4random on Windows; BCryptGenRandom is the documented CSPRNG.
// Preferred over std::random_device, whose libstdc++ Windows backend reports entropy() == 0.
if (BCryptGenRandom(NULL, buffer, static_cast<ULONG>(length), BCRYPT_USE_SYSTEM_PREFERRED_RNG) == 0) { // STATUS_SUCCESS
filled = true;
}
#elif defined(__EMSCRIPTEN__)
// Browser/wasm: no getrandom/arc4random - fall through to std::random_device,
// which emscripten backs with crypto.getRandomValues().
Expand Down
4 changes: 4 additions & 0 deletions src/mqtt/MQTT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@

#include "IPAddress.h"
#if defined(ARCH_PORTDUINO)
#if defined(_WIN32)
#include <winsock2.h> // ntohl()
#else
#include <netinet/in.h>
#endif
#elif !defined(ntohl)
#include <machine/endian.h>
#define ntohl __ntohl
Expand Down
Loading
Loading