Repository: abs-tudelft/vhdeps

Issue:
Passing arbitrary arguments to GHDL
Sometimes one might want to pass some extra arguments to GHDL, especially with the GCC backend to pass additional compiler flags. There is currently no command-line syntax for this.

Discussion:

Patch:
diff --git a/vhdeps/targets/ghdl.py b/vhdeps/targets/ghdl.py
index 3e0e2b8..ff426a1 100644
--- a/vhdeps/targets/ghdl.py
+++ b/vhdeps/targets/ghdl.py
@@ -81,7 +81,22 @@ def add_arguments(parser):
         'regardless of whether it passed or not. If there are multiple test '
         'cases, gtkwave is launched for the first failure.')
 
-def _get_ghdl_cmds(vhd_list, ieee='synopsys', no_debug=False, coverage=None, **_):
+    parser.add_argument(
+        '-W', action='append', metavar='#{#},<options>', dest='extra_args',
+        #   It'd be great to use [] here ^^^ but Python devs managed to
+        # sufficiently bork argparse's internals to make that break before
+        # Python 3.8. Since it's completely asenine to require 3.8 for
+        # something like this, {} will have to do.
+        help='Pass comma-separated options to the command specified by #. The '
+        'first # can be \'a\' for the analysis command, \'e\' for the '
+        'elaboration command, and \'r\' for the run command. If a second '
+        'character is specified, <options> are chained to a \'-W#,<options>\' '
+        'option for the command specified by the first letter. For instance, '
+        '\'-Wac,-O3\' passes -O3 to the GCC compiler during the analysis '
+        'phase.')
+
+def _get_ghdl_cmds(vhd_list, ieee='synopsys', no_debug=False,
+                   coverage=None, extra_args=None, **_):
     """Returns a three-tuple of the analyze, elaborate, and run commands for
     GHDL in plumbum form."""
 
@@ -130,6 +145,25 @@ def _get_ghdl_cmds(vhd_list, ieee='synopsys', no_debug=False, coverage=None, **_
         ghdl_analyze = ghdl_analyze['-Wc,-fprofile-arcs', '-Wc,-ftest-coverage', '-Wc,-O3']
         ghdl_elaborate = ghdl_elaborate['-Wl,-lgcov']
 
+    # Add user-specified extra arguments.
+    if extra_args:
+        for extra_arg in extra_args:
+            if ',' not in extra_arg:
+                raise ValueError('invalid value for -W')
+            target, *args = extra_arg.split(',')
+            if len(target) not in (1, 2):
+                raise ValueError('invalid value for -W')
+            if len(target) == 2:
+                args = ['-W%s,%s' % (target[1], ','.join(args))]
+            if target[0] == 'a':
+                ghdl_analyze = ghdl_analyze[args]
+            elif target[0] == 'e':
+                ghdl_elaborate = ghdl_elaborate[args]
+            elif target[0] == 'r':
+                ghdl_run = ghdl_run[args]
+            else:
+                raise ValueError('invalid value for -W')
+
     return ghdl_analyze, ghdl_elaborate, ghdl_run
 
 def _run_test_case(output_file, test_case, vcd_dir, ghdl_elaborate, ghdl_run):


Test patch:
diff --git a/tests/test_ghdl.py b/tests/test_ghdl.py
index a02b2a8..9e16da5 100644
--- a/tests/test_ghdl.py
+++ b/tests/test_ghdl.py
@@ -2,6 +2,7 @@
 
 from unittest import TestCase, skipIf
 from unittest.mock import patch
+import re
 import os
 import tempfile
 from plumbum import local
@@ -254,6 +255,22 @@ class TestGhdlSpecific(TestCase):
             code, _, _ = run_vhdeps('ghdl', '-i', DIR+'/simple/multiple-ok', '-j')
             self.assertEqual(code, 1)
 
+    def test_extra_options(self):
+        """Test the -W option for GHDL"""
+        with local.env(PATH=DIR+'/ghdl/fake-ghdl:' + local.env['PATH']):
+            self.assertNotEqual(run_vhdeps('ghdl', '-i', DIR+'/simple/all-good', '-W'), 0)
+            self.assertNotEqual(run_vhdeps('ghdl', '-i', DIR+'/simple/all-good', '-Wx'), 0)
+            self.assertNotEqual(run_vhdeps('ghdl', '-i', DIR+'/simple/all-good', '-W,x'), 0)
+            self.assertNotEqual(run_vhdeps('ghdl', '-i', DIR+'/simple/all-good', '-Wx,x'), 0)
+            code, out, _ = run_vhdeps(
+                'ghdl', '-i', DIR+'/simple/all-good',
+                '-Wa,a,na,lyze', '-We,e,la,bo,rate', '-Wr,run', '-Wrx,a,b,c')
+        self.assertEqual(code, 0)
+        self.assertTrue(bool(re.search(r'ghdl -a [^\n]* a na lyze', out)))
+        self.assertTrue(bool(re.search(r'ghdl -e [^\n]* e la bo rate', out)))
+        self.assertTrue(bool(re.search(r'ghdl -r [^\n]* run', out)))
+        self.assertTrue(bool(re.search(r'ghdl -r [^\n]* -Wx,a,b,c', out)))
+
 
 @skipIf(
     not coverage_supported(),