fix: add macOS Apple Silicon support for VHDL and Verilog language servers#845
Open
alexmodrono wants to merge 11 commits intoTerosTechnology:devfrom
Open
fix: add macOS Apple Silicon support for VHDL and Verilog language servers#845alexmodrono wants to merge 11 commits intoTerosTechnology:devfrom
alexmodrono wants to merge 11 commits intoTerosTechnology:devfrom
Conversation
The teroshdl.verible.restart command was registered twice: once inside the Verilbe_lsp constructor and again as a fallback in configure_verilog() when the language server was not found. This caused the extension to fail to activate with "command already exists" on every startup. Remove the redundant registration from configure_verilog().
…uage servers vhdl-ls (rust_hdl.ts): - Replace the hardcoded Linux binary name with platform/arch detection, adding proper support for vhdl_ls-aarch64-apple-darwin (Apple Silicon) and vhdl_ls-x86_64-apple-darwin (Intel Mac). - Move the binary liveness check before the language client is created, so a missing or non-executable bundled binary is caught early. - If the bundled binary fails, fall back to system-installed vhdl_ls at /opt/homebrew/bin, /usr/local/bin, and PATH. verible (verible.ts): - Apply the same early liveness check + system fallback pattern: tries /opt/homebrew/bin/verible-verilog-ls, /usr/local/bin/verible-verilog-ls, then the bare command from PATH. - Remove stale commented-out middleware block. Both changes make the extension usable on macOS where bundled binaries may be missing or built for a different architecture.
Add the vhdl_ls-aarch64-apple-darwin binary and its accompanying vhdl_libraries, matching the structure already in place for Windows and Linux. This makes the extension work out-of-the-box on Macs with Apple Silicon without requiring a separate vhdl-ls installation.
When vhdl-ls is not installed system-wide, it searches for the standard VHDL libraries (IEEE, std, etc.) relative to its own binary at hardcoded paths like ../vhdl_libraries and /usr/lib/rust_hdl/vhdl_libraries. These paths resolve correctly on Linux but not on macOS, causing vhdl-ls to panic on startup with "Couldn't find installed libraries". Fix: compute the path of the bundled vhdl_libraries/vhdl_ls.toml at startup and pass it via --libraries when it exists, making vhdl-ls always able to locate the IEEE standard libraries.
…flag vhdl_ls 0.84.0's --libraries flag expects a path to the DIRECTORY containing vhdl_ls.toml, not the path to the file itself. Passing the file path caused "Not a directory (os error 20)" which prevented the standard IEEE/std libraries from loading, ultimately crashing the server when analyzing any VHDL file. Also fix get_toml() to remap the reserved 'work' library name to teroshdlDefault, since vhdl_ls 0.84.0 rejects 'work' as an explicit library name in the project configuration.
…th corruption context.asAbsolutePath() uses path.join() internally, which prepends the extension path to any string — including absolute system paths like /opt/homebrew/bin/vhdl_ls, corrupting them to <extension-dir>/opt/homebrew/bin/vhdl_ls (which doesn't exist). Fix: store the already-resolved absolute path in the languageServer module variable from the start. getServerOptionsEmbedded now uses it directly without calling asAbsolutePath, making the system binary fallback actually work correctly. Same fix applied to verible.ts.
…ontent VSG formatter (vsg.ts): The 63e925f commit broke formatting by treating VSG exit code 1 as a failure. VSG exits with 1 when violations are found and fixed (--fix mode) — the normal formatting outcome. Only codes > 1 indicate real errors. Result: edits were never applied when VSG actually did formatting work. teroshdl.ts: Initialize the VHDL_LS_CONFIG temp file with '[libraries]\n' instead of an empty string. An empty file causes vhdl_ls to report "missing field libraries" and then fall through to search the workspace root for vhdl_ls.toml (which may have invalid content like 'work' library).
…startup - Pass ...process.env to vhdl_ls server options so it inherits PATH, HOME, TMPDIR and other OS-level env vars; previously only VHDL_LS_CONFIG was passed, stripping the entire environment and causing unexpected crashes. - Remove redundant explicit forceRefresh() call in init_tree_views(): Tree_view_manager's constructor already emits GLOBAL_REFRESH which synchronously triggers refreshToml() → forceRefresh() → restart. The duplicate call was scheduling a second concurrent restart, causing an extra SIGKILL cycle against the freshly-started language server.
…rors child_process.exec() provides the real exit code via error.code on the ExecException object. The previous code unconditionally set return_value to -1 for any non-zero exit, making it impossible for callers to distinguish between exit 1 (VSG: violations found and fixed) and exit 2+ (VSG: real error). VSG exits with 1 when --fix successfully reformats the file, so the VSG formatter's check `return_value === 1` was never matching and every file except already-clean ones was silently returning no edits.
The edalize ghdl backend assembles the ghdl -i command by joining file
paths with spaces (" ".join(files)) and writes them verbatim into the
generated Makefile. When any source path contains a space the shell
splits the path into separate tokens and GHDL fails:
ghdl:error: cannot open /Users/.../Sistemas
make: *** [work-obj08.cf] Error 1
After backend.configure() writes the Makefile, quote_paths_with_spaces_in_makefile()
re-reads it and wraps every path that contains a space in double quotes.
The set of paths to fix is derived directly from the EDAM so only known
file names are touched; projects without spaces in their paths are
unaffected. The function is wrapped in a try/except so a failure during
patching never blocks the actual build.
Make and the shell require different escaping for paths that contain spaces: - Recipe lines (tab-indented, executed by the shell) need double-quote wrapping: ghdl -i ... "/path/with spaces/file.vhd" - Make variable definitions and dependency rules use Make's own word- splitting which does not respect shell quoting; spaces must be backslash-escaped: VHDL_SOURCES = /path/with\ spaces/file.vhd The previous fix only applied shell double-quotes everywhere, which fixed the ghdl -i recipe but caused Make itself to fail when expanding $(VHDL_SOURCES) in dependency rules: make: No rule to make target '"path/with', needed by 'tb_pract4' Now process the Makefile line-by-line, applying the correct escaping strategy based on whether each line is a recipe or a Make directive.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR fixes several bugs that prevented TerosHDL from working correctly on macOS (particularly Apple Silicon), and fixes a formatter regression that affected all platforms.
1. Bundle vhdl-ls v0.84.0 for macOS Apple Silicon + system binary fallback
The extension only shipped Linux/Windows binaries. macOS ARM64 users had no bundled server and no fallback path. Two changes:
vhdl_ls-aarch64-apple-darwinbinary (v0.84.0) underserver/vhdl_ls//opt/homebrew/bin/vhdl_ls,/usr/local/bin/vhdl_ls, andvhdl_lsin PATH2. Fix
--librariesargument (directory vs. file path)vhdl_ls 0.84.0expects the--librariesflag to point to the directory that containsvhdl_ls.toml, not to the file itself. The previous code passed the full file path, causing:on every VHDL file open, which prevented the bundled IEEE/STD library from loading and crashed the server.
3. Fix
context.asAbsolutePath()corrupting system binary pathscontext.asAbsolutePath(str)callspath.join(extensionRoot, str). Whenstris already an absolute path like/opt/homebrew/bin/vhdl_ls,path.joinprepends the extension directory, producing a broken path like/ext/path/opt/homebrew/bin/vhdl_ls.Fix: resolve the bundled binary's absolute path once at startup and store it directly, so
getServerOptionsEmbeddednever needs to callasAbsolutePath.4. Fix
worklibrary name in generated TOMLvhdl_ls 0.84.0rejectsworkas an explicit library name in project config TOML (it is a reserved identifier). When files in a project havelogical_name = "work",get_toml()was emittingwork.files = [...], causing:Fix: remap library name
work→teroshdlDefaultinproject_manager.ts.5. Fix duplicate
verible.restartcommand registrationThe extension was registering the
teroshdl.verible.restartcommand twice, which caused an activation error when opening Verilog/SV files. Removed the redundant empty registration.6. Initialise VHDL_LS_CONFIG temp file with valid content
The temp file used for
VHDL_LS_CONFIGwas created with an empty string, causing:on startup. The server would then fall through to auto-discover any
vhdl_ls.tomlin the workspace root, bypassing the project config entirely. Fix: initialise the file with[libraries]\nso vhdl_ls always has a valid minimal config.7. Inherit full process environment for the language server
ServerOptions.options.envin vscode-languageclient replaces the entire child process environment (Node.jschild_process.spawnbehaviour). The previous code only passed{ VHDL_LS_CONFIG: "..." }, strippingPATH,HOME,TMPDIR, and every other OS-level variable from the vhdl_ls process.Fix: spread
process.envfirst so vhdl_ls inherits the full environment, withVHDL_LS_CONFIGoverriding/adding on top.8. Fix duplicate
forceRefreshcausing unnecessary SIGKILL at startupAt startup, the
teroshdl.vhdlls.restartcommand was being scheduled twice in quick succession:Tree_view_managerconstructor firesGLOBAL_REFRESH→refreshToml()→forceRefresh()→ restartinit_tree_views()then also calledforceRefresh()explicitlyEach restart call sends an LSP shutdown to the running vhdl_ls. If the server is busy (indexing files), it does not respond within the timeout and VS Code sends SIGKILL, then spawns a new instance. The double call meant two SIGKILLs back-to-back on startup.
Fix: remove the redundant explicit
forceRefreshcall ininit_tree_views; theGLOBAL_REFRESHevent from theTree_view_managerconstructor already handles it.9. Fix VSG formatter not applying edits to files with violations (all platforms)
This is a regression introduced in commit
63e925f. The formatter was changed to checkexec_result.return_value === 0for success, but the underlyinglocal_process.tsalways stores-1inreturn_valuefor any non-zero exit — it was ignoringerror.codefrom Node'sExecException.VSG exits with:
--fix(the normal formatting outcome)With
return_valuealways being-1for exit code 1, the success checkreturn_value === 0 || return_value === 1never matched exit code 1.successfulwasfalse, the edit was discarded, and the file appeared unchanged.Only files that were already fully compliant (exit 0) ever got an edit applied, but all others did not.
Two-part fix:
local_process.ts: store the actual exit code fromerror.codeinstead of always using-1. This is correct for all tools.vsg.ts: the existingreturn_value === 0 || return_value === 1check now works as intended.Test plan
.vhdfile on macOS Apple Silicon — VHDL LS starts without crashingLoaded Installation configuration file: .../vhdl_libraries/vhdl_ls.tomlLoaded VHDL_LS_CONFIG configuration file: ...Loaded workspace root configuration file: ...(if workspace hasvhdl_ls.toml).v/.svfile — no duplicate command activation error