[v7,3/4] c++modules: report imported CMI files as dependencies
Checks
Commit Message
They affect the build, so report them via `-MF` mechanisms.
gcc/cp/
* module.cc (do_import): Report imported CMI files as
dependencies.
gcc/testsuite/
* g++.dg/modules/depreport-1_a.C: New test.
* g++.dg/modules/depreport-1_b.C: New test.
* g++.dg/modules/test-depfile.py: New tool for validating depfile
information.
* lib/modules.exp: Support for validating depfile contents.
Signed-off-by: Ben Boeckel <ben.boeckel@kitware.com>
---
gcc/cp/module.cc | 3 +
gcc/testsuite/g++.dg/modules/depreport-1_a.C | 10 +
gcc/testsuite/g++.dg/modules/depreport-1_b.C | 12 ++
gcc/testsuite/g++.dg/modules/test-depfile.py | 187 +++++++++++++++++++
gcc/testsuite/lib/modules.exp | 29 +++
5 files changed, 241 insertions(+)
create mode 100644 gcc/testsuite/g++.dg/modules/depreport-1_a.C
create mode 100644 gcc/testsuite/g++.dg/modules/depreport-1_b.C
create mode 100644 gcc/testsuite/g++.dg/modules/test-depfile.py
@@ -18968,6 +18968,9 @@ module_state::do_import (cpp_reader *reader, bool outermost)
dump () && dump ("CMI is %s", file);
if (note_module_cmi_yes || inform_cmi_p)
inform (loc, "reading CMI %qs", file);
+ /* Add the CMI file to the dependency tracking. */
+ if (cpp_get_deps (reader))
+ deps_add_dep (cpp_get_deps (reader), file);
fd = open (file, O_RDONLY | O_CLOEXEC | O_BINARY);
e = errno;
}
new file mode 100644
@@ -0,0 +1,10 @@
+// { dg-additional-options -fmodules-ts }
+
+export module Foo;
+// { dg-module-cmi Foo }
+
+export class Base
+{
+public:
+ int m;
+};
new file mode 100644
@@ -0,0 +1,12 @@
+// { dg-additional-options -fmodules-ts }
+// { dg-additional-options -MD }
+// { dg-additional-options "-MF depreport-1.d" }
+
+import Foo;
+
+void foo ()
+{
+ Base b;
+}
+
+// { dg-final { run-check-module-dep-expect-input "depreport-1.d" "gcm.cache/Foo.gcm" } }
new file mode 100644
@@ -0,0 +1,187 @@
+import json
+
+
+# Parameters.
+ALL_ERRORS = False
+
+
+def _report_error(msg):
+ '''Report an error.'''
+ full_msg = 'ERROR: ' + msg
+ if ALL_ERRORS:
+ print(full_msg)
+ else:
+ raise RuntimeError(full_msg)
+
+
+class Token(object):
+ pass
+
+
+class Output(Token):
+ def __init__(self, path):
+ self.path = path
+
+
+class Input(Token):
+ def __init__(self, path):
+ self.path = path
+
+
+class Colon(Token):
+ pass
+
+
+class Append(Token):
+ pass
+
+
+class Variable(Token):
+ def __init__(self, name):
+ self.name = name
+
+
+class Word(Token):
+ def __init__(self, name):
+ self.name = name
+
+
+def validate_depfile(depfile, expect_input=None):
+ '''Validate a depfile contains some information
+
+ Returns `False` if the information is not found.
+ '''
+ with open(depfile, 'r') as fin:
+ depfile_content = fin.read()
+
+ real_lines = []
+ join_line = False
+ for line in depfile_content.split('\n'):
+ # Join the line if needed.
+ if join_line:
+ line = real_lines.pop() + line
+
+ # Detect line continuations.
+ join_line = line.endswith('\\')
+ # Strip line continuation characters.
+ if join_line:
+ line = line[:-1]
+
+ # Add to the real line set.
+ real_lines.append(line)
+
+ # Perform tokenization.
+ tokenized_lines = []
+ for line in real_lines:
+ tokenized = []
+ join_word = False
+ for word in line.split(' '):
+ if join_word:
+ word = tokenized.pop() + ' ' + word
+
+ # Detect word joins.
+ join_word = word.endswith('\\')
+ # Strip escape character.
+ if join_word:
+ word = word[:-1]
+
+ # Detect `:` at the end of a word.
+ if word.endswith(':'):
+ tokenized.append(word[:-1])
+ word = word[-1]
+
+ # Add word to the tokenized set.
+ tokenized.append(word)
+
+ tokenized_lines.append(tokenized)
+
+ # Parse.
+ ast = []
+ for line in tokenized_lines:
+ kind = None
+ for token in line:
+ if token == ':':
+ kind = 'dependency'
+ elif token == '+=':
+ kind = 'append'
+ if line == ['']:
+ kind = 'empty'
+
+ if kind is None:
+ _report_error('unknown line kind: %s' % line)
+
+ line_parse = []
+ if kind == 'dependency':
+ after_colon = False
+ for token in line:
+ if token == ':':
+ after_colon = True
+ elif after_colon:
+ line_parse.append(Input(token))
+ else:
+ line_parse.append(Output(token))
+ elif kind == 'append':
+ after_op = False
+ for token in line:
+ if token == '+=':
+ after_op = True
+ elif after_op:
+ line_parse.append(Word(token))
+ else:
+ line_parse.append(Variable(token))
+
+ ast.append(line_parse)
+
+ for node in ast:
+ for token in node:
+ if expect_input is not None:
+ # If the input is found, clear the expectation.
+ if isinstance(token, Input) and token.path == expect_input:
+ expect_input = None
+
+ result = True
+ if expect_input:
+ _report_error('missing input: %s' % expect_input)
+ result = False
+
+ return result
+
+
+if __name__ == '__main__':
+ import sys
+
+ depfile = None
+ have_expect = False
+ expect_input = None
+
+ # Parse arguments.
+ args = sys.argv[1:]
+ while args:
+ # Take an argument.
+ arg = args.pop(0)
+
+ # Flag to change how errors are reported.
+ if arg == '-A' or arg == '--all':
+ ALL_ERRORS = True
+ # Required arguments.
+ elif arg == '-d' or arg == '--depfile':
+ depfile = args.pop(0)
+ elif arg == '-i' or arg == '--expect-input':
+ expect_input = args.pop(0)
+ have_expect = True
+
+ # Validate that we have the required arguments.
+ if depfile is None:
+ raise RuntimeError('missing "depfile" file')
+ if have_expect is None:
+ raise RuntimeError('missing an "expect" argument')
+
+ # Do the actual work.
+ try:
+ is_ok = validate_depfile(depfile, expect_input=expect_input)
+ except BaseException as e:
+ _report_error('exception: %s' % e)
+
+ # Fail if errors are found.
+ if not is_ok:
+ sys.exit(1)
@@ -69,3 +69,32 @@ proc run-check-p1689-valid { depfile template } {
clean-p1689 $testcase
}
+
+proc run-check-module-dep { depfile flag expected } {
+ global srcdir subdir
+ # Extract the test file name from the arguments.
+ set testcase [file rootname [file tail $depfile]]
+
+ verbose "Verifying dependencies for $testcase in $srcdir/$subdir" 2
+ set testcase [remote_download host $testcase]
+
+ set pytest_script "test-depfile.py"
+ if { ![check_effective_target_recent_python3] } {
+ unsupported "$pytest_script python3 is missing"
+ return
+ }
+
+ verbose "running script test-depfile.py" 1
+ spawn -noecho python3 $srcdir/$subdir/$pytest_script --all --depfile $depfile $flag $expected
+
+ expect {
+ -re "ERROR: (\[^\r\n\]*)" {
+ fail $expect_out(0,string)
+ exp_continue
+ }
+ }
+}
+
+proc run-check-module-dep-expect-input { depfile expected } {
+ run-check-module-dep $depfile "--expect-input" $expected
+}