labview-fpga-hdl-tools 2.0.0.dev2__tar.gz → 2.0.0.dev4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/PKG-INFO +5 -5
  2. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/README.md +2 -2
  3. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/__main__.py +3 -2
  4. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/check_syntax.py +21 -27
  5. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/common.py +69 -25
  6. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/compile_modelsim_lib.py +24 -27
  7. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/compile_project.py +25 -31
  8. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/create_lvbitx.py +6 -5
  9. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/create_modelsim_project.py +8 -9
  10. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/create_vivado_project.py +39 -40
  11. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/gen_labview_target_plugin.py +29 -40
  12. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/generate_vhdl.py +20 -17
  13. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/get_window_netlist.py +19 -16
  14. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/install_dependencies.py +14 -4
  15. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/install_labview_target_plugin.py +37 -23
  16. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/launch_modelsim.py +14 -12
  17. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/launch_vivado.py +18 -12
  18. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/migrate_clip.py +28 -26
  19. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/process_constraints.py +3 -3
  20. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/sim_modelsim.py +8 -8
  21. labview_fpga_hdl_tools-2.0.0.dev4/pyproject.toml +75 -0
  22. labview_fpga_hdl_tools-2.0.0.dev2/pyproject.toml +0 -50
  23. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/LICENSE +0 -0
  24. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/.gitignore +0 -0
  25. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/__init__.py +0 -0
  26. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/command_config.py +0 -0
  27. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/command_hooks.py +0 -0
  28. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/nihdlsettings_default.py +0 -0
  29. {labview_fpga_hdl_tools-2.0.0.dev2 → labview_fpga_hdl_tools-2.0.0.dev4}/labview_fpga_hdl_tools/reporting.py +0 -0
@@ -1,19 +1,19 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: labview-fpga-hdl-tools
3
- Version: 2.0.0.dev2
3
+ Version: 2.0.0.dev4
4
4
  Summary: LabVIEW FPGA HDL Tools
5
5
  License: MIT
6
6
  Author: Salvador Santolucito
7
7
  Author-email: salvador.santolucito@gmail.com
8
- Requires-Python: >=3.9,<4.0
8
+ Requires-Python: >=3.10,<3.15
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.9
12
11
  Classifier: Programming Language :: Python :: 3.10
13
12
  Classifier: Programming Language :: Python :: 3.11
14
13
  Classifier: Programming Language :: Python :: 3.12
15
14
  Classifier: Programming Language :: Python :: 3.13
16
15
  Requires-Dist: click (>=8.1.8,<8.2)
16
+ Requires-Dist: defusedxml (>=0.7.1)
17
17
  Requires-Dist: mako (>=1.3.10)
18
18
  Requires-Dist: packaging (>=21.0)
19
19
  Requires-Dist: tomli (>=1.1.0) ; python_version < "3.11"
@@ -66,8 +66,8 @@ You need the external tools your flow uses:
66
66
  - **ModelSim** — only for simulation (`gen-modelsim`, `sim-modelsim`,
67
67
  `launch-modelsim`, `compile-modelsim-lib`). `compile-modelsim-lib` also needs
68
68
  Vivado to compile the Xilinx simulation libraries.
69
- - **Git** and **Python** — the tools support Python 3.9+; CI tests on 3.9 and
70
- 3.11 (3.11 is also used for static analysis).
69
+ - **Git** and **Python** — the tools support Python 3.10–3.14; CI tests on
70
+ 3.10 through 3.14 (3.11 is also used for static analysis).
71
71
 
72
72
  ### Installing the tools
73
73
 
@@ -45,8 +45,8 @@ You need the external tools your flow uses:
45
45
  - **ModelSim** — only for simulation (`gen-modelsim`, `sim-modelsim`,
46
46
  `launch-modelsim`, `compile-modelsim-lib`). `compile-modelsim-lib` also needs
47
47
  Vivado to compile the Xilinx simulation libraries.
48
- - **Git** and **Python** — the tools support Python 3.9+; CI tests on 3.9 and
49
- 3.11 (3.11 is also used for static analysis).
48
+ - **Git** and **Python** — the tools support Python 3.10–3.14; CI tests on
49
+ 3.10 through 3.14 (3.11 is also used for static analysis).
50
50
 
