diff --git a/.gitignore b/.gitignore
index 7b872195880..dd7d41d3709 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@
/windows/src/**/*.identcache
/windows/src/**/*.vcxproj.user
/windows/src/**/version.res
+/windows/src/**/version*.res
/windows/src/**/*.pch
/windows/src/**/*.wixobj
/windows/src/**/*.sbr
diff --git a/common/windows/delphi/components/FixedTrackbar.pas b/common/windows/delphi/components/FixedTrackbar.pas
index 50cb444a55f..096d6340415 100644
--- a/common/windows/delphi/components/FixedTrackbar.pas
+++ b/common/windows/delphi/components/FixedTrackbar.pas
@@ -64,16 +64,16 @@ procedure TTntFixedDrawGrid.WMEraseBkgnd(var Message: TMessage);
Tested on VER320 (10.2)
Tested on VER330 (10.3) - 29 Oct 2019 - mcdurdin
+ TODO: Not yet fully verified against Vcl.Grids.pas in VER350 (11) or VER360 (12)
}
-{$IFNDEF VER340}
-{$MESSAGE WARN 'Not yet checked against Delphi 10.4'}
-{$IFNDEF VER330}
-{$IFNDEF VER320}
+{$IF Defined(VER340) or Defined(VER350) or Defined(VER360)}
+{$MESSAGE WARN 'TODO: Trackbar scrolling on bottom cell not yet checked against Delphi 10.4, 11.0 or 12.0'}
+{$ELSEIF Defined(VER320) or Defined(VER330)}
+// Tested on Delphi 10.2 (VER320) and 10.3 (VER330)
+{$ELSE}
{$MESSAGE ERROR 'Check that this fix is still applicable for a new version of Delphi. Checked against Delphi 10.2, 10.3' }
-{$ENDIF}
-{$ENDIF}
-{$ENDIF}
+{$IFEND}
procedure TTntFixedDrawGrid.MouseDown(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
diff --git a/common/windows/delphi/general/CleartypeDrawCharacter.pas b/common/windows/delphi/general/CleartypeDrawCharacter.pas
index 6d1a51976de..44562012ed2 100644
--- a/common/windows/delphi/general/CleartypeDrawCharacter.pas
+++ b/common/windows/delphi/general/CleartypeDrawCharacter.pas
@@ -591,11 +591,11 @@ function TestFont(FFontName: string): Boolean;
StrPCopy(lf.lfFaceName, FFontName); //'Code2000');
hdc := GetDC(0);
//FPlane0FontName := 'Code2000';
-{$IFDEF VER340}
+{$IF Defined(VER340) or Defined(VER350) or Defined(VER360)}
if EnumFontFamiliesEx(hdc, lf, @EnumFallbackFonts, 0, 0) <> 0 then
{$ELSE}
if EnumFontFamiliesEx(hdc, lf, @EnumFallbackFonts, 0, 0) then
-{$ENDIF}
+{$IFEND}
begin
FPlane0FontName := FFontName;
Result := True;
diff --git a/common/windows/delphi/general/JsonUtil.pas b/common/windows/delphi/general/JsonUtil.pas
index ae2b9b0e9c6..16a90bc729d 100644
--- a/common/windows/delphi/general/JsonUtil.pas
+++ b/common/windows/delphi/general/JsonUtil.pas
@@ -52,7 +52,11 @@ function JSONToString(obj: TJSONAncestor; ReplaceSlashes: Boolean = False): stri
begin
builder := TStringBuilder.Create;
try
+{$IF Defined(VER350) or Defined(VER360)}
+ obj.ToChars(builder, []);
+{$ELSE}
obj.ToChars(builder);
+{$IFEND}
Result := builder.ToString;
finally
builder.Free;
diff --git a/common/windows/delphi/tools/devtools/DevIncludePaths.pas b/common/windows/delphi/tools/devtools/DevIncludePaths.pas
index 509045d4d17..d6df62621df 100644
--- a/common/windows/delphi/tools/devtools/DevIncludePaths.pas
+++ b/common/windows/delphi/tools/devtools/DevIncludePaths.pas
@@ -144,6 +144,7 @@ class function TIncludePaths.AddPathToProjectXML(const ProjectXMLFileName, Path:
doc: IXMLDocument;
sn, node: IXMLNode;
IncludePath: string;
+ Condition: string;
I: Integer;
begin
if not FileExists(ProjectXMLFileName) then
@@ -159,16 +160,20 @@ class function TIncludePaths.AddPathToProjectXML(const ProjectXMLFileName, Path:
for I := 0 to node.ChildNodes.Count - 1 do
begin
sn := node.ChildNodes[I];
+ // Delphi 12 EnvOptions.proj emits an empty without a
+ // Condition attribute; convert defensively via VarToStrDef so a Null or
+ // Empty variant returns '' instead of raising EVariantTypeCastError in Pos().
+ Condition := VarToStrDef(sn.Attributes['Condition'], '');
if (sn.NodeName = 'PropertyGroup') and
- not VarIsNull(sn.Attributes['Condition']) and
- ((Pos('Win32', sn.Attributes['Condition']) > 0) or
- (Pos('Win64', sn.Attributes['Condition']) > 0)) then
+ ((Pos('''Win32''', Condition) > 0) or (Pos('''Win64''', Condition) > 0)) then
begin
- IncludePath := sn.ChildNodes['DelphiBrowsingPath'].NodeValue;
+ // Guard against empty child nodes (e.g. ) whose
+ // NodeValue is Null on Delphi 12 and can't coerce to a string directly.
+ IncludePath := VarToStrDef(sn.ChildNodes['DelphiBrowsingPath'].NodeValue, '');
if AddPathToIncludePath(IncludePath, Path) then
sn.ChildNodes['DelphiBrowsingPath'].nodeValue := IncludePath;
- IncludePath := sn.ChildNodes['DelphiLibraryPath'].NodeValue;
+ IncludePath := VarToStrDef(sn.ChildNodes['DelphiLibraryPath'].NodeValue, '');
if AddPathToIncludePath(IncludePath, Path) then
sn.ChildNodes['DelphiLibraryPath'].nodeValue := IncludePath;
end;
@@ -256,6 +261,7 @@ class function TIncludePaths.Reset: Boolean;
doc: IXMLDocument;
node: IXMLNode;
ProjectFileName: string;
+ Condition: string;
I: Integer;
sn: IXMLNode;
begin
@@ -296,10 +302,11 @@ class function TIncludePaths.Reset: Boolean;
for I := 0 to node.ChildNodes.Count - 1 do
begin
sn := node.ChildNodes[I];
+ // See AddPathToProjectXML: guard against Delphi 12's empty
+ // where Attributes['Condition'] returns a Null variant.
+ Condition := VarToStrDef(sn.Attributes['Condition'], '');
if (sn.NodeName = 'PropertyGroup') and
- not VarIsNull(sn.Attributes['Condition']) and
- ((Pos('Win32', sn.Attributes['Condition']) > 0) or
- (Pos('Win64', sn.Attributes['Condition']) > 0)) then
+ ((Pos('''Win32''', Condition) > 0) or (Pos('''Win64''', Condition) > 0)) then
begin
sn.ChildNodes['DelphiBrowsingPath'].NodeValue := SDefault_DelphiBrowsingPath;
sn.ChildNodes['DelphiLibraryPath'].NodeValue := SDefault_DelphiSearchPath;
diff --git a/common/windows/delphi/tools/devtools/SourceRootPath.pas b/common/windows/delphi/tools/devtools/SourceRootPath.pas
index 360477641e7..c8372c3c6e2 100644
--- a/common/windows/delphi/tools/devtools/SourceRootPath.pas
+++ b/common/windows/delphi/tools/devtools/SourceRootPath.pas
@@ -14,11 +14,19 @@ interface
{$IFDEF VER340}
const DelphiMajorVersion = '21.0';
{$ELSE}
+{$IFDEF VER350}
+const DelphiMajorVersion = '22.0';
+{$ELSE}
+{$IFDEF VER360}
+const DelphiMajorVersion = '23.0';
+{$ELSE}
ERROR: must define Delphi version
{$ENDIF}
{$ENDIF}
{$ENDIF}
{$ENDIF}
+{$ENDIF}
+{$ENDIF}
const DelphiBasePath = 'C:\Program Files (x86)\Embarcadero\Studio\' + DelphiMajorVersion + '\';
diff --git a/common/windows/delphi/web/Keyman.System.HttpServer.Base.pas b/common/windows/delphi/web/Keyman.System.HttpServer.Base.pas
index d1daf087c39..9af0ac60e96 100644
--- a/common/windows/delphi/web/Keyman.System.HttpServer.Base.pas
+++ b/common/windows/delphi/web/Keyman.System.HttpServer.Base.pas
@@ -44,10 +44,15 @@ function CrackUTF8ZeroExtendedString(CommandType: THTTPCommandType; const p: str
end;
// Indy's UTF8 handling of URLs is *completely* broken.
- // We may need to check this with updated versions of Delphi
-{$IFNDEF VER330}
- ERROR! Check if this is still needed with Delphi update
-{$ENDIF}
+ // We may need to check this with updated versions of Delphi.
+ // VER340/VER350/VER360 (10.4/11/12): unblocked but not re-verified; workaround kept.
+{$IF Defined(VER340) or Defined(VER350) or Defined(VER360)}
+ {$MESSAGE WARN 'TODO: Check if Indy URL UTF-8 handling is still needed with Delphi 10.4/11.0/12.0'}
+{$ELSEIF Defined(VER330)}
+ // Verified against Delphi 10.3 (VER330)
+{$ELSE}
+ {$MESSAGE ERROR 'Check if Indy URL UTF-8 handling is still needed with Delphi update'}
+{$IFEND}
SetLength(s, p.Length);
for i := 1 to p.Length do
diff --git a/developer/src/ext/jedi/jcl/jcl/source/common/JclSynch.pas b/developer/src/ext/jedi/jcl/jcl/source/common/JclSynch.pas
index f73b1722b09..c014318f09e 100644
--- a/developer/src/ext/jedi/jcl/jcl/source/common/JclSynch.pas
+++ b/developer/src/ext/jedi/jcl/jcl/source/common/JclSynch.pas
@@ -1075,7 +1075,10 @@ constructor TJclMutex.Create(SecAttr: PSecurityAttributes; InitialOwner: Boolean
begin
inherited Create;
FName := Name;
- FHandle := JclWin32.CreateMutex(SecAttr, InitialOwner, PChar(Name));
+ // Keyman patch (D12, source-compiled JCL): JclWin32.CreateMutex is an external
+ // decl with a BOOL param; D12 rejects Boolean->BOOL there (E2010). Route via the
+ // RTL like OpenMutex below. Only bites when JCL is built from source.
+ FHandle := {$IFDEF HAS_UNITSCOPE}Winapi.{$ENDIF}Windows.CreateMutex(SecAttr, BOOL(InitialOwner), PChar(Name));
if FHandle = 0 then
raise EJclMutexError.CreateRes(@RsSynchCreateMutex);
FExisted := GetLastError = ERROR_ALREADY_EXISTS;
diff --git a/developer/src/ext/jedi/jvcl/jvcl/run/JvComponent.pas b/developer/src/ext/jedi/jvcl/jvcl/run/JvComponent.pas
index 1877ee48efe..9c0cd1e9719 100644
--- a/developer/src/ext/jedi/jvcl/jvcl/run/JvComponent.pas
+++ b/developer/src/ext/jedi/jvcl/jvcl/run/JvComponent.pas
@@ -123,8 +123,10 @@ constructor TJvForm.Create(AOwner: TComponent);
finally
Exclude(FFormState, fsCreating);
end;
+ {$IFDEF HAS_PROPERTY_OLDCREATEORDER}
if OldCreateOrder then
DoCreate;
+ {$ENDIF HAS_PROPERTY_OLDCREATEORDER}
end;
finally
GlobalNameSpace.EndWrite;
diff --git a/developer/src/ext/mbcolor/mxs.inc b/developer/src/ext/mbcolor/mxs.inc
index 41b82ad96e5..358ee4b84b3 100644
--- a/developer/src/ext/mbcolor/mxs.inc
+++ b/developer/src/ext/mbcolor/mxs.inc
@@ -7,6 +7,26 @@
{$define DELPHI_10_UP}
{$endif}
+ // Keyman patch (D11/12, vendored mbcolor): define DELPHI_*_UP on VER350/VER360
+ // too, else HTMLColors.pas drops "uses Variants" and Null won't resolve.
+ {$ifdef VER350}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$define DELPHI_7_UP}
+ {$define DELPHI_8_UP}
+ {$define DELPHI_9_UP}
+ {$define DELPHI_10_UP}
+ {$endif}
+
+ {$ifdef VER360}
+ {$define DELPHI_5_UP}
+ {$define DELPHI_6_UP}
+ {$define DELPHI_7_UP}
+ {$define DELPHI_8_UP}
+ {$define DELPHI_9_UP}
+ {$define DELPHI_10_UP}
+ {$endif}
+
{$ifdef VER330}
{$define DELPHI_5_UP}
{$define DELPHI_6_UP}
diff --git a/docs/build/windows.md b/docs/build/windows.md
index edc756340ff..9e482a894cc 100644
--- a/docs/build/windows.md
+++ b/docs/build/windows.md
@@ -184,8 +184,8 @@ In bash, run the following commands:
cd /c/Projects/keyman
git clone https://github.com/emscripten-core/emsdk
cd emsdk
-emsdk install 3.1.58
-emsdk activate 3.1.58
+emsdk install 3.1.64
+emsdk activate 3.1.64
cd upstream/emscripten
npm install
```
@@ -195,8 +195,8 @@ If you are updating an existing install of Emscripten:
```bash
cd emsdk
git pull
-emsdk install 3.1.58
-emsdk activate 3.1.58
+emsdk install 3.1.64
+emsdk activate 3.1.64
cd upstream/emscripten
npm install
```
@@ -257,6 +257,11 @@ of appropriate node versions during builds.
for a short time. (We are actively working to remove Delphi dependencies
given the licensing issues with using it.)
+ * If you have Delphi 11 or 12 with a CLI-capable license (Professional or
+ higher), set `KEYMAN_DELPHI_VERSION` to the Studio path — `22.0` for
+ Delphi 11, `23.0` for Delphi 12 — before running `build.sh`. The default
+ (`20.0`, Delphi 10.3) is preserved when the variable is unset.
+
Start Delphi IDE once after installation as it will create various environment
files and take you through required registration.
diff --git a/resources/build/win/configure_environment.inc.sh b/resources/build/win/configure_environment.inc.sh
index d0bce72a812..1d274c67be1 100644
--- a/resources/build/win/configure_environment.inc.sh
+++ b/resources/build/win/configure_environment.inc.sh
@@ -65,9 +65,11 @@ _build_vs_environment() {
_locate_rsvars() {
#
- # Delphi Compiler Configuration - Delphi 10.3.2
+ # Delphi Compiler Configuration - defaults to Delphi 10.3 (BDS 20.0).
+ # Override via the KEYMAN_DELPHI_VERSION environment variable to build with
+ # a newer Delphi installation (e.g., KEYMAN_DELPHI_VERSION=23.0 for Delphi 12).
#
- DELPHI_VERSION=20.0
+ DELPHI_VERSION="${KEYMAN_DELPHI_VERSION:-20.0}"
DCC32PATH="$(cygpath -u "$ProgramFilesx86\\Embarcadero\\Studio\\$DELPHI_VERSION\\bin")"
RSVars_path="$DCC32PATH/rsvars.bat"
}
diff --git a/resources/build/win/delphi_environment.inc.sh b/resources/build/win/delphi_environment.inc.sh
index 0465bcb6930..a68b08421c4 100644
--- a/resources/build/win/delphi_environment.inc.sh
+++ b/resources/build/win/delphi_environment.inc.sh
@@ -13,7 +13,10 @@
DELPHIWARNINGS=(-W-MESSAGE_DIRECTIVE -W-IMPLICIT_STRING_CAST -W-IMPLICIT_STRING_CAST_LOSS -W-EXPLICIT_STRING_CAST -W-EXPLICIT_STRING_CAST_LOSS -W-CVT_WCHAR_TO_ACHAR -W-CVT_NARROWING_STRING_LOST -W-CVT_ACHAR_TO_WCHAR -W-CVT_WIDENING_STRING_LOST -W-UNICODE_TO_LOCALE -W-LOCALE_TO_UNICODE -W-IMPLICIT_VARIANTS -W-IMPLICIT_INTEGER_CAST_LOSS -W-IMPLICIT_CONVERSION_LOSS -W-COMBINING_SIGNED_UNSIGNED64 -W-COMBINING_SIGNED_UNSIGNED64)
# !ENDIF
-DELPHI_VERSION=20.0
+# DELPHI_VERSION defaults to Delphi 10.3 (BDS 20.0). Override via the
+# KEYMAN_DELPHI_VERSION environment variable to build with a newer Delphi
+# installation (e.g., KEYMAN_DELPHI_VERSION=23.0 for Delphi 12).
+DELPHI_VERSION="${KEYMAN_DELPHI_VERSION:-20.0}"
DCC32PATH="$(cygpath -u "$ProgramFilesx86\\Embarcadero\\Studio\\$DELPHI_VERSION\\bin")"
source "$KEYMAN_ROOT/resources/build/win/delphi_environment_generated.inc.sh"
diff --git a/resources/builder.inc.sh b/resources/builder.inc.sh
index 6ec2fbcebff..7e9ea36a0e4 100755
--- a/resources/builder.inc.sh
+++ b/resources/builder.inc.sh
@@ -2323,7 +2323,8 @@ builder_describe_platform() {
# Detect delphi compiler (see also delphi_environment.inc.sh)
if builder_is_windows; then
local ProgramFilesx86="$(cygpath -w -F 42)"
- if [[ -x "$(cygpath -u "$ProgramFilesx86\\Embarcadero\\Studio\\20.0\\bin\\dcc32.exe")" ]]; then
+ local _delphi_version="${KEYMAN_DELPHI_VERSION:-20.0}"
+ if [[ -x "$(cygpath -u "$ProgramFilesx86\\Embarcadero\\Studio\\$_delphi_version\\bin\\dcc32.exe")" ]]; then
builder_installed_tools+=(delphi)
fi
fi
diff --git a/windows/src/global/delphi/cust/CustomisationStorage.pas b/windows/src/global/delphi/cust/CustomisationStorage.pas
index db4e5c018fa..1af7f2a8a5e 100644
--- a/windows/src/global/delphi/cust/CustomisationStorage.pas
+++ b/windows/src/global/delphi/cust/CustomisationStorage.pas
@@ -157,8 +157,13 @@ function TCustomisationStorage.GetFileOfType(FileType: TCustFileType; StartIndex
{ TCustFileList }
function TCustFileList.AddCustFile: TCustFile;
+var
+ NewObj: TObject;
begin
- Result := Items[inherited Add(FObjectClass.Create)];
+ // Delphi 12 dcc64 rejects the inline form with E2010 'TCustFile' and 'TObject';
+ // splitting via a local TObject makes the widening explicit.
+ NewObj := FObjectClass.Create;
+ Result := Items[inherited Add(NewObj)];
end;
constructor TCustFileList.Create(AObjectClass: TCustFileClass);