Skip to content

feat(windows): Delphi CE interactive build workflow#16044

Open
MattGyverLee wants to merge 16 commits into
keymanapp:masterfrom
MattGyverLee:feat/windows/delphi-ce-workflow
Open

feat(windows): Delphi CE interactive build workflow#16044
MattGyverLee wants to merge 16 commits into
keymanapp:masterfrom
MattGyverLee:feat/windows/delphi-ce-workflow

Conversation

@MattGyverLee

Copy link
Copy Markdown
Contributor

Summary

Lets developers on the free Delphi Community Edition tier build the still-Delphi parts of the tree, even though CE 11+ deliberately blocks the dcc32 CLI that build.sh normally drives. None of the compiler / runtime stack moves; this is purely build-environment work plus docs.

CI is unchanged. KEYMAN_DELPHI_CE is unset by default, so build.sh keeps invoking msbuild.exe exactly as before.

Split off from #16039 per @mcdurdin's review — that PR bundled the CE workflow with the Delphi 11/12 source-compat patches. This PR is the CE half; the compat half is #16043.

Depends on #16043 for the source-compat patches and the KEYMAN_DELPHI_VERSION knob that lets the IDE find Delphi 11/12.

Why this matters for the Delphi removal roadmap

Delphi 10.3 Pro is no longer obtainable, and CE 10.3 has been removed from Embarcadero's downloads. New contributors who want to help finish #4599 currently hit a wall: they can install CE 12, but CE blocks dcc32 and build.sh falls over before reaching any Delphi project. This PR fixes the wall — without committing to keep Delphi alive.

What's in the change

1. CE-aware delphi_msbuild

resources/build/win/environment.inc.sh: delphi_msbuild() now checks KEYMAN_DELPHI_CE. When =1, it prints an IDE-build prompt naming the .dproj and waits for Enter instead of invoking msbuild.exe. The pre-build (rc.exe, manifest gen) and post-build (binary copy, codegen) steps in build.sh run normally around the prompt, so the developer only has to drive the Delphi compile by hand.

This is the approach @mcdurdin proposed in #16039 (comment) — gate by version in the existing shell script rather than parallel PowerShell helpers.

2. Docs

  • docs/build/windows.md: one-line pointer to windows-delphi-ce.md from the existing "Delphi 10.3 CE no longer available" warning, so future CE users find the workaround instead of dead-ending.
  • docs/build/windows-delphi-ce.md: ~940-line guide — prerequisites, registry library-path setup, source patches required on chore(windows): Delphi 11/12 source compatibility #16043, the canonical build order (including the tsysinfox64 -> .bin -> tsysinfo_x64.res chicken-and-egg), install-and-overlay workflow, debugging, troubleshooting catalog, and a section labeling which patches are safe to upstream. Written against Delphi 12 Athens CE specifically (currently the only free tier); Delphi 11 CE works with the same approach modulo a couple of paths.

3. engine.groupproj fix

windows/src/engine/engine.groupproj: drop the inst\insthelper reference. The path doesn't resolve — insthelper.dproj lives at windows/src/engine/insthelper, not .../inst/insthelper — and IDE "Build All" was failing on it. build.sh-based builds were never affected because they don't walk the groupproj.

Technically independent of the CE work, but bundled here because IDE "Build All" is the path CE users actually exercise. Happy to split into its own PR if reviewers prefer.

4. .gitignore

Patterns for the per-script log files that the CE workflow writes (configure.log, core-build.log, etc.).

Reviewer notes

  • The PR's behaviour-changing surface is exactly one if [[ "${KEYMAN_DELPHI_CE:-}" == "1" ]] branch in delphi_msbuild(). Everything else is docs / .gitignore / the orthogonal engine.groupproj cleanup.
  • The PowerShell helper scripts from the original chore(windows): support Delphi 11/12 compilation + Delphi 12 CE dev workflow #16039 are gone in this revision; the workflow runs entirely through KEYMAN_DELPHI_CE=1 ./build.sh.
  • The doc file is named windows-delphi-ce.md (not windows-d12.md) so the same workflow guide applies if/when a future Delphi 11 CE user shows up; the body is Delphi 12-specific because that's what's been actually tested.

User Testing

User testing not required: build-environment / docs change with no runtime behaviour modification. CI will exercise the non-CE default path (KEYMAN_DELPHI_CE unset); the CE flow is manually validated via the steps in docs/build/windows-delphi-ce.md.

Relates-to

Relates-to: #4599
Depends-on: #16043
Replaces: #16039 (split into #16043 + this PR)

@github-project-automation github-project-automation Bot moved this to Todo in Keyman Jun 2, 2026
@keymanapp-test-bot keymanapp-test-bot Bot added has-user-test user-test-missing User tests have not yet been defined for the PR labels Jun 2, 2026
@keymanapp-test-bot