51
51
  ### Installing the tools
52
52
 
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """LVFPGAHDLTools - Command-line interface for LabVIEW FPGA HDL Tools."""
3
+
3
4
  # Copyright (c) 2025 National Instruments Corporation
4
5
  #
5
6
  # SPDX-License-Identifier: MIT
@@ -11,7 +12,7 @@ import traceback
11
12
  import click
12
13
 
13
14
  # Import main functions from all the tool modules
14
- from . import (
15
+ from labview_fpga_hdl_tools import (
15
16
  __version__,
16
17
  check_syntax,
17
18
  command_hooks,
@@ -32,7 +33,7 @@ from . import (
32
33
  process_constraints,
33
34
  sim_modelsim,
34
35
  )
35
- from .reporting import reporter
36
+ from labview_fpga_hdl_tools.reporting import reporter
36
37
 
37
38
 
38
39
  class SectionGroup(click.Group):
@@ -8,8 +8,9 @@
8
8
  import os
9
9
  import subprocess
10
10
 
11
- from . import common
12
- from .reporting import reporter
11
+ from labview_fpga_hdl_tools import common
12
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
13
+ from labview_fpga_hdl_tools.reporting import reporter
13
14
 
14
15
 
15
16
  def _generate_check_syntax_tcl(config, output_path):
@@ -83,12 +84,9 @@ def _validate_ini(config):
83
84
  if invalid_path:
84
85
  invalid_paths.append(invalid_path)
85
86
 
86
- error_msg = common.get_missing_settings_error(missing_settings)
87
- error_msg += common.get_invalid_paths_error(invalid_paths)
88
-
89
- if missing_settings or invalid_paths:
90
- error_msg += "\nPlease update your configuration file and try again."
91
- raise ValueError(error_msg)
87
+ error = common.build_settings_error(missing_settings, invalid_paths)
88
+ if error:
89
+ raise ValueError(error)
92
90
 
93
91
 
94
92
  def _get_check_syntax_status_from_log(log_contents):
@@ -109,16 +107,7 @@ def _get_check_syntax_status_from_log(log_contents):
109
107
 
110
108
  def _run_check_syntax(config, generated_tcl_path):
111
109
  """Run the generated check-syntax TCL script in Vivado batch mode."""
112
- vivado_executable = common.get_vivado_executable(config.vivado_tools_folder)
113
- if not vivado_executable:
114
- raise ValueError("VivadoToolsFolder setting is missing from configuration")
115
-
116
- vivado_abs = os.path.abspath(vivado_executable)
117
- if not os.path.exists(vivado_abs):
118
- raise FileNotFoundError(
119
- f"Vivado executable not found at: {vivado_abs}\n"
120
- f"Please check your --vivado argument or VivadoToolsFolder setting in nihdlsettings.py"
121
- )
110
+ vivado_abs = common.resolve_vivado_executable_abs(config)
122
111
 
123
112
  vivado_project_dir = os.path.join(os.getcwd(), config.vivado_project_folder)
124
113
  log_path = os.path.join(vivado_project_dir, "check_syntax.log")
@@ -128,19 +117,24 @@ def _run_check_syntax(config, generated_tcl_path):
128
117
  if os.path.exists(path):
129
118
  os.remove(path)
130
119
 
131
- command = (
132
- f'"{vivado_abs}" -mode batch '
133
- f'-source "{generated_tcl_path}" '
134
- f'-log "{log_path}" '
135
- f'-journal "{journal_path}"'
136
- )
120
+ command = [
121
+ vivado_abs,
122
+ "-mode",
123
+ "batch",
124
+ "-source",
125
+ generated_tcl_path,
126
+ "-log",
127
+ log_path,
128
+ "-journal",
129
+ journal_path,
130
+ ]
137
131
 
138
132
  reporter.detail(f"Generated TCL script: {generated_tcl_path}")
139
133
  reporter.detail(f"Vivado executable: {vivado_abs}")
140
134
  reporter.detail(f"Working directory: {vivado_project_dir}")
141
- reporter.detail(f"Running command: {command}")
135
+ reporter.detail(f"Running command: {' '.join(command)}")
142
136
 
143
- result = subprocess.run(command, cwd=vivado_project_dir, shell=True, check=False)
137
+ result = subprocess.run(command, cwd=vivado_project_dir, check=False)
144
138
 
145
139
  if not os.path.exists(log_path):
146
140
  raise RuntimeError("Vivado check-syntax log file was not created.")
@@ -185,7 +179,7 @@ def check_syntax(config=None):
185
179
  int: 0 for success, 1 for error
186
180
  """
187
181
  if config is None:
188
- config = common.CommandConfiguration()
182
+ config = CommandConfiguration()
189
183
 
190
184
  try:
191
185
  _validate_ini(config)
@@ -14,8 +14,7 @@ from typing import Optional
14
14
 
15
15
  from mako.template import Template
16
16
 
17
- from .command_config import CommandConfiguration, resolve_path # noqa: F401
18
- from .reporting import reporter
17
+ from labview_fpga_hdl_tools.reporting import reporter
19
18
 
20
19
 
21
20
  def handle_long_path(path):
@@ -237,7 +236,8 @@ def _parse_vhdl_entity(vhdl_path):
237
236
 
238
237
  except Exception as e:
239
238
  reporter.error(f"Error parsing VHDL file: {str(e)}")
240
- traceback.print_exc()
239
+ if reporter.verbose:
240
+ traceback.print_exc()
241
241
  return None, []
242
242
 
243
243
 
@@ -406,10 +406,16 @@ def apply_hdl_excludes(file_list, excluded_abspaths):
406
406
 
407
407
 
408
408
  def run_command(cmd, cwd=None, capture_output=True, check=False):
409
- """Run a shell command and return its output.
409
+ """Run a command and return its output.
410
+
411
+ The command is given as a pre-tokenized argument list (for example
412
+ ``[vivado_abs, "-mode", "batch", ...]``) and is executed directly without a
413
+ shell. Passing an argument list means arguments containing spaces or
414
+ shell-significant characters are passed through verbatim (no quoting bugs)
415
+ and there is no shell-injection surface.
410
416
 
411
417
  Args:
412
- cmd: Command string to run through the shell.
418
+ cmd: The command to run, as a list of arguments.
413
419
  cwd: Working directory to run the command in, if any.
414
420
  capture_output: When True, capture stdout and return it as a string.
415
421
  When False, let the command's output stream to the console and
@@ -421,7 +427,7 @@ def run_command(cmd, cwd=None, capture_output=True, check=False):
421
427
  The captured stdout (stripped) when ``capture_output`` is True, otherwise
422
428
  an empty string.
423
429
  """
424
- reporter.detail(f"Running command: {cmd}")
430
+ reporter.detail(f"Running command: {' '.join(str(arg) for arg in cmd)}")
425
431
 
426
432
  kwargs = {}
427
433
  if cwd:
@@ -429,14 +435,12 @@ def run_command(cmd, cwd=None, capture_output=True, check=False):
429
435
 
430
436
  if capture_output:
431
437
  # Capture and return output
432
- result = subprocess.run(
433
- cmd, shell=True, text=True, capture_output=True, check=check, **kwargs
434
- )
438
+ result = subprocess.run(cmd, text=True, capture_output=True, check=check, **kwargs)
435
439
  # Check if stdout is None before calling strip()
436
440
  return result.stdout.strip() if result.stdout is not None else ""
437
441
  else:
438
442
  # Don't capture output (let it go to console)
439
- subprocess.run(cmd, shell=True, check=check, **kwargs)
443
+ subprocess.run(cmd, check=check, **kwargs)
440
444
  return "" # Return empty string instead of None
441
445
 
442
446
 
@@ -495,6 +499,48 @@ def get_invalid_paths_error(invalid_paths):
495
499
  return error_msg
496
500
 
497
501
 
502
+ def build_settings_error(missing_settings, invalid_paths):
503
+ """Return the standard missing-settings / invalid-paths message, or "" if none.
504
+
505
+ Combines the missing-settings and invalid-path messages with the shared hint
506
+ used by every ``_validate_ini``. Returns an empty string when there is
507
+ nothing to report, so callers can do ``if error: raise ValueError(error)``
508
+ (or report-and-return) without repeating the concatenation and hint text.
509
+ """
510
+ if not (missing_settings or invalid_paths):
511
+ return ""
512
+ return (
513
+ get_missing_settings_error(missing_settings)
514
+ + get_invalid_paths_error(invalid_paths)
515
+ + "\nPlease update your configuration file and try again."
516
+ )
517
+
518
+
519
+ def resolve_vivado_executable_abs(config):
520
+ """Resolve the Vivado executable to an absolute path.
521
+
522
+ Args:
523
+ config: CommandConfiguration providing ``vivado_tools_folder``.
524
+
525
+ Returns:
526
+ str: Absolute path to the Vivado executable.
527
+
528
+ Raises:
529
+ ValueError: if the executable cannot be resolved from the configuration.
530
+ FileNotFoundError: if the resolved executable does not exist on disk.
531
+ """
532
+ vivado_executable = get_vivado_executable(config.vivado_tools_folder)
533
+ if not vivado_executable:
534
+ raise ValueError("VivadoToolsFolder setting is missing from configuration")
535
+ vivado_abs = os.path.abspath(vivado_executable)
536
+ if not os.path.exists(vivado_abs):
537
+ raise FileNotFoundError(
538
+ f"Vivado executable not found at: {vivado_abs}\n"
539
+ "Please check your VivadoToolsFolder setting in nihdlsettings.py"
540
+ )
541
+ return vivado_abs
542
+
543
+
498
544
  def collect_missing_settings(config, required):
499
545
  """Return the labels of required settings that are unset (falsy) on config.
500
546
 
@@ -517,23 +563,21 @@ def collect_missing_settings(config, required):
517
563
  def raise_for_missing_settings(missing_settings, invalid_paths):
518
564
  """Raise ``ValueError`` for missing settings or invalid paths, if any.
519
565
 
520
- Missing settings take precedence over invalid paths. Produces the standard
521
- launch-style messages shared by the launch commands. Does nothing when both
522
- lists are empty.
566
+ Missing settings take precedence over invalid paths (only one message is
567
+ raised). Uses the shared ``get_missing_settings_error`` /
568
+ ``get_invalid_paths_error`` builders so the wording matches the
569
+ ``_validate_ini`` validators. Does nothing when both lists are empty.
523
570
  """
524
571
  if missing_settings:
525
- error_msg = "Missing required configuration settings:\n"
526
- for setting in missing_settings:
527
- error_msg += f" - {setting}\n"
528
- error_msg += "\nCheck your nihdlsettings.py file and try again."
529
- raise ValueError(error_msg)
530
-
572
+ raise ValueError(
573
+ get_missing_settings_error(missing_settings)
574
+ + "\nPlease update your configuration file and try again."
575
+ )
531
576
  if invalid_paths:
532
- error_msg = "The following settings have invalid paths:\n"
533
- for path in invalid_paths:
534
- error_msg += f" - {path}\n"
535
- error_msg += "\nCheck your configuration and try again."
536
- raise ValueError(error_msg)
577
+ raise ValueError(
578
+ get_invalid_paths_error(invalid_paths)
579
+ + "\nPlease update your configuration file and try again."
580
+ )
537
581
 
538
582
 
539
583
  def generate_guid():
@@ -554,7 +598,7 @@ def render_mako_template(template_path, output_path, **kwargs):
554
598
  output_path (str): Path where the rendered output will be written
555
599
  **kwargs: Template variables to substitute
556
600
  """
557
- template = Template(filename=template_path, input_encoding="utf-8")
601
+ template = Template(filename=template_path, input_encoding="utf-8") # nosec B702
558
602
  rendered = str(template.render(**kwargs))
559
603
 
560
604
  # Mako preserves \r\n from template files on Windows. Normalize to \n
@@ -21,9 +21,10 @@ import os
21
21
  import subprocess
22
22
  import sys
23
23
 
24
- from . import common
25
- from .create_modelsim_project import _find_modelsim_executable
26
- from .reporting import reporter
24
+ from labview_fpga_hdl_tools import common
25
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
26
+ from labview_fpga_hdl_tools.create_modelsim_project import _find_modelsim_executable
27
+ from labview_fpga_hdl_tools.reporting import reporter
27
28
 
28
29
  # Libraries built when nihdlsettings.py does not specify an explicit scope.
29
30
  _DEFAULT_XILINX_SIM_LIBRARIES = ["unisim", "secureip"]
@@ -69,11 +70,9 @@ def _validate_ini(config):
69
70
  f"{config.modelsim_tools_folder}"
70
71
  )
71
72
 
72
- error_msg = common.get_missing_settings_error(missing_settings)
73
- error_msg += common.get_invalid_paths_error(invalid_paths)
74
- if missing_settings or invalid_paths:
75
- error_msg += "\nPlease update your configuration file and try again."
76
- raise ValueError(error_msg)
73
+ error = common.build_settings_error(missing_settings, invalid_paths)
74
+ if error:
75
+ raise ValueError(error)
77
76
 
78
77
 
79
78
  def _libraries_already_built(out_folder, libraries):
@@ -134,16 +133,7 @@ def _is_progress_line(line):
134
133
 
135
134
  def _run_compile_simlib(config, tcl_path, out_folder, libraries):
136
135
  """Run the generated compile_simlib TCL script in Vivado batch mode."""
137
- vivado_executable = common.get_vivado_executable(config.vivado_tools_folder)
138
- if not vivado_executable:
139
- raise ValueError("VivadoToolsFolder setting is missing from configuration")
140
-
141
- vivado_abs = os.path.abspath(vivado_executable)
142
- if not os.path.exists(vivado_abs):
143
- raise FileNotFoundError(
144
- f"Vivado executable not found at: {vivado_abs}\n"
145
- f"Please check your VivadoToolsFolder setting in nihdlsettings.py"
146
- )
136
+ vivado_abs = common.resolve_vivado_executable_abs(config)
147
137
 
148
138
  os.makedirs(out_folder, exist_ok=True)
149
139
  # Use a distinct name for Vivado's session log/journal. compile_simlib
@@ -165,12 +155,19 @@ def _run_compile_simlib(config, tcl_path, out_folder, libraries):
165
155
  "try again."
166
156
  ) from exc
167
157
 
168
- command = (
169
- f'"{vivado_abs}" -mode batch '
170
- f'-source "{tcl_path}" '
171
- f'-log "{log_path}" '
172
- f'-journal "{journal_path}"'
173
- )
158
+ # Build the argument list explicitly and run without a shell so paths that
159
+ # contain spaces or shell-significant characters are passed through verbatim.
160
+ command = [
161
+ vivado_abs,
162
+ "-mode",
163
+ "batch",
164
+ "-source",
165
+ tcl_path,
166
+ "-log",
167
+ log_path,
168
+ "-journal",
169
+ journal_path,
170
+ ]
174
171
 
175
172
  # Run with the MODELSIM environment variable unset so compile_simlib does
176
173
  # not read or modify a global modelsim.ini; it writes a fresh one into the
@@ -181,7 +178,7 @@ def _run_compile_simlib(config, tcl_path, out_folder, libraries):
181
178
  reporter.detail(f"Vivado executable: {vivado_abs}")
182
179
  reporter.detail(f"Output directory: {out_folder}")
183
180
  reporter.detail(f"Libraries: {', '.join(libraries)}")
184
- reporter.detail(f"Running command: {command}")
181
+ reporter.detail(f"Running command: {' '.join(command)}")
185
182
  reporter.success("")
186
183
  reporter.success("=" * 78)
187
184
  reporter.success("Compiling the Xilinx ModelSim simulation libraries with Vivado.")
@@ -201,7 +198,7 @@ def _run_compile_simlib(config, tcl_path, out_folder, libraries):
201
198
  process = subprocess.Popen(
202
199
  command,
203
200
  cwd=out_folder,
204
- shell=True,
201
+ shell=False,
205
202
  env=env,
206
203
  stdout=subprocess.PIPE,
207
204
  stderr=subprocess.STDOUT,
@@ -254,7 +251,7 @@ def compile_modelsim_lib(config=None, force=False):
254
251
  int: 0 for success, 1 for error.
255
252
  """
256
253
  if config is None:
257
- config = common.CommandConfiguration()
254
+ config = CommandConfiguration()
258
255
 
259
256
  out_folder = config.xilinx_sim_lib_folder
260
257
  if not out_folder:
@@ -8,8 +8,9 @@
8
8
  import os
9
9
  import subprocess
10
10
 
11
- from . import common
12
- from .reporting import reporter
11
+ from labview_fpga_hdl_tools import common
12
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
13
+ from labview_fpga_hdl_tools.reporting import reporter
13
14
 
14
15
 
15
16
  def _generate_compile_project_tcl(config, output_path):
@@ -25,12 +26,12 @@ def _generate_compile_project_tcl(config, output_path):
25
26
 
26
27
  def _validate_ini(config):
27
28
  """Validate required settings and paths for compile-project."""
28
- missing_settings = []
29
+ missing_settings = common.collect_missing_settings(
30
+ config,
31
+ [("vivado_project_folder", "VivadoProjectSettings.VivadoProjectFolder")],
32
+ )
29
33
  invalid_paths = []
30
34
 
31
- if not config.vivado_project_folder:
32
- missing_settings.append("VivadoProjectSettings.VivadoProjectFolder")
33
-
34
35
  if not config.vivado_tcl_scripts_folder:
35
36
  missing_settings.append("VivadoProjectSettings.VivadoTclScriptsFolder")
36
37
  else:
@@ -72,12 +73,9 @@ def _validate_ini(config):
72
73
  if invalid_path:
73
74
  invalid_paths.append(invalid_path)
74
75
 
75
- error_msg = common.get_missing_settings_error(missing_settings)
76
- error_msg += common.get_invalid_paths_error(invalid_paths)
77
-
78
- if missing_settings or invalid_paths:
79
- error_msg += "\nPlease update your configuration file and try again."
80
- raise ValueError(error_msg)
76
+ error = common.build_settings_error(missing_settings, invalid_paths)
77
+ if error:
78
+ raise ValueError(error)
81
79
 
82
80
 
83
81
  def _get_compile_status_from_log(log_contents):
@@ -103,16 +101,7 @@ def _get_compile_status_from_log(log_contents):
103
101
 
104
102
  def _run_compile_project(config, generated_tcl_path):
105
103
  """Run the generated compile-project TCL script in Vivado batch mode."""
106
- vivado_executable = common.get_vivado_executable(config.vivado_tools_folder)
107
- if not vivado_executable:
108
- raise ValueError("VivadoToolsFolder setting is missing from configuration")
109
-
110
- vivado_abs = os.path.abspath(vivado_executable)
111
- if not os.path.exists(vivado_abs):
112
- raise FileNotFoundError(
113
- f"Vivado executable not found at: {vivado_abs}\n"
114
- f"Please check your --vivado argument or VivadoToolsFolder setting in nihdlsettings.py"
115
- )
104
+ vivado_abs = common.resolve_vivado_executable_abs(config)
116
105
 
117
106
  current_dir = os.getcwd()
118
107
  vivado_project_dir = os.path.join(current_dir, config.vivado_project_folder)
@@ -123,18 +112,23 @@ def _run_compile_project(config, generated_tcl_path):
123
112
  if os.path.exists(path):
124
113
  os.remove(path)
125
114
 
126
- command = (
127
- f'"{vivado_abs}" -mode batch '
128
- f'-source "{generated_tcl_path}" '
129
- f'-log "{log_path}" '
130
- f'-journal "{journal_path}"'
131
- )
115
+ command = [
116
+ vivado_abs,
117
+ "-mode",
118
+ "batch",
119
+ "-source",
120
+ generated_tcl_path,
121
+ "-log",
122
+ log_path,
123
+ "-journal",
124
+ journal_path,
125
+ ]
132
126
 
133
127
  reporter.detail(f"Vivado executable: {vivado_abs}")
134
128
  reporter.detail(f"Working directory: {vivado_project_dir}")
135
- reporter.detail(f"Running command: {command}")
129
+ reporter.detail(f"Running command: {' '.join(command)}")
136
130
 
137
- result = subprocess.run(command, cwd=vivado_project_dir, shell=True, check=False)
131
+ result = subprocess.run(command, cwd=vivado_project_dir, check=False)
138
132
 
139
133
  if not os.path.exists(log_path):
140
134
  raise RuntimeError("Vivado compile-project log file was not created.")
@@ -194,7 +188,7 @@ def compile_project(config=None):
194
188
  int: 0 for success, 1 for error
195
189
  """
196
190
  if config is None:
197
- config = common.CommandConfiguration()
191
+ config = CommandConfiguration()
198
192
 
199
193
  try:
200
194
  _validate_ini(config)
@@ -7,8 +7,8 @@
7
7
  import os # For file and directory operations
8
8
  import subprocess # For executing external programs
9
9
 
10
- from . import common # For shared utilities across tools
11
- from .reporting import reporter
10
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
11
+ from labview_fpga_hdl_tools.reporting import reporter
12
12
 
13
13
  # createBitfile.exe lives at this path relative to a LabVIEW install root.
14
14
  _CREATEBITFILE_RELPATH = os.path.join("vi.lib", "rvi", "CDR", "createBitfile.exe")
@@ -114,7 +114,7 @@ def _create_lv_bitfile(config=None):
114
114
 
115
115
  # Load configuration
116
116
  if config is None:
117
- config = common.CommandConfiguration()
117
+ config = CommandConfiguration()
118
118
 
119
119
  # Determine path to CodeGenerationResults.lvtxt from TheWindow folder
120
120
  if not config.lv_window_netlist_folder:
@@ -211,7 +211,8 @@ def create_lv_bitx(config=None):
211
211
  return result # Return the result code
212
212
  except Exception as e:
213
213
  reporter.error(f"Unhandled exception: {str(e)}")
214
- import traceback
214
+ if reporter.verbose:
215
+ import traceback
215
216
 
216
- traceback.print_exc()
217
+ traceback.print_exc()
217
218
  return 1 # Return error code on exception
@@ -11,8 +11,9 @@ import shutil
11
11
  import stat
12
12
  import subprocess
13
13
 
14
- from . import common, generate_vhdl
15
- from .reporting import reporter
14
+ from labview_fpga_hdl_tools import common, generate_vhdl
15
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
16
+ from labview_fpga_hdl_tools.reporting import reporter
16
17
 
17
18
 
18
19
  def _validate_ini(config):
@@ -61,11 +62,9 @@ def _validate_ini(config):
61
62
  f"{config.modelsim_tools_folder}"
62
63
  )
63
64
 
64
- error_msg = common.get_missing_settings_error(missing_settings)
65
- error_msg += common.get_invalid_paths_error(invalid_paths)
66
- if missing_settings or invalid_paths:
67
- error_msg += "\nPlease update your configuration file and try again."
68
- raise ValueError(error_msg)
65
+ error = common.build_settings_error(missing_settings, invalid_paths)
66
+ if error:
67
+ raise ValueError(error)
69
68
 
70
69
 
71
70
  def _get_vsim_executable(modelsim_path):
@@ -428,7 +427,7 @@ def create_modelsim_project(overwrite=False, config=None):
428
427
  - .do files for GUI and batch simulation
429
428
  """
430
429
  if config is None:
431
- config = common.CommandConfiguration()
430
+ config = CommandConfiguration()
432
431
 
433
432
  try:
434
433
  _validate_ini(config)
@@ -501,7 +500,7 @@ def create_modelsim_project(overwrite=False, config=None):
501
500
  # This is idempotent: once compiled it is a cheap no-op (and does not
502
501
  # even require Vivado). Imported locally to avoid a circular import,
503
502
  # since compile_modelsim_lib imports helpers from this module.
504
- from . import compile_modelsim_lib
503
+ from labview_fpga_hdl_tools import compile_modelsim_lib
505
504
 
506
505
  if compile_modelsim_lib.compile_modelsim_lib(config=config) != 0:
507
506
  return 1