public inbox for fortran@gcc.gnu.org
 help / color / mirror / Atom feed
From: Ben Boeckel <ben.boeckel@kitware.com>
To: gcc-patches@gcc.gnu.org
Cc: Ben Boeckel <ben.boeckel@kitware.com>,
	jason@redhat.com, nathan@acm.org, fortran@gcc.gnu.org,
	gcc@gcc.gnu.org, brad.king@kitware.com
Subject: [PATCH v7 3/4] c++modules: report imported CMI files as dependencies
Date: Sun,  2 Jul 2023 12:32:10 -0400	[thread overview]
Message-ID: <20230702163211.3396210-4-ben.boeckel@kitware.com> (raw)
In-Reply-To: <20230702163211.3396210-1-ben.boeckel@kitware.com>

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

diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 9df60d695b1..f3acc4e02fe 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -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;
     }
diff --git a/gcc/testsuite/g++.dg/modules/depreport-1_a.C b/gcc/testsuite/g++.dg/modules/depreport-1_a.C
new file mode 100644
index 00000000000..241701728a2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/depreport-1_a.C
@@ -0,0 +1,10 @@
+// { dg-additional-options -fmodules-ts }
+
+export module Foo;
+// { dg-module-cmi Foo }
+
+export class Base
+{
+public:
+  int m;
+};
diff --git a/gcc/testsuite/g++.dg/modules/depreport-1_b.C b/gcc/testsuite/g++.dg/modules/depreport-1_b.C
new file mode 100644
index 00000000000..b6e317c6703
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/depreport-1_b.C
@@ -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" } }
diff --git a/gcc/testsuite/g++.dg/modules/test-depfile.py b/gcc/testsuite/g++.dg/modules/test-depfile.py
new file mode 100644
index 00000000000..ea4edb61434
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/test-depfile.py
@@ -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)
diff --git a/gcc/testsuite/lib/modules.exp b/gcc/testsuite/lib/modules.exp
index d466a73cbe1..e24ee7618d9 100644
--- a/gcc/testsuite/lib/modules.exp
+++ b/gcc/testsuite/lib/modules.exp
@@ -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
+}
-- 
2.40.1


  parent reply	other threads:[~2023-07-02 16:32 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-07-02 16:32 [PATCH v7 0/4] P1689R5 support Ben Boeckel
2023-07-02 16:32 ` [PATCH v7 1/4] spec: add a spec function to join arguments Ben Boeckel
2023-07-02 16:32 ` [PATCH v7 2/4] p1689r5: initial support Ben Boeckel
2023-08-23 19:37   ` Jason Merrill
2023-07-02 16:32 ` Ben Boeckel [this message]
2023-07-02 16:32 ` [PATCH v7 4/4] c++modules: report module mapper files as a dependency Ben Boeckel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230702163211.3396210-4-ben.boeckel@kitware.com \
    --to=ben.boeckel@kitware.com \
    --cc=brad.king@kitware.com \
    --cc=fortran@gcc.gnu.org \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=gcc@gcc.gnu.org \
    --cc=jason@redhat.com \
    --cc=nathan@acm.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).