Skip to content

Commit 28c184a

Browse files
committed
POC: Use ARm64EC libraries for X64 support on ARM64 systems
1 parent a6c67b6 commit 28c184a

File tree

5 files changed

+76
-48
lines changed

5 files changed

+76
-48
lines changed

nvdaHelper/archBuild_sconscript

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
# This file may be used under the terms of the GNU General Public License, version 2 or later.
44
# For more details see: https://www.gnu.org/licenses/gpl-2.0.html
55

6+
import glob
7+
import os
68
import sysconfig
79

10+
from SCons.Tool.MSCommon.vc import find_vc_pdir
11+
812
Import(
913
"env",
1014
"sourceDir",
@@ -68,21 +72,23 @@ def COMProxyDllBuilder(env, target, source, proxyClsid):
6872

6973
env.AddMethod(COMProxyDllBuilder, "COMProxyDll")
7074

71-
# We only support compiling with MSVC 14.2 (2019) or newer
72-
if not env.get("MSVC_VERSION") or tuple(map(int, env.get("MSVC_VERSION").split("."))) < (14, 2):
73-
raise RuntimeError("Visual C++ 14.2 (Visual Studio 2019) or newer not found")
75+
# We only support compiling with MSVC 14.3 (2022) or newer
76+
if not env.get("MSVC_VERSION") or tuple(map(int, env.get("MSVC_VERSION").split("."))) < (14, 3):
77+
raise RuntimeError("Visual C++ 14.3 (Visual Studio 2022) or newer not found")
7478

7579
PYTHON_PLATFORM = sysconfig.get_platform()
7680
TARGET_ARCH = env["TARGET_ARCH"]
81+
isArm64EC = env.get("isArm64EC", False)
7782

7883

7984
isNVDACoreArch = (
8085
(PYTHON_PLATFORM == "win32" and TARGET_ARCH == "x86")
8186
or (PYTHON_PLATFORM == "win-amd64" and TARGET_ARCH == "x86_64")
8287
or (PYTHON_PLATFORM == "win-arm64" and TARGET_ARCH == "arm64")
8388
)
84-
85-
89+
isNVDAHelperLocalArch = isNVDACoreArch or (
90+
PYTHON_PLATFORM == "win-amd64" and TARGET_ARCH == "arm64" and isArm64EC
91+
)
8692
debug = env["nvdaHelperDebugFlags"]
8793
release = env["release"]
8894
signExec = env["signExec"] if (bool(env["certFile"]) ^ bool(env["apiSigningToken"])) else None
@@ -124,7 +130,7 @@ env.Append(
124130
if TARGET_ARCH == "x86_64":
125131
env.Append(MIDLFLAGS="/x64")
126132
elif TARGET_ARCH == "arm64":
127-
env.Append(MIDLFLAGS="/arm64")
133+
env.Append(MIDLFLAGS="/x64" if isArm64EC else "/arm64")
128134
else:
129135
env.Append(MIDLFLAGS="/win32")
130136

@@ -146,7 +152,8 @@ if "RTC" in debug:
146152
# We always want debug symbols
147153
env.Append(PDB="${TARGET}.pdb")
148154
env.Append(
149-
LINKFLAGS="/OPT:REF"
155+
LINKFLAGS="/OPT:REF",
156+
ARFLAGS="/LTCG"
150157
) # having symbols usually turns this off but we have no need for unused symbols
151158

152159
env.Append(
@@ -160,6 +167,11 @@ env.Append(
160167
]
161168
)
162169

170+
if isArm64EC:
171+
env.Append(CCFLAGS="/arm64EC")
172+
env.Append(LINKFLAGS="/machine:arm64ec")
173+
env.Append(ARFLAGS="/machine:arm64ec")
174+
163175
if "debugCRT" in debug:
164176
env.Append(CCFLAGS=["/MTd"])
165177
else:
@@ -221,7 +233,7 @@ Export("detoursLib")
221233
apiHookObj = env.Object("apiHook", "common/apiHook.cpp")
222234
Export("apiHookObj")
223235

224-
if isNVDACoreArch:
236+
if isNVDAHelperLocalArch:
225237
localLib = env.SConscript("local/sconscript")
226238
Export("localLib")
227239
if signExec:
@@ -250,11 +262,41 @@ if signExec:
250262
env.AddPostAction(remoteLib[0], [signExec])
251263
env.Install(libInstallDir, remoteLib)
252264

253-
remoteLoaderProgram = env.SConscript("remoteLoader/sconscript")
254-
if signExec:
255-
env.AddPostAction(remoteLoaderProgram, [signExec])
256-
env.Install(libInstallDir, remoteLoaderProgram)
265+
if not isNVDAHelperLocalArch:
266+
remoteLoaderProgram = env.SConscript("remoteLoader/sconscript")
267+
if signExec:
268+
env.AddPostAction(remoteLoaderProgram, [signExec])
269+
env.Install(libInstallDir, remoteLoaderProgram)
257270

258271
if isNVDACoreArch:
259272
thirdPartyEnv.SConscript("espeak/sconscript")
260273
thirdPartyEnv.SConscript("liblouis/sconscript")
274+
275+
# UWP dlls can only be dynamically linked with the CRT,
276+
# but some systems might not have this version of the CRT.
277+
# Therefore, we must include it.
278+
# VS keeps changing the path to reflect the latest major.minor.build version which we canot easily find out.
279+
# Therefore Search these versioned directories from newest to oldest to collect all the files we need.
280+
msvc = env.get("MSVC_VERSION")
281+
vcRedistDirs = glob.glob(
282+
os.path.join(
283+
find_vc_pdir(msvc, env), rf"Redist\MSVC\{msvc[:2]}*\x86\Microsoft.VC{msvc.replace('.', '')}.CRT"
284+
)
285+
)
286+
if len(vcRedistDirs) == 0:
287+
raise RuntimeError(
288+
"Could not locate vc redistributables. Perhaps the Universal Windows Platform component in visual Studio is not installed"
289+
)
290+
vcRedistDirs.sort(reverse=True)
291+
for fn in ("msvcp140.dll", "vccorlib140.dll", "vcruntime140.dll"):
292+
for vcRedistDir in vcRedistDirs:
293+
path = os.path.join(vcRedistDir, fn)
294+
if os.path.isfile(path):
295+
env.Install(sourceDir, path)
296+
break
297+
else:
298+
raise RuntimeError(
299+
"Could not locate %s. Perhaps the Universal Windows Platform component in visual Studio is not installed"
300+
% fn
301+
)
302+

nvdaHelper/localWin10/sconscript

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@
1212
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
1313
###
1414

15-
import os
16-
import glob
17-
18-
from SCons.Tool.MSCommon.vc import find_vc_pdir
19-
2015
Import(
2116
"env",
2217
"sourceDir",
@@ -41,32 +36,4 @@ localWin10Lib = env.SharedLibrary(
4136
], # noqa: F821
4237
)
4338

44-
# UWP dlls can only be dynamically linked with the CRT,
45-
# but some systems might not have this version of the CRT.
46-
# Therefore, we must include it.
47-
# VS keeps changing the path to reflect the latest major.minor.build version which we canot easily find out.
48-
# Therefore Search these versioned directories from newest to oldest to collect all the files we need.
49-
msvc = env.get("MSVC_VERSION")
50-
vcRedistDirs = glob.glob(
51-
os.path.join(
52-
find_vc_pdir(msvc, env), rf"Redist\MSVC\{msvc[:2]}*\x86\Microsoft.VC{msvc.replace('.', '')}.CRT"
53-
)
54-
)
55-
if len(vcRedistDirs) == 0:
56-
raise RuntimeError(
57-
"Could not locate vc redistributables. Perhaps the Universal Windows Platform component in visual Studio is not installed"
58-
)
59-
vcRedistDirs.sort(reverse=True)
60-
for fn in ("msvcp140.dll", "vccorlib140.dll", "vcruntime140.dll"):
61-
for vcRedistDir in vcRedistDirs:
62-
path = os.path.join(vcRedistDir, fn)
63-
if os.path.isfile(path):
64-
env.Install(sourceDir, path)
65-
break
66-
else:
67-
raise RuntimeError(
68-
"Could not locate %s. Perhaps the Universal Windows Platform component in visual Studio is not installed"
69-
% fn
70-
)
71-
7239
Return(["localWin10Lib"])

sconstruct

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ sourceLibDir64 = sourceDir.Dir("lib/x64")
196196
Export("sourceLibDir64")
197197
sourceLibDirArm64 = sourceDir.Dir("lib/arm64")
198198
Export("sourceLibDirArm64")
199+
sourceLibDirArm64EC = sourceDir.Dir("lib/arm64ec")
200+
Export("sourceLibDirArm64EC")
199201
buildDir = Dir("build")
200202
outFilePrefix = "nvda{type}_{version}".format(type="" if release else "_snapshot", version=version)
201203
Export("outFilePrefix")
@@ -247,10 +249,13 @@ archTools = ["default", "midl", "msrpc"]
247249
env32 = env.Clone(TARGET_ARCH="x86", tools=archTools)
248250
env64 = env.Clone(TARGET_ARCH="x86_64", tools=archTools)
249251
envArm64 = env.Clone(TARGET_ARCH="arm64", tools=archTools)
252+
envArm64EC = envArm64.Clone()
253+
envArm64EC["isArm64EC"] = True
250254
# Hack around odd bug where some tool [after] msvc states that static and shared objects are different
251255
env32["STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME"] = 1
252256
env64["STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME"] = 1
253257
envArm64["STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME"] = 1
258+
envArm64EC["STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME"] = 1
254259

255260
if platform.architecture()[0].startswith("64"):
256261
env = env64
@@ -276,6 +281,7 @@ resFile = env.RES(
276281
env32["projectResFile"] = resFile
277282
env64["projectResFile"] = resFile
278283
envArm64["projectResFile"] = resFile
284+
envArm64EC["projectResFile"] = resFile
279285

280286
# Fill sourceDir with anything provided for it by miscDeps
281287
env.recursiveCopy(sourceDir, Dir("miscdeps/source"))
@@ -306,6 +312,11 @@ envArm64.SConscript(
306312
exports={"env": envArm64, "clientInstallDir": clientDir.Dir("arm64"), "libInstallDir": sourceLibDirArm64},
307313
variant_dir="build/arm64",
308314
)
315+
envArm64EC.SConscript(
316+
"nvdaHelper/archBuild_sconscript",
317+
exports={"env": envArm64EC, "clientInstallDir": clientDir.Dir("arm64ec"), "libInstallDir": sourceLibDirArm64EC},
318+
variant_dir="build/arm64ec",
319+
)
309320

310321
# Allow all NVDA's gettext po files to be compiled in source/locale
311322
for po in env.Glob(sourceDir.path + "/locale/*/lc_messages/*.po"):

source/NVDAHelper/__init__.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,19 @@
3434

3535
import buildVersion
3636
import globalVars
37+
import winVersion
3738

3839
versionedLibX86Path = os.path.join(globalVars.appDir, "lib", "x86")
3940
versionedLibARM64Path = os.path.join(globalVars.appDir, "lib", "arm64")
40-
versionedLibAMD64Path = os.path.join(globalVars.appDir, "lib", "x64")
41+
arch = winVersion.getWinVer().processorArchitecture
42+
versionedLibAMD64Path = os.path.join(
43+
globalVars.appDir,
44+
"lib",
45+
(
46+
# On ARM64 Windows, we use arm64ec libraries for interop with x64 code.
47+
"arm64ec" if arch == "ARM64" else "x64"
48+
),
49+
)
4150

4251
import NVDAState # noqa: E402
4352

@@ -86,7 +95,6 @@ def warnDeprecatedWithReplacement(deprecated: str, replacement: str):
8695

8796

8897
from . import localLib # noqa: E402
89-
import winVersion # noqa: E402
9098
import winKernel # noqa: E402
9199
import config # noqa: E402
92100
import winUser # noqa: E402
@@ -882,7 +890,6 @@ def initialize() -> None:
882890
log.error("Error installing IA2 support")
883891
# Manually start the in-process manager thread for this NVDA main thread now, as a slow system can cause this action to confuse WX
884892
_remoteLib.initInprocManagerThreadIfNeeded()
885-
arch = winVersion.getWinVer().processorArchitecture
886893
if arch == "AMD64" and coreArchLibPath != versionedLibAMD64Path:
887894
_remoteLoaderAMD64 = _RemoteLoader(versionedLibAMD64Path)
888895
elif arch == "ARM64" and coreArchLibPath != versionedLibARM64Path:

source/setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ def _genManifestTemplate(shouldHaveUIAccess: bool) -> tuple[int, int, bytes]:
259259
("lib/x86/%s" % version, glob("lib/x86/*.dll") + glob("lib/x86/*.manifest")),
260260
("lib/x64/%s" % version, glob("lib/x64/*.dll") + glob("lib/x64/*.manifest")),
261261
("lib/arm64/%s" % version, glob("lib/arm64/*.dll") + glob("lib/arm64/*.manifest")),
262+
("lib/arm64ec/%s" % version, glob("lib/arm64ec/*.dll") + glob("lib/arm64ec/*.manifest")),
262263
("waves", glob("waves/*.wav")),
263264
("images", glob("images/*.ico")),
264265
("fonts", glob("fonts/*.ttf")),

0 commit comments

Comments
 (0)