Copy link
Copy Markdown

User Test Results

Test specification and instructions

ERROR: user tests have not yet been defined

@keymanapp-test-bot keymanapp-test-bot Bot added this to the A19S30 milestone Jun 2, 2026
@keyman-server

Copy link
Copy Markdown
Collaborator

This pull request is from an external repo and will not automatically be built. The build must still be passed before it can be merged. Ask one of the team members to make a manual build of this PR.

@MattGyverLee

Copy link
Copy Markdown
Contributor Author

Took a hard look at the groupproj-batching path. Sharing the analysis so we can pick the cheapest workable design.

Coverage and order — what each groupproj would buy us on CE

Groupproj Delphi sub-projects in dir In groupproj Missing Internal order
engine.groupproj keyman, kmcomapi, tsysinfo, tsysinfox64, insthelper (5) 4 insthelper WRONG: keyman;kmcomapi;tsysinfo;tsysinfox64tsysinfo is attempted before the tsysinfox64.exe it embeds exists
desktop.groupproj kmshell, kmbrowserhost, setup, kmconfig, insthelp (5) 5 OK internally
developer.groupproj TIKE, setup, kmconvert, kmdbrowserhost + ext BPLs 3 kmdbrowserhost + design-time BPLs OK internally

So before any other consideration, adopting engine.groupproj would need a one-line reorder (tsysinfox64 before tsysinfo) plus restoring insthelper at its real path. That's a change to the non-CE flow we'd like to avoid here.

What build.sh actually does between Delphi compiles

The reason per-.dproj prompts fit cleanly is that the build chain interleaves a lot of work that the groupproj knows nothing about. Per child build.sh:

rc.exe (version.rc, manifest.rc, project-specific .rc)
   → manifest.in → manifest.xml generation
   → delphi_msbuild <project>.dproj    ← CE prompt currently fires here
   → sentrytool_delphiprep (symbol-upload prep, per-arch)
   → tds2dbg (TDS → PDB conversion)
   → copy outputs into install layout

Plus the cross-project hand-offs that aren't in any .dproj or .groupproj:

  • (a) devtools → keyman/setup/kmshell. devtools.exe -buildmessageconstants emits MessageIdentifierConsts.pas (~1500 lines) from windows/src/desktop/kmshell/xml/strings.xml. devtools.exe -buildsetupstrings emits ~32 Keyman.Setup.System.Locale.<bcp47>.pas files from windows/src/desktop/setup/locale/*.xml. All .gitignored. Skipping this gives F1026 File not found: '...MessageIdentifierConsts.pas'.
  • (b) build_standards_data → TIKE. Emits five BCP-47 registry .pas files in common/windows/delphi/standards/ (the largest is LangTagsRegistry.pas at ~2.4 MB). All .gitignored. Skipping gives F1026 File not found: '...BCP47SubtagRegistry.pas'.
  • (c) tsysinfox64 → tsysinfo. tsysinfo.dproj (Win32) embeds the Win64 helper tsysinfox64.exe as tsysinfo_x64.res. After building tsysinfox64.dproj, build.sh copies bin/Win64/Debug/tsysinfox64.exe → tsysinfo/tsysinfox64.bin, then runs rc tsysinfo_x64.rc in windows/src/engine/tsysinfo/ to produce the .res, then builds tsysinfo.dproj. Skipping any step gives F1026 File not found: 'tsysinfo_x64.res'.
  • (d) kmcmplib CLI → TIKE runtime. TIKE links to kmcmplib-19.dll, which is built CLI via ./developer/src/kmcmplib/build.sh build — no IDE involvement, but a clean CE build that skips it produces a TIKE that throws "kmcmplib-19.dll not found" on any compile action.
  • (e) keyman.dproj → kmshell runtime. kmshell compiles without keyman.exe — failure is at runtime: kmshell shows "Keyman failed to start" when enabling a keyboard.
  • (f) regsvr32 kmcomapi.dll → kmshell startup. windows/src/engine/build.sh install does this; without it, CoCreateInstance returns REGDB_E_CLASSNOTREG.

(c), (d), and the codegen halves of (a) and (b) are exactly the "post-Delphi hand-offs" build.sh interjects between Delphi compiles. The groupproj has no equivalent and can't fire rc.exe, devtools.exe, or shell copies.

What "use the groupproj" would actually require

To get one prompt per group, we'd have to restructure each child build.sh so that under CE:

  1. All children's pre-build (rc.exe runs, manifest.in.xml) fire first across the whole group.
  2. ONE IDE prompt fires for the groupproj.
  3. All children's post-build (sentrytool_delphiprep, tds2dbg, copies, codegen hand-off (c)) fire across the whole group.

That's a CE-only fork of how every child build.sh is structured — and the engine group still needs the tsysinfox64 → tsysinfo reorder (or it fails inside the IDE the same way) plus an insthelper add-back. Per-dproj prompts side-step both: the existing dependency order in build.sh already gets it right, including the (c) hand-off as a post-build step on tsysinfox64.

Proposal

Ship as-is. The PR-as-it-stands is a one-if-branch CE-only change in delphi_msbuild that fits between the existing rc.exe and sentrytool/tds2dbg calls in every child. ~30 prompts on a top-level clean build, but:

  • no reorder of engine.groupproj (no non-CE flow change),
  • no fork of any child build.sh (no non-CE flow change),
  • chicken-and-egg (a)–(f) all keep working because the existing build.sh order is what handles them,
  • design-time BPLs / insthelper / kmdbrowserhost don't need a special case — they're driven by their own build.sh and get the same per-dproj prompt.

Happy to revisit if there's a tighter design I'm missing, but under the "minimal CE-only changes" constraint, that's where I land. Also happy to add this analysis as a section to docs/build/windows-delphi-ce.md so the next CE contributor doesn't re-litigate it.

@MattGyverLee

Copy link
Copy Markdown
Contributor Author

I had Claude plow through the build and tell me what to do every time I hit an error until I could compile and run everything. I hit all those A-F chicken/egg compile problems above trying to follow the build. I wonder if the CLI version would hit some of the same blockers if it was put onto a fresh new machine, but I can't test that (and don't want to start from scratch again while in "hackathon" mode. I only know it worked on my machine after all the intervening workarounds.

@mcdurdin mcdurdin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of redundant and incorrect information in the docs. I think this needs a thorough rework

Comment thread docs/build/windows-delphi-ce.md Outdated
Comment on lines +70 to +76
* **Keyman 19 (official release)** installed from
https://keyman.com/desktop. This is *not* optional for Delphi 12 CE
developers -- the dev kmshell.exe runtime-discovers its install path via
`TKeymanPaths.KeymanDesktopInstallPath()`, which is hardcoded to
`C:\Program Files (x86)\Keyman\Keyman Desktop\`. Without the install,
kmshell crashes at startup with `SKApplicationTitle has had a fatal
error...` before its main form appears. See section 7 (Running).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem right; TKeymanPaths.KeymanDesktopInstallPath() does not hard code this path as it is read from the registry.

To run keyman.exe properly so that it can interact with elevated UI processes (uiAccess=true in the manifest), it needs to be signed with a valid cert, and be installed in a hardened path such as Program Files. But for debugging, you can run keyman.exe unsigned with uiAccess=false, in non-elevated processes. All other components should be runnable outside of Program Files, although there are libraries that have to be located.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right — I hadn't actually followed TKeymanPaths.KeymanDesktopInstallPath() to see where the path comes from. It reads from a registry key the Keyman installer populates, not a hardcoded path. Reworded the §1 prereq (install simplifies setup by populating that key rather than being strictly required) and updated the §7 troubleshooting entry to attribute the failure to the missing registry key. URL corrected to keyman.com/windows.

Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
Comment on lines +157 to +162
### 2.6 Install Keyman 19 official

Install Keyman 19.0 from https://keyman.com/desktop. After install verify
`C:\Program Files (x86)\Keyman\Keyman Desktop\kmshell.exe` exists and that
`HKLM\SOFTWARE\WOW6432Node\Keyman\Keyman Engine` is populated. The overlay
workflow in section 7 depends on these.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not right. keyman.com/windows. s/overlay/debugging. The reason for installing Keyman is to simplify the install environment but it is possible to debug without Keyman installed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded — install "simplifies setup" rather than "required", and it's possible to debug without an official install. URL corrected to keyman.com/windows. See the TKeymanPaths reply above for the underlying reason.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once this PR is clean, I need to test with a full uninstall/rebuild to make sure. It seemed very much to be required to move forward.

Comment thread docs/build/windows-delphi-ce.md Outdated
Comment on lines +175 to +213
```powershell
# Elevated PowerShell -- back up first
reg export 'HKCU\Software\Embarcadero\BDS\23.0\Library' `
C:\Projects\keyman\keyman\delphi-library-paths.backup.reg /y

$key32 = 'HKCU:\Software\Embarcadero\BDS\23.0\Library\Win32'
$key64 = 'HKCU:\Software\Embarcadero\BDS\23.0\Library\Win64'

$paths = @(
# Keyman common includes (mirrors DELPHIINCLUDES in delphi_flags.inc.sh)
'C:\Projects\keyman\keyman\common\windows\delphi\ext\cef4delphi\source',
'C:\Projects\keyman\keyman\common\windows\delphi\ext\dcpcrypt',
'C:\Projects\keyman\keyman\common\windows\delphi\ext\jwa\Win32API',
'C:\Projects\keyman\keyman\common\windows\delphi\ext\sentry',
'C:\Projects\keyman\keyman\developer\src\ext\mbcolor',
'C:\Projects\keyman\keyman\developer\src\ext\scfontcombobox',
# JCL / JVCL source roots (needed for TIKE)
'C:\Projects\keyman\keyman\developer\src\ext\jedi\jcl\jcl\source\common',
'C:\Projects\keyman\keyman\developer\src\ext\jedi\jcl\jcl\source\prototypes',
'C:\Projects\keyman\keyman\developer\src\ext\jedi\jcl\jcl\source\vcl',
'C:\Projects\keyman\keyman\developer\src\ext\jedi\jcl\jcl\source\windows',
'C:\Projects\keyman\keyman\developer\src\ext\jedi\jcl\jcl\source\include', # jcl.inc
'C:\Projects\keyman\keyman\developer\src\ext\jedi\jvcl\jvcl\design',
'C:\Projects\keyman\keyman\developer\src\ext\jedi\jvcl\jvcl\run',
'C:\Projects\keyman\keyman\developer\src\ext\jedi\jvcl\jvcl\common', # jvcl.inc
'C:\Projects\keyman\keyman\developer\src\ext\jedi\jvcl\jvcl\resources', # JvConsts.res
'C:\Projects\keyman\keyman\developer\src\ext\jedi\jedi', # jedi.inc
'C:\Projects\keyman\keyman\developer\src\ext\jedi' # parent for {$I jedi\jedi.inc}
)

foreach ($key in @($key32, $key64)) {
$cur = (Get-ItemProperty -Path $key -Name 'Search Path' `
-ErrorAction SilentlyContinue).'Search Path'
$merged = (@($cur -split ';') + $paths |
Where-Object { $_ } |
Select-Object -Unique) -join ';'
Set-ItemProperty -Path $key -Name 'Search Path' -Value $merged
}
```

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fragile because if we make changes to the project the docs will need to be updated so we don't get out of sync. I don't like having this kind of thing twice really.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept the explicit list because when I hit F1026 File not found: jvcl.inc on my first .dproj open, chasing it down took me longer than the recipe took to write — I'd rather leave a working recipe with drift risk than a pointer a future CE contributor has to reconstruct. Added a note that the first six paths mirror DELPHIINCLUDES (verified against the file) and the other eleven don't. Fair on the drift risk though, this is a doc that'll need refreshing if either side changes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reduced some of the fragility with a grep.

Comment thread docs/build/windows-delphi-ce.md Outdated
Comment on lines +815 to +836
### `E2010 Incompatible types: 'Cardinal' and 'Boolean'` (JCL)

JCL Boolean -> BOOL casts missing. See section 3.4.

### `E2003 Undeclared identifier: 'OldCreateOrder'` (JVCL)

JvComponent.pas not patched. See section 3.5.

### `E2003 Undeclared identifier: 'null'` (HTMLColors)

mbcolor's `mxs.inc` not patched for VER350/VER360 -- the `Variants` unit
was silently dropped from the `uses` clause. See section 3.6.

### `E2029 Declaration expected` near SourceRootPath.pas

devtools/SourceRootPath.pas hit the `{$ELSE} {$MESSAGE ERROR}` fallback.
See section 3.7.

### `E2012 Type of expression must be BOOLEAN` near EnumFontFamiliesEx

CleartypeDrawCharacter.pas guard not extended for VER350/VER360. See
section 3.10.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should not be necessary

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, false positive from the delphi docs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed — those were the source-compat error entries. §7 now points to #16043 for source-compat and only troubleshoots CE-specific things.

Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread .gitignore Outdated
@keyman-server keyman-server modified the milestones: A19S30, A19S31 Jun 8, 2026
@keyman-server keyman-server modified the milestones: A19S31, A19S32 Jun 22, 2026
@mcdurdin

Copy link
Copy Markdown
Member

Any movement on this @MattGyverLee? We're need to close stale pull requests so please let me know where you plan to take this.

@MattGyverLee

Copy link
Copy Markdown
Contributor Author

@mcdurdin , looking at this now.

MattGyverLee added a commit to MattGyverLee/keyman that referenced this pull request Jul 2, 2026
Pro-tier Delphi 11/12 users had no way to discover the env-var knob
introduced by this PR - it was documented only in the PR body and in
the CE-workflow doc from keymanapp#16044. Add a short paragraph to the Delphi
requirements section pointing at the two Studio path values and
reaffirming that the unset default preserves CI behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@MattGyverLee

Copy link
Copy Markdown
Contributor Author

Reworked substantively based on your review. Doc dropped from 939 → 253 lines. Trying to map your inline comments to what changed:

Comment Status
L82, L136, L338, L340, L374, L391, L421, L474, L507, L514, L539, L836, L879, L922 Fixed — most was content that duplicated windows.md or belonged in #16043
L236 (engine.groupproj discussion "unnecessary") Fixed — deleted the section
L248 (SETX consistency) Fixed
L545 (unnecessary CAUTION about --builder-dep-parent) Fixed — removed
L621 (enabling→launching) Fixed
L630 (canonical build order redundant with prompts) Fixed — dropped the numbered list
L771 (debugging accuracy + scope) Fixed — trimmed, dropped incorrect PDB claim, acknowledged non-CE scope
L76, L162, L854, L108, L292, .gitignore L198 See inline replies

Also one thing I got wrong in my own doc and only caught spot-checking after your review: I'd claimed all 17 Library Search Path entries mirror DELPHIINCLUDES, but that variable only defines 6 of them. The other 11 (JCL/JVCL/jedi) are IDE-only. Corrected.

Flagging for scope — this PR also drops inst\insthelper\insthelper.dproj from engine.groupproj. That path has been stale since d06f4b13d0 moved the file out of inst/. Bundled it here because it's the sort of thing CE contributors trip on via IDE Build All, but it's a drive-by. Happy to split into its own chore commit if you'd rather review it separately.

MattGyverLee and others added 5 commits July 2, 2026 13:42
Lets the existing tree compile under Delphi 11 (VER350) and Delphi 12
(VER360) without breaking Delphi 10.3 (VER330). CI is unchanged: with
KEYMAN_DELPHI_VERSION unset, every build script keeps defaulting to
Delphi 10.3 (BDS 20.0) exactly as before.

This is a bridge to lower contributor friction on keymanapp#4599 (deprecate
Delphi). The 10.3 build server can also be upgraded to a paid Delphi
11/12 license on top of these patches if/when that's wanted.

Build-script knob:
* resources/build/win/configure_environment.inc.sh,
  resources/build/win/delphi_environment.inc.sh: DELPHI_VERSION now
  reads ${KEYMAN_DELPHI_VERSION:-20.0}.
* resources/builder.inc.sh: builder_describe_platform's delphi tool
  detection respects the same KEYMAN_DELPHI_VERSION default, so
  machines with only Delphi 12 installed no longer have win,delphi-
  gated targets silently skipped.

Keyman-owned source compat (additive VER350/VER360 arms; VER330 paths
untouched):
* common/windows/delphi/tools/devtools/SourceRootPath.pas: teach
  DelphiMajorVersion about BDS 22.0 / 23.0 install dirs.
* common/windows/delphi/web/Keyman.System.HttpServer.Base.pas: extend
  the IFNDEF VER330 tripwire to accept VER340/350/360.
* common/windows/delphi/components/FixedTrackbar.pas: same tripwire
  extension.
* common/windows/delphi/general/CleartypeDrawCharacter.pas:
  EnumFontFamiliesEx integer-return comparison on VER340/350/360 (was
  VER340-only).
* common/windows/delphi/general/JsonUtil.pas: pass [] options arg to
  TJSONAncestor.ToChars on VER350/360 (Delphi 11+ added the parameter).

Vendored third-party patches (each hunk annotated with a "Keyman local
patch" comment plus refresh strategy):
* developer/src/ext/jedi/jcl/jcl/source/common/JclSynch.pas: wrap
  Boolean args to CreateEvent / OpenEvent / CreateWaitableTimer /
  OpenWaitableTimer / OpenSemaphore / CreateMutex / OpenMutex in
  explicit BOOL() casts (Delphi 12 tightened implicit Boolean->BOOL at
  call sites); switch JclWin32.CreateMutex -> Winapi.Windows.CreateMutex
  to match the file's existing pattern for the other Winapi calls.
* developer/src/ext/jedi/jvcl/jvcl/run/JvComponent.pas: on VER350+,
  unconditionally call DoCreate (Embarcadero removed the
  OldCreateOrder property in Delphi 11; modern behavior is equivalent
  to OldCreateOrder=True).
* developer/src/ext/mbcolor/mxs.inc: add VER350/VER360 blocks defining
  DELPHI_5_UP through DELPHI_10_UP. Without these, HTMLColors.pas
  silently drops Variants from its uses clause and breaks with
  "Undeclared identifier: 'null'".

.gitignore: add patterns for per-arch version*.res and meson
wraplock files.

Relates-to: keymanapp#4599
Pro-tier Delphi 11/12 users had no way to discover the env-var knob
introduced by this PR - it was documented only in the PR body and in
the CE-workflow doc from keymanapp#16044. Add a short paragraph to the Delphi
requirements section pointing at the two Studio path values and
reaffirming that the unset default preserves CI behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per @mcdurdin's review: he built with unmodified JclSynch.pas and
verified the Delphi 12 compile succeeds without the BOOL casts.
The patches were defensive against a compile failure I never
actually observed on this file — I assumed Delphi 12's tightened
implicit-Boolean handling required them without empirically
checking. Removing.

If a real compile failure on JclSynch shows up later, address it
then with a specific error to fix rather than a preemptive patch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per @mcdurdin's review: the OldCreateOrder-was-removed premise
disagrees with the Athens documentation he linked
(https://docwiki.embarcadero.com/Libraries/Athens/en/Vcl.Forms.TForm.OldCreateOrder).
Given JclSynch.pas turned out to be an unnecessary defensive patch,
this one deserves the same skepticism. Reverting until a specific
Delphi 12 compile failure on JVCL surfaces that motivates a
targeted fix.

The remaining vendored ext patch (mbcolor/mxs.inc) stays because it
produces a concrete E2003 Undeclared identifier: 'null' at
HTMLColors.pas:290 on Delphi 12 — reproducible, not defensive.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- FixedTrackbar.pas: extend the existing 'Tested on' comment block
  to note VER350/VER360 IFNDEF arms are added without full
  verification against Vcl.Grids.pas — same self-documentation
  pattern @mcdurdin used when he added the 10.3 attestation line
  in 2019.
- HttpServer.Base.pas: extend the pre-existing "may need to check"
  comment to name the newly-silenced tripwire arms and record that
  Indy's URL handling on 10.4/11/12 has not been re-verified. The
  workaround stays applied conservatively rather than removed
  optimistically.
- Drop `.wraplock` .gitignore rule — Meson subproject artifact,
  no connection to Delphi 11/12 source compat. Scope drift.
- windows.md: trim the "so CI is unaffected" tail from the
  KEYMAN_DELPHI_VERSION paragraph. CI default is a separate
  concern from the env-var default.

Motivated by the same "no unverified defensive extensions" concern
that led me to revert the JclSynch and JvComponent patches earlier.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@MattGyverLee MattGyverLee force-pushed the feat/windows/delphi-ce-workflow branch from e58423a to d0daa8e Compare July 2, 2026 18:45
resources/build/minimum-versions.inc.sh pins KEYMAN_MIN_VERSION_EMSCRIPTEN
at 3.1.64, but the windows.md walkthrough still told contributors to
`emsdk install 3.1.58`. That version now fails to compile core/src/wasm.cpp
because a recent Core commit (9909d7d "expose km_core_state_options_update
to WASM") uses stricter emscripten pointer-binding APIs. Bumping the docs
to match the pin.

Discovered while running the CE build walkthrough on this branch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@MattGyverLee MattGyverLee force-pushed the feat/windows/delphi-ce-workflow branch from d0daa8e to 223c2f8 Compare July 3, 2026 04:53
@mcdurdin

mcdurdin commented Jul 3, 2026

Copy link
Copy Markdown
Member

This PR currently includes all the commits from #16043. Can you rebase onto master so we can look at just the changes for this PR?

@mcdurdin mcdurdin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking a lot cleaner, thank you. Suggestions in-line.

One significant change request -- and please test with a clean Delphi install before pushing, because I won't be able to do that here:

Rather than having the include paths script written in the documentation, where it will inevitably go stale, can you make any necessary updates to devtools/DevIncludePaths.pas and devtools/DevInstallPackages.pas to support Delphi 10.4/11/12 and use that to write include paths and install packages (with devtools -ai and devtools -ip, because that keeps the registration of paths and packages associated with the modules, e.g.

function do_build() {
"$DEVTOOLS" -ai "$THIS_SCRIPT_PATH/source"
delphi_msbuild packages/CEF4Delphi.dproj "//p:Platform=Win32"
"$DEVTOOLS" -ip "$COMMON_OUTLIB/CEF4Delphi.bpl"
}

Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
Comment thread docs/build/windows-delphi-ce.md Outdated
MattGyverLee and others added 2 commits July 3, 2026 14:25
Two Delphi 12 dev-env issues in devtools' -ai / TIncludePaths.Reset when
processing %AppData%\Embarcadero\BDS\23.0\EnvOptions.proj:

1. Delphi 12 emits an empty <PropertyGroup/> at the top of the file with
   no Condition attribute. The pre-existing VarIsNull guard didn't
   short-circuit reliably under Delphi 12's variant handling, so a Null
   Attributes['Condition'] hit Pos() and raised EVariantTypeCastError.
   Replace with VarToStrDef(..., '') for a defensive Variant->string
   coercion that handles Null and Empty.

2. The Win32/Win64 match was Pos('Win32', ...) — a substring match that
   also matches 'Win64x' (a Delphi 12 target now present in the file).
   Building against the Win64x PropertyGroup then hit an empty
   <DelphiBrowsingPath/> and again produced Null. Match with the
   surrounding literal single quotes ('''Win32''' / '''Win64''') so
   'Win64x' no longer matches, and additionally wrap the NodeValue
   reads in VarToStrDef so empty child elements degrade cleanly.

Applied to both AddPathToProjectXML (the -ai/-ip path) and Reset (the
-ri path) — same code shape in both.

Found while running the CE build workflow against Delphi 12 CE — the
tripwire fired at cef4delphi's `devtools -ai` step and blocked the
engine build entirely until this fix landed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
TCustFileList.AddCustFile used a nested expression the Delphi 12 Win64
compiler rejects with E2010 'Incompatible types: TCustFile and TObject':

    Result := Items[inherited Add(FObjectClass.Create)];

dcc32 (Win32) accepted the same source, so the file has been building
fine for CI's default 10.3 flow. On Delphi 12 dcc64, the inline chain
loses the widening from TCustFile (returned by FObjectClass.Create) to
TObject (Add's parameter). Splitting the object creation into a local
TObject variable lets the widening happen explicitly and both compilers
accept the code without any IFDEF.

Found while building the Win64 half of kmcomapi.dproj as part of the
CE workflow walkthrough — the Win32 half compiled cleanly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reverts the earlier revert (dcef4c2). @mcdurdin's original empirical
test — building without the JclSynch patches to prove they were
unnecessary — was correct for the projects he compiled, but it did
not include TIKE.

TIKE pulls in JCL through JVCL's dependency graph and instantiates
TJclMutex.Create (JclSynch.pas:1078). On Delphi 12, that hits:

  [dcc32 Error] JclSynch.pas(1078): E2010 Incompatible types: 'Cardinal' and 'Boolean'
  [dcc32 Fatal Error] JclSysUtils.pas(2685): F2063 Could not compile used unit 'JclSynch.pas'

The BOOL() casts on CreateEvent/OpenEvent/CreateWaitableTimer/
OpenWaitableTimer/OpenSemaphore/CreateMutex/OpenMutex are what let
TIKE compile. Marc's smaller test set was Delphi-12-compatible without
them; the fuller Developer tree isn't.

Discovered by running the CE build workflow through
`developer/src/tike/build.sh` on Delphi 12 CE — TIKE fails at the
JclSynch link stage without this patch.

Note the CreateMutex site also switches JclWin32.CreateMutex to
Winapi.Windows.CreateMutex directly, because JclWin32's own
CreateMutex is a re-declaration Delphi 12 resolves ambiguously.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@MattGyverLee MattGyverLee force-pushed the feat/windows/delphi-ce-workflow branch from b6b9b7e to 6776456 Compare July 3, 2026 20:06
Reverts the earlier revert (d7b3784). @mcdurdin's original
concern was that the Athens docs seemed to say OldCreateOrder was
still present, so the patch's premise was wrong. Empirical Delphi 12
CE contradicts that:

  [dcc32 Error] JvComponent.pas(126): E2003 Undeclared identifier: 'OldCreateOrder'
  [dcc32 Fatal Error] JvDockControlForm.pas(2442): F2063 Could not compile used unit 'JvComponent.pas'

Line 126 is `if OldCreateOrder then DoCreate;`. Under Delphi 12,
`TCustomForm.OldCreateOrder` genuinely no longer exists. The
{$IF Defined(VER350) or Defined(VER360)} arm unconditionally calls
DoCreate on 11/12 (equivalent to OldCreateOrder=True, which the
docs describe as the modern default) and keeps the runtime check
on older Delphis.

Discovered by running the CE build workflow through
`developer/src/tike/build.sh` on Delphi 12 CE — TIKE pulls in
JvDockControlForm which uses JvComponent, and TIKE fails without
this patch.

The docs Marc linked may still be reachable, but the compiler is
the source of truth here — VER350+ has removed OldCreateOrder from
TCustomForm's published surface.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@MattGyverLee

MattGyverLee commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Ok, @mcdurdin I'm done with these. I did a fresh build of all of the Delphi-related items. This allowed me to confirm the docs and that some of the JCL and JVCL changes are needed to build Tike.exe (at least on Windows). I also opened a couple of follow-up issues. I'm only set up to test Windows, Delphi12 AND CE together on this machine, so the 2 branch separation is a bit artificial. I can't test or speak for Delphi 12 Pro on any platform or Delphi 12 CE on Linux.

@mcdurdin

mcdurdin commented Jul 6, 2026

Copy link
Copy Markdown
Member

I am out of time to continue reviewing this. I will return to it in August after I return from leave.

MattGyverLee and others added 5 commits July 9, 2026 17:58
Missing this guard means that tike fails to build on a fresh install without the OLDCREATEORDER flag.
Revert the JclSynch.pas BOOL() cast patch to match project-jedi/jcl
upstream verbatim at all 7 call sites (no casts; restore
JclWin32.CreateMutex). TIKE builds without it once the JvComponent.pas
OldCreateOrder guard (already the upstream guard) is in place.

Apply mcdurdin's inline review suggestions on FixedTrackbar.pas and
Keyman.System.HttpServer.Base.pas: reorder version guards
newest-outermost, consolidate messages, and convert the raw ERROR!
line to a {$MESSAGE ERROR} directive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapse the nested {$IFNDEF} version walls in FixedTrackbar.pas and
Keyman.System.HttpServer.Base.pas into single {$IF Defined(VERxxx) or
...} / {$ELSEIF} / {$ELSE} blocks, matching the style already used in
CleartypeDrawCharacter.pas and JsonUtil.pas.

Expressed positively: WARN on the unblocked-but-unverified Delphi
10.4/11/12 (VER340/VER350/VER360), silent on the tested baselines, and
ERROR on any unrecognised version. No change to the runtime workarounds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… build

TJclMutex.Create called JclWin32.CreateMutex, whose `external kernel32`
redeclaration takes a BOOL param (legacy WinNT4 workaround). Delphi 12's
dcc32 rejects the implicit Boolean->BOOL there (E2010), which blocks a
from-source build of the vendored JCL. Route through the RTL like the
sibling OpenMutex, with an explicit BOOL() cast. Only the CreateMutex
site is touched; the other calls already go through the RTL. Builds that
link a precompiled JCL .dcu never hit this.

Also compact the local-patch comments in mxs.inc and HttpServer.Base.pas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Delphi Community Edition blocks CLI compilation, so when KEYMAN_DELPHI_CE=1
delphi_msbuild pauses and prompts the developer to build the project in the
IDE, then continues. To support the "open the .groupproj, Build All once, then
Enter through each prompt" flow, prompts auto-skip when the expected Debug
output is already newer than its source.

Also fix a stale path in engine.groupproj: insthelper was referenced as
inst\insthelper\insthelper.dproj, but the project lives at insthelper\; the
bad path broke IDE Build All for the engine group. (The automated build is
unaffected — it builds insthelper via insthelper/build.sh, not the groupproj.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@MattGyverLee MattGyverLee force-pushed the feat/windows/delphi-ce-workflow branch 4 times, most recently from 3192c24 to 3c9d0f6 Compare July 10, 2026 07:03
Document the Community Edition interactive build workflow (KEYMAN_DELPHI_CE,
the IDE Build All + auto-skip flow, Config Manager Debug/Win32 requirement)
in windows-delphi-ce.md, and link it from windows.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@MattGyverLee MattGyverLee force-pushed the feat/windows/delphi-ce-workflow branch from 3c9d0f6 to f6a2c63 Compare July 10, 2026 07:11
@MattGyverLee

MattGyverLee commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@mcdurdin , these are as clean as I can get them.

The first round of figuring this out was a brute-force session where I kept trying to build and passed Claude the error messages that surfaced, like the JCL errors and a half-dozen missing .res files. After the first JC errors were solved, I asked Claude to solve similar errors in one go. The results were not hallucinations, but some went too far (blocking some paths 100% instead of conditionally) and some defensively hit similar JC code you don't touch. The result was getting everything compiling in an hour while I was working on something else, but I wasn't trying to follow what changes were made.

I think I originally submitted the first "monoPR" as a draft and honestly didn't expect these branches to get prioritized or merged into main any time soon, I just figured it would be documentation for future reference of someone doing this "right" if they got stuck.

I can manually test most of 16043 alone via the IDE or the full build if I got a 12 trial. I can't get 10 to verify things aren't broken by my changes in 10/10CE, but I assume you and the CI have done that. You asked me to rebase both off of main, I haven't done that yet because with only 12CE, I can't test 16044 without 16043 underneath. Let me know if you still want that once this stops churning.

Once 16043 lands, I can rebase this off of main if that's still your preference. This is the clean compare (MattGyverLee/keyman@chore/windows/delphi-11-12-compat...feat/windows/delphi-ce-workflow).

Now the JC fixes are reduced to the absolute minimum for me to progress (verified by upstream changes). Removing either breaks my JC builds since I don't have installed binaries (I assume you must).

I did the full clear and rebuild during the last refactor. What I haven't tested is to uninstall Delphi and Keyman Developer and Keyman and trying to get both keyman apps running from a build without installing any precompiled binaries. I heard you say that it's possible, but haven't looked into what that entails.

I've kept the .groupproj fix on this branch because the regular build doesn't touch them, but my new CE process is MUCH faster with them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat has-user-test user-test-missing User tests have not yet been defined for the PR windows/

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

3 participants