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
151 changes: 151 additions & 0 deletions .github/workflows/windows-cross-compile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ on:
- 'vm/**'
- 'Ports/WindowsPort/**'
- 'maven/windows/**'
- 'scripts/gen-windows-uwp-stubs.py'
- 'maven/build-engine/src/main/java/com/codename1/builders/WindowsNativeBuilder.java'
- 'maven/build-engine/src/test/java/com/codename1/builders/WindowsNativeBuilderArchTest.java'
- 'maven/build-engine/src/test/resources/com/codename1/builders/xbox-smoke-AppxManifest.xml'
- '!vm/**/README.md'
- '!vm/**/readme.md'
- '!vm/**/docs/**'
Expand All @@ -25,6 +29,10 @@ on:
- 'vm/**'
- 'Ports/WindowsPort/**'
- 'maven/windows/**'
- 'scripts/gen-windows-uwp-stubs.py'
- 'maven/build-engine/src/main/java/com/codename1/builders/WindowsNativeBuilder.java'
- 'maven/build-engine/src/test/java/com/codename1/builders/WindowsNativeBuilderArchTest.java'
- 'maven/build-engine/src/test/resources/com/codename1/builders/xbox-smoke-AppxManifest.xml'

concurrency:
# Only the latest revision needs validation. PR numbers keep forks isolated;
Expand Down Expand Up @@ -155,13 +163,156 @@ jobs:
test -f core/target/classes/com/codename1/ui/Form.class
test -f windows/target/classes/com/codename1/impl/windows/WindowsImplementation.class

# A UWP (Xbox Developer Mode) build compiles the desktop-only natives
# out and links generated stubs in their place; a native added to one of
# those files without regenerating would fail only the UWP link below.
- name: UWP stubs are current
run: python3 scripts/gen-windows-uwp-stubs.py --check

# Builds the desktop exe and the UWP one (app partition, WindowsApp.lib
# only, /APPCONTAINER): the UWP link is the gate that every Win32 call the
# port makes in that build exists inside the app container.
- name: Cross-compile the native Windows .exe (clang-cl + xwin)
env:
CN1_XWIN_SYSROOT: ${{ runner.temp }}/xwin-sdk
CN1_CLANG_CL: clang-cl
CN1_LLVM_RC: llvm-rc
CN1_UWP_EXE_OUT: ${{ github.workspace }}/uwp-smoke/app.exe
run: |
cd vm
JAVA_HOME="$JDK_8_HOME" mvn -B -pl tests -am test \
'-Dtest=CleanTargetIntegrationTest#crossCompilesWindowsExeWithXwin' \
'-Dsurefire.failIfNoSpecifiedTests=false'
test -f "$CN1_UWP_EXE_OUT"

# The development certificate windows.xbox builds create, made by the
# builder's own openssl commands; the Windows job signs with it, so a
# certificate Windows refuses fails there.
- name: Create the Xbox development certificate (WindowsNativeBuilder)
env:
CN1_XBOX_DEVCERT_OUT: ${{ github.workspace }}/uwp-smoke/devcert
RETRY_ONLY_MATCHING: transient
RETRY_ATTEMPTS: '3'
RETRY_DELAY_SECONDS: '60'
run: |
set -euo pipefail
mkdir -p ~/.codenameone
cp maven/UpdateCodenameOne.jar ~/.codenameone/
cd maven
JAVA_HOME="$JDK_8_HOME" bash ../scripts/ci/retry.sh mvn -B -pl build-engine -am install \
-DskipTests -Dmaven.javadoc.skip=true -Plocal-dev-javase
JAVA_HOME="$JDK_8_HOME" mvn -B -pl build-engine test -Plocal-dev-javase \
'-Dtest=WindowsNativeBuilderArchTest' -Dsurefire.failIfNoSpecifiedTests=false
test -s "$CN1_XBOX_DEVCERT_OUT/dev.pfx"
test -s "$CN1_XBOX_DEVCERT_OUT/dev.cer"

# The manifest is pinned to WindowsNativeBuilder.buildXboxAppxManifest by
# WindowsNativeBuilderArchTest, so the run below installs what windows.xbox
# builds ship.
- name: Upload the UWP smoke app
uses: actions/upload-artifact@v4
with:
name: uwp-smoke
path: |
uwp-smoke/app.exe
uwp-smoke/devcert/dev.pfx
uwp-smoke/devcert/dev.cer
maven/build-engine/src/test/resources/com/codename1/builders/xbox-smoke-AppxManifest.xml
if-no-files-found: error
retention-days: 3

# Phase 1's proof without a console: the cross-built UWP app is packed, signed,
# installed and launched as a real UWP activation (app container, CoreWindow,
# swap chain) on a Windows runner. The app writes a marker into its package
# LocalState once its first form has painted; no marker fails the job.
uwp-run:
name: install + launch the UWP package (Windows x64)
needs: cross-compile
runs-on: windows-latest
timeout-minutes: 20
steps:
- name: Download the UWP smoke app
uses: actions/download-artifact@v4
with:
name: uwp-smoke
path: uwp-smoke

- name: Pack, sign and install
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$root = Resolve-Path uwp-smoke
$layout = Join-Path $env:RUNNER_TEMP 'uwp-layout'
New-Item -ItemType Directory -Force (Join-Path $layout 'Assets') | Out-Null
Copy-Item (Join-Path $root 'uwp-smoke/app.exe') (Join-Path $layout 'app.exe')
$manifest = Get-ChildItem -Recurse $root -Filter 'xbox-smoke-AppxManifest.xml' | Select-Object -First 1
Copy-Item $manifest.FullName (Join-Path $layout 'AppxManifest.xml')
Add-Type -AssemblyName System.Drawing
foreach ($logo in 'StoreLogo','Square150x150Logo','Square44x44Logo','SplashScreen') {
$bmp = New-Object System.Drawing.Bitmap 150,150
$g = [System.Drawing.Graphics]::FromImage($bmp); $g.Clear([System.Drawing.Color]::SteelBlue); $g.Dispose()
$bmp.Save((Join-Path $layout "Assets/$logo.png"), [System.Drawing.Imaging.ImageFormat]::Png); $bmp.Dispose()
}
$kit = Get-ChildItem 'C:/Program Files (x86)/Windows Kits/10/bin/*/x64/makeappx.exe' |
Sort-Object FullName -Descending | Select-Object -First 1
$bin = $kit.DirectoryName
$msix = Join-Path $env:RUNNER_TEMP 'UwpSmoke.msix'
& "$bin/makeappx.exe" pack /o /d $layout /p $msix
if ($LASTEXITCODE -ne 0) { throw "makeappx failed" }
# Signed with the certificate WindowsNativeBuilder created (subject
# CN=UwpSmoke, the manifest Publisher; empty PKCS#12 password).
$pfx = Join-Path $root 'uwp-smoke/devcert/dev.pfx'
$cer = Join-Path $root 'uwp-smoke/devcert/dev.cer'
& "$bin/signtool.exe" sign /fd SHA256 /f $pfx $msix
if ($LASTEXITCODE -ne 0) { throw "signtool rejected the builder's development certificate" }
Import-Certificate -FilePath $cer -CertStoreLocation 'Cert:/LocalMachine/TrustedPeople' | Out-Null
$unlock = 'HKLM:/SOFTWARE/Microsoft/Windows/CurrentVersion/AppModelUnlock'
New-Item -Force $unlock | Out-Null
Set-ItemProperty $unlock AllowAllTrustedApps 1 -Type DWord
Set-ItemProperty $unlock AllowDevelopmentWithoutDevLicense 1 -Type DWord
Add-AppxPackage -Path $msix
Get-AppxPackage com.codename1.uwpsmoke | Format-List Name,PackageFamilyName,InstallLocation,SignatureKind

- name: Launch and wait for the first frame
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$pkg = Get-AppxPackage com.codename1.uwpsmoke
$pfn = $pkg.PackageFamilyName
$state = Join-Path $env:LOCALAPPDATA "Packages/$pfn/LocalState"
Start-Process "shell:AppsFolder\$pfn!App"
$marker = $null
for ($i = 0; $i -lt 90 -and -not $marker; $i++) {
Start-Sleep -Seconds 1
if (Test-Path $state) {
$marker = Get-ChildItem -Recurse $state -Filter 'uwp-smoke.txt' -ErrorAction SilentlyContinue | Select-Object -First 1
}
}
Add-Type -AssemblyName System.Drawing, System.Windows.Forms
$b = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
$bmp = New-Object System.Drawing.Bitmap $b.Width, $b.Height
$g = [System.Drawing.Graphics]::FromImage($bmp); $g.CopyFromScreen($b.Location, [System.Drawing.Point]::Empty, $b.Size); $g.Dispose()
New-Item -ItemType Directory -Force uwp-run | Out-Null
$bmp.Save('uwp-run/desktop.png', [System.Drawing.Imaging.ImageFormat]::Png)
Get-Process app -ErrorAction SilentlyContinue | Format-Table Id,ProcessName,StartTime
if (-not $marker) {
Get-WinEvent -LogName 'Microsoft-Windows-AppModel-Runtime/Admin' -MaxEvents 20 -ErrorAction SilentlyContinue |
Format-List TimeCreated,Id,Message
Get-WinEvent -FilterHashtable @{LogName='Application'; Id=1000,1001} -MaxEvents 5 -ErrorAction SilentlyContinue |
Format-List TimeCreated,Message
if (Test-Path $state) { Get-ChildItem -Recurse $state | Format-Table FullName,Length }
throw "the UWP app wrote no marker in 90s: it did not reach its first frame"
}
$text = Get-Content $marker.FullName -Raw
Write-Host "marker: $text"
if ($text -notmatch '^CN1-UWP-STARTED [1-9][0-9]*x[1-9][0-9]*') { throw "unexpected marker: $text" }
Get-Process app -ErrorAction SilentlyContinue | Stop-Process -Force

- name: Upload the desktop capture
if: always()
uses: actions/upload-artifact@v4
with:
name: uwp-run-capture
path: uwp-run/
if-no-files-found: ignore
retention-days: 7
64 changes: 64 additions & 0 deletions Ports/WindowsPort/nativeSources/cn1_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,60 @@
#include <wincodec.h>
#include <stdint.h>

#if defined(CN1_WINDOWS_UWP)
/*
* UWP (Xbox Developer Mode) build: the app container's API partition drops a
* few Win32 calls that have a direct app-partition equivalent. They are mapped
* here once, so the shared file code reads the same in both builds.
* CreateFileW -> CreateFile2 (same handle, same flags, split differently);
* CharUpperW -> an invariant-locale LCMapStringEx in place. A template file is
* never used by this port, so CreateFile2 ignores that argument.
*/
static __inline HANDLE cn1UwpCreateFileW(LPCWSTR name, DWORD access, DWORD share,
LPSECURITY_ATTRIBUTES sa, DWORD disposition, DWORD flagsAndAttributes, HANDLE templateFile) {
CREATEFILE2_EXTENDED_PARAMETERS ext;
(void) templateFile;
ZeroMemory(&ext, sizeof(ext));
ext.dwSize = sizeof(ext);
ext.dwFileAttributes = flagsAndAttributes & 0x0000FFFF;
ext.dwFileFlags = flagsAndAttributes & 0xFFF00000;
ext.dwSecurityQosFlags = flagsAndAttributes & 0x000F0000;
ext.lpSecurityAttributes = sa;
return CreateFile2(name, access, share, disposition, &ext);
}
#undef CreateFileW
#define CreateFileW cn1UwpCreateFileW

static __inline HANDLE cn1UwpCreateFileA(LPCSTR name, DWORD access, DWORD share,
LPSECURITY_ATTRIBUTES sa, DWORD disposition, DWORD flagsAndAttributes, HANDLE templateFile) {
WCHAR wide[MAX_PATH];
if (MultiByteToWideChar(CP_UTF8, 0, name, -1, wide, MAX_PATH) <= 0) {
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return INVALID_HANDLE_VALUE;
}
return cn1UwpCreateFileW(wide, access, share, sa, disposition, flagsAndAttributes, templateFile);
}
#undef CreateFileA
#define CreateFileA cn1UwpCreateFileA

static __inline LPWSTR cn1UwpCharUpperW(LPWSTR s) {
int len = (int) wcslen(s);
LCMapStringEx(LOCALE_NAME_INVARIANT, LCMAP_UPPERCASE, s, len, s, len, NULL, NULL, 0);
return s;
}
#undef CharUpperW
#define CharUpperW cn1UwpCharUpperW

/* Declared by the SDK for the desktop partition only, but exported by kernel32
* on every Windows, the Xbox included. It is how a null dereference becomes a
* NullPointerException (cn1WinFaultToException), so the UWP build keeps it.
* Developer Mode sideloading does not run the Store's API certification. */
#ifdef __cplusplus
extern "C"
#endif
WINBASEAPI PVOID WINAPI AddVectoredExceptionHandler(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler);
#endif /* CN1_WINDOWS_UWP */

#ifdef __cplusplus
/* C++ method-call macros for the COBJMACROS-style call sites. */
#include "cn1_windows_comc.h"
Expand All @@ -71,6 +125,16 @@ extern "C" {
/* The ParparVM runtime is C; keep its declarations C-linked under C++. */
#include "cn1_globals.h"

#if defined(CN1_WINDOWS_UWP)
/* The app's local data folder (ApplicationData.Current.LocalFolder) into buf,
* NUL terminated. Returns 0 when it cannot be read. cn1_windows_corewindow.cpp. */
int cn1UwpLocalFolder(WCHAR* buf, int capacity);
#endif

/* COM, the fault handlers and the input ring, shared by every host's
* initDisplay. 0 when already initialised. cn1_windows_window.cpp. */
int cn1WinInitRuntime(void);

/* ------------------------------------------------------------------ events */

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
*/

/* Native virtual accessibility tree for the lightweight Win32 port. */
#ifdef _WIN32
#if defined(_WIN32) && !defined(CN1_WINDOWS_UWP) /* desktop only: stubbed for UWP in cn1_windows_uwp_stubs.c */

/* xwin may provide a newer MSVC STL than the clang-cl installed by the Linux
* cross-compile runner. This file uses only the long-stable string/vector
Expand Down
2 changes: 1 addition & 1 deletion Ports/WindowsPort/nativeSources/cn1_windows_audiorec.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
* and closes.
*/

#ifdef _WIN32
#if defined(_WIN32) && !defined(CN1_WINDOWS_UWP) /* desktop only: stubbed for UWP in cn1_windows_uwp_stubs.c */

#include "cn1_windows.h"
#include <mmsystem.h>
Expand Down
2 changes: 1 addition & 1 deletion Ports/WindowsPort/nativeSources/cn1_windows_camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
* (negative stride); both are handled when packing into CN1's 0xAARRGGBB ints.
*/

#ifdef _WIN32
#if defined(_WIN32) && !defined(CN1_WINDOWS_UWP) /* desktop only: stubbed for UWP in cn1_windows_uwp_stubs.c */

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
Expand Down
2 changes: 1 addition & 1 deletion Ports/WindowsPort/nativeSources/cn1_windows_chrome.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/* Integrated main-window title bar. The window keeps WS_OVERLAPPEDWINDOW:
* DWM owns the real caption buttons and DefWindowProc owns system commands,
* resize tracking, caption double-click and the window system menu. */
#ifdef _WIN32
#if defined(_WIN32) && !defined(CN1_WINDOWS_UWP) /* desktop only: stubbed for UWP in cn1_windows_uwp_stubs.c */
#include "cn1_windows.h"
#include <dwmapi.h>
#include <windowsx.h>
Expand Down
Loading
Loading