Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ be112ed44cb68f622a770c72fd29dd10899e9d07
73a3529d8b10eb73a45117eead906c0a9cb4c41b
# String concatenation
9616ef6b18b5c5a5b64037a640d205868e66b964
# Run runlint.bat on scons and .pyw files
0643d6e41089fb6aed22d489c7ccc3f32381f199
8 changes: 3 additions & 5 deletions nvdaHelper/archBuild_sconscript
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ isNVDACoreArch = (
or (PYTHON_PLATFORM == "win-arm64" and TARGET_ARCH == "arm64")
)
isNVDAHelperLocalArch = isNVDACoreArch or (
PYTHON_PLATFORM == "win-amd64"
and TARGET_ARCH == "arm64"
and isArm64EC
PYTHON_PLATFORM == "win-amd64" and TARGET_ARCH == "arm64" and isArm64EC
)
debug = env["nvdaHelperDebugFlags"]
release = env["release"]
Expand Down Expand Up @@ -266,7 +264,7 @@ if not isNVDAHelperLocalArch:
if isNVDACoreArch:
sonicLib = thirdPartyEnv.SConscript("sonic/sconscript")
Export("sonicLib")
env.Install(sourceDir.Dir('synthDrivers'), sonicLib)
env.Install(sourceDir.Dir("synthDrivers"), sonicLib)
thirdPartyEnv.SConscript("espeak/sconscript")
thirdPartyEnv.SConscript("liblouis/sconscript")
thirdPartyEnv.SConscript("javaAccessBridge/sconscript")
Expand All @@ -275,4 +273,4 @@ if isNVDACoreArch:
if TARGET_ARCH == "x86":
sonicLib = thirdPartyEnv.SConscript("sonic/sconscript")
Export("sonicLib")
env.Install(sourceDir.Dir('_synthDrivers32'), sonicLib)
env.Install(sourceDir.Dir("_synthDrivers32"), sonicLib)
7 changes: 4 additions & 3 deletions nvdaHelper/espeak/sconscript
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ freeLibrary.argtypes = (ctypes.wintypes.HANDLE,)
DLL_DIRECTORY_COOKIE = ctypes.c_void_p
AddDllDirectory = ctypes.windll.kernel32.AddDllDirectory
AddDllDirectory.argtypes = (ctypes.wintypes.LPCWSTR,)
AddDllDirectory.restype = DLL_DIRECTORY_COOKIE
AddDllDirectory.restype = DLL_DIRECTORY_COOKIE

# Add the directory of the sonic dll to our DLL search path
# So that eSpeak.dll cna find it when being loaded to compile dictionaries etc.
sonicDllDir = sonicLib[0].get_dir().abspath
AddDllDirectory(sonicDllDir)


class AutoFreeCDLL(ctypes.CDLL):
def __del__(self):
Expand Down Expand Up @@ -1053,6 +1053,7 @@ def espeak_compileDict_buildAction(
espeak.espeak_Terminate()
return ACTION_SUCCESS


espeakLib = env.SharedLibrary(
target="espeak",
srcdir=espeakSrcDir.Dir("libespeak-ng").abspath,
Expand Down Expand Up @@ -1112,7 +1113,7 @@ espeakLib = env.SharedLibrary(
# com\ttsengine.cpp
# We do not use the ASYNC compile option in espeak.
],
LIBS=["advapi32", sonicLib[2]] ,
LIBS=["advapi32", sonicLib[2]],
LIBPATH=".",
)

Expand Down
5 changes: 3 additions & 2 deletions nvdaHelper/javaAccessBridge/sconscript
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ match TARGET_ARCH:
jabDllName = None
if jabDllName:
env.Command(
sourceDir.File("windowsaccessbridge.dll"), env.Dir("#include/javaAccessBridge32").File(jabDllName), Copy("$TARGET", "$SOURCE")
sourceDir.File("windowsaccessbridge.dll"),
env.Dir("#include/javaAccessBridge32").File(jabDllName),
Copy("$TARGET", "$SOURCE"),
)

2 changes: 1 addition & 1 deletion nvdaHelper/sonic/sconscript
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ env: SCons.Environment.Environment = thirdPartyEnv.Clone()

env.Append(CPPPATH=sonicSrcDir)

sonicObj = env.Object("sonic.obj", sonicSrcDir.File('sonic.c'))
sonicObj = env.Object("sonic.obj", sonicSrcDir.File("sonic.c"))

sonicLib = env.SharedLibrary(
target="sonic",
Expand Down
98 changes: 58 additions & 40 deletions sconstruct
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@
# This file may be used under the terms of the GNU General Public License, version 2 or later.
# For more details see: https://www.gnu.org/licenses/gpl-2.0.html

import copy
import multiprocessing
import os
import shutil
import platform
import sys
from pathlib import Path

from SCons.Errors import UserError

# Ensure we are inside the Python virtual environment, and that it is started with uv
virtualEnv = os.getenv("VIRTUAL_ENV")
Expand All @@ -27,7 +24,6 @@ if not virtualEnv or not uv or Path.cwd() != Path(virtualEnv).parent:

import time # noqa: E402
import importlib.util # noqa: E402
import winreg # noqa: E402


def recursiveCopy(env, targetDir, sourceDir):
Expand Down Expand Up @@ -310,7 +306,11 @@ envArm64.SConscript(
)
envArm64EC.SConscript(
"nvdaHelper/archBuild_sconscript",
exports={"env": envArm64EC, "clientInstallDir": clientDir.Dir("arm64ec"), "libInstallDir": sourceLibDirArm64EC},
exports={
"env": envArm64EC,
"clientInstallDir": clientDir.Dir("arm64ec"),
"libInstallDir": sourceLibDirArm64EC,
},
variant_dir="build/arm64ec",
)

Expand Down Expand Up @@ -390,20 +390,22 @@ def NVDADistGenerator(target, source, env, for_signature):
updateVersionType = env["updateVersionType"] or None
# Any '\n' characters written are translated to the system default line separator, os.linesep.
action = [
lambda target, source, env: open(buildVersionFn, "w", encoding="utf-8").write(
"version = {version!r}\n"
"publisher = {publisher!r}\n"
"updateVersionType = {updateVersionType!r}\n"
"version_build = {version_build!r}\n".format(
version=version,
publisher=publisher,
updateVersionType=updateVersionType,
version_build=version_build,
lambda target, source, env: (
open(buildVersionFn, "w", encoding="utf-8").write(
"version = {version!r}\n"
"publisher = {publisher!r}\n"
"updateVersionType = {updateVersionType!r}\n"
Comment on lines +396 to +400
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

Optional: this uses open(...).write(...) without an explicit close. Using a context manager (or Path.write_text) would guarantee the handle is closed before later build steps (e.g., cleanup/Delete on Windows) and is clearer to readers.

Copilot uses AI. Check for mistakes.
"version_build = {version_build!r}\n".format(
version=version,
publisher=publisher,
updateVersionType=updateVersionType,
version_build=version_build,
)
)
# In Python 3 write returns the number of characters written,
# which scons treats as an error code.
and None
)
# In Python 3 write returns the number of characters written,
# which scons treats as an error code.
and None
]

buildCmd = ["cd", source[0].path, "&&", sys.executable]
Expand All @@ -428,9 +430,9 @@ def NVDADistGenerator(target, source, env, for_signature):
for prog in executablesToSign:
path = os.path.join(target[0].abspath, prog)
action.append(
lambda target, source, env, pathByVal=path: env["signExec"](
[File(pathByVal)], source, env
) if os.path.isfile(pathByVal) else None
lambda target, source, env, pathByVal=path: (
env["signExec"]([File(pathByVal)], source, env) if os.path.isfile(pathByVal) else None
)
)

action.extend((Delete(buildVersionFn), Delete(importlib.util.cache_from_source(buildVersionFn))))
Expand Down Expand Up @@ -708,22 +710,22 @@ synthDriverHost32Runtime = env.Command(
source="#runtime-builders/synthDriverHost32/setup-runtime.py",
chdir=Dir("#runtime-builders/synthDriverHost32"),
action=[
[
"uv",
"run",
"-v",
"--no-active",
"--directory",
".",
"python",
"setup-runtime.py",
"--dest-dir",
"dist",
"--version",
version,
"--publisher",
publisher,
],
[
"uv",
"run",
"-v",
"--no-active",
"--directory",
".",
"python",
"setup-runtime.py",
"--dest-dir",
"dist",
"--version",
version,
"--publisher",
publisher,
],
r"rmdir /s /q ..\..\source\lib\x86\synthDriverHost-runtime || exit 0",
r"xcopy /e /i /y dist ..\..\source\lib\x86\synthDriverHost-runtime",
],
Expand All @@ -734,9 +736,25 @@ env.Alias("synthDriverHost32Runtime", synthDriverHost32Runtime)

# Copy sapi5 and sonic Python files to the _synthDrivers32 directory
# As they require no changes, but need to be in an isolated directory for 32 bit.
env.Command(sourceDir.File('_synthDrivers32/_sonic.py'), sourceDir.File('synthDrivers/_sonic.py'), Copy("$TARGET", "$SOURCE"))
env.Command(sourceDir.File('_synthDrivers32/_sapi5.py'), sourceDir.File('synthDrivers/sapi5.py'), Copy("$TARGET", "$SOURCE"))
env.Command(
sourceDir.File("_synthDrivers32/_sonic.py"),
sourceDir.File("synthDrivers/_sonic.py"),
Copy("$TARGET", "$SOURCE"),
)
env.Command(
sourceDir.File("_synthDrivers32/_sapi5.py"),
sourceDir.File("synthDrivers/sapi5.py"),
Copy("$TARGET", "$SOURCE"),
)

# Parts of the speech package are quired by the 32 bit synthDriver host tunrime
env.Command(sourceDir.File('_bridge/runtimes/synthDriverHost/speech/commands.py'), sourceDir.File('speech/commands.py'), Copy("$TARGET", "$SOURCE"))
env.Command(sourceDir.File('_bridge/runtimes/synthDriverHost/speech/types.py'), sourceDir.File('speech/types.py'), Copy("$TARGET", "$SOURCE"))
env.Command(
sourceDir.File("_bridge/runtimes/synthDriverHost/speech/commands.py"),
sourceDir.File("speech/commands.py"),
Copy("$TARGET", "$SOURCE"),
)
env.Command(
sourceDir.File("_bridge/runtimes/synthDriverHost/speech/types.py"),
sourceDir.File("speech/types.py"),
Copy("$TARGET", "$SOURCE"),
)
7 changes: 6 additions & 1 deletion source/_bridge/runtimes/synthDriverHost/main.pyw
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import tempfile
from winBindings.kernel32 import GetCurrentProcessId

oldRecordFactory = logging.getLogRecordFactory()


def recordFactory(*args, **kwargs):
record = oldRecordFactory(*args, **kwargs)
frame = inspect.currentframe()
Expand All @@ -27,6 +29,8 @@ def recordFactory(*args, **kwargs):
# co_qualname may be unavailable for some frames; in that case, keep the default record.name
pass
return record


logging.setLogRecordFactory(recordFactory)

exeName = os.path.splitext(os.path.basename(sys.executable))[0]
Expand All @@ -36,7 +40,7 @@ logging.basicConfig(
filename=logPath,
filemode="w",
level=logging.DEBUG,
format="%(levelname)s - %(module)s.%(name)s (%(asctime)s):\n%(message)s"
format="%(levelname)s - %(module)s.%(name)s (%(asctime)s):\n%(message)s",
)
log = logging.getLogger(exeName)
# No comtypes debug logging
Expand All @@ -46,6 +50,7 @@ log.info(f"Logging initialized, log file: {logPath}")
try:
gettext.install("nvda", names=["pgettext", "npgettext", "ngettext"])
import core

core.main()
except Exception:
log.exception("Unhandled exception")
Loading