[v4] preprocessor: More escapes for Makefile rules (-M option) [PR41329, PR121450]

Message ID 20250916161943.39675-1-joergboe@snafu.de
State New
Headers
Series [v4] preprocessor: More escapes for Makefile rules (-M option) [PR41329, PR121450] |

Commit Message

joergboe Sept. 16, 2025, 4:19 p.m. UTC
  This patch adds support for more characters that are special to GNU make in
file-names.
Especially GNU make expects in rules that #, %, :, *, ? and [ characters
are preceded by a backslash to remove their special meaning.

	PR preprocessor/41329

	PR preprocessor/121450

libcpp/ChangeLog:

	* mkdeps.cc (munge): Quote additional characters: %, :, *, ? and [

gcc/testsuite/ChangeLog:

	* g++.dg/modules/dep-1_a.C: Adapt tescase
	* g++.dg/modules/dep-1_b.C: Likewise
	* g++.dg/modules/dep-2.C: Likewise
---
 gcc/testsuite/g++.dg/modules/dep-1_a.C |  4 +-
 gcc/testsuite/g++.dg/modules/dep-1_b.C |  4 +-
 gcc/testsuite/g++.dg/modules/dep-2.C   |  6 +--
 libcpp/mkdeps.cc                       | 52 +++++++++++++++++++-------
 4 files changed, 45 insertions(+), 21 deletions(-)
  

Comments

Jakub Jelinek Sept. 16, 2025, 4:34 p.m. UTC | #1
On Tue, Sep 16, 2025 at 06:19:41PM +0200, Joerg Boehmer wrote:
> -/* Apply Make quoting to STR, TRAIL.  Note that it's not possible to
> -   quote all such characters - e.g. \n, %, *, ?, [, \ (in some
> -   contexts), and ~ are not properly handled.  It isn't possible to
> -   get this right in any current version of Make.  (??? Still true?
> -   Old comment referred to 3.76.1.)  */
> +/* Apply Make quoting to STR, TRAIL.  GNU make supports escape sequences in
> +   rules for characters that are special to make.  Note that it's not possible
> +   to quote all such characters and some are not properly handled.  The current
> +   status with version 4.4.1 of GNU make is:
> +   <newline> - No escape possible.  The character always introduces a line break
> +   or a continuation line.
> +   <space>, :, # - Escaped spaces are properly handled in targets and
> +   prerequisites.
> +   <tab> - Escaped tab-characters sometimes decay to a space in targets and
> +   prerequisites.
> +   =, ; - No escape possible.  These characters always retain their special
> +   meaning.

Can't one use e.g.
$(shell echo =)
for =, or $(shell echo ';') ?
Or perhaps better
$(firstword =)
or
$(firstword ;)
?

	Jakub
  
joergboe Sept. 16, 2025, 6:13 p.m. UTC | #2
Am 16.09.25 um 6:34 PM schrieb Jakub Jelinek:
> On Tue, Sep 16, 2025 at 06:19:41PM +0200, Joerg Boehmer wrote:
>> -/* Apply Make quoting to STR, TRAIL.  Note that it's not possible to
>> -   quote all such characters - e.g. \n, %, *, ?, [, \ (in some
>> -   contexts), and ~ are not properly handled.  It isn't possible to
>> -   get this right in any current version of Make.  (??? Still true?
>> -   Old comment referred to 3.76.1.)  */
>> +/* Apply Make quoting to STR, TRAIL.  GNU make supports escape sequences in
>> +   rules for characters that are special to make.  Note that it's not possible
>> +   to quote all such characters and some are not properly handled.  The current
>> +   status with version 4.4.1 of GNU make is:
>> +   <newline> - No escape possible.  The character always introduces a line break
>> +   or a continuation line.
>> +   <space>, :, # - Escaped spaces are properly handled in targets and
>> +   prerequisites.
>> +   <tab> - Escaped tab-characters sometimes decay to a space in targets and
>> +   prerequisites.
>> +   =, ; - No escape possible.  These characters always retain their special
>> +   meaning.
> Can't one use e.g.
> $(shell echo =)
> for =, or $(shell echo ';') ?
> Or perhaps better
> $(firstword =)
> or
> $(firstword ;)
> ?
>
> 	Jakub
>
Good idea.

It seems to work for the case $(firstword =);

it doesn't work for (firstword ;).

But supporting filenames with characters from the non-portable filename 
character set is only a voluntary feature of (GNU) Make. Therefore, I 
would only support characters that Make can handle directly through 
quoting. An expansion hack doesn't seem appropriate here.

Joerg
  

Patch

diff --git a/gcc/testsuite/g++.dg/modules/dep-1_a.C b/gcc/testsuite/g++.dg/modules/dep-1_a.C
index 3e92eeaef9f..f90c2378702 100644
--- a/gcc/testsuite/g++.dg/modules/dep-1_a.C
+++ b/gcc/testsuite/g++.dg/modules/dep-1_a.C
@@ -4,6 +4,6 @@  export module m:part;
 // { dg-module-cmi m:part }
 
 // All The Backslashes!
-// { dg-final { scan-file dep-1_a.d {\nm:part\.c\+\+-module: gcm.cache/m-part\.gcm} } }
+// { dg-final { scan-file dep-1_a.d {\nm\\:part\.c\+\+-module: gcm.cache/m-part\.gcm} } }
 // { dg-final { scan-file dep-1_a.d {\ngcm.cache/m-part\.gcm:| dep-1_a\.o} } }
-// { dg-final { scan-file dep-1_a.d {\n\.PHONY: m:part\.c\+\+-module} } }
+// { dg-final { scan-file dep-1_a.d {\n\.PHONY: m\\:part\.c\+\+-module} } }
diff --git a/gcc/testsuite/g++.dg/modules/dep-1_b.C b/gcc/testsuite/g++.dg/modules/dep-1_b.C
index 265ebfcda64..1ecb31c230e 100644
--- a/gcc/testsuite/g++.dg/modules/dep-1_b.C
+++ b/gcc/testsuite/g++.dg/modules/dep-1_b.C
@@ -3,8 +3,8 @@  export module m;
 // { dg-module-cmi m }
 
 export import :part;
-// { dg-final { scan-file dep-1_b.d {\ndep-1_b\.s gcm.cache/m\.gcm: m:part\.c\+\+-module} } }
+// { dg-final { scan-file dep-1_b.d {\ndep-1_b\.s gcm.cache/m\.gcm: m\\:part\.c\+\+-module} } }
 // { dg-final { scan-file dep-1_b.d {\nm\.c\+\+-module: gcm.cache/m\.gcm} } }
 // { dg-final { scan-file dep-1_b.d {\n\.PHONY: m\.c\+\+-module} } }
 // { dg-final { scan-file dep-1_b.d {\ngcm.cache/m\.gcm:| dep-1_b.o} } }
-// { dg-final { scan-file dep-1_b.d {\nCXX_IMPORTS \+= m:part\.c\+\+-module} } }
+// { dg-final { scan-file dep-1_b.d {\nCXX_IMPORTS \+= m\\:part\.c\+\+-module} } }
diff --git a/gcc/testsuite/g++.dg/modules/dep-2.C b/gcc/testsuite/g++.dg/modules/dep-2.C
index 3c869755785..eca5653f548 100644
--- a/gcc/testsuite/g++.dg/modules/dep-2.C
+++ b/gcc/testsuite/g++.dg/modules/dep-2.C
@@ -5,8 +5,8 @@  module m:part;
 // { dg-module-cmi !m:part }
 
 // All The Backslashes!
-// { dg-final { scan-file dep-2.d {\nm:part\.c\+\+-module: gcm.cache/m-part\.gcm} } }
-// { dg-final { scan-file dep-2.d {\ngcm.cache/m:part\.gcm:| dep-2\.o} } }
-// { dg-final { scan-file dep-2.d {\n\.PHONY: m:part\.c\+\+-module} } }
+// { dg-final { scan-file dep-2.d {\nm\\:part\.c\+\+-module: gcm.cache/m-part\.gcm} } }
+// { dg-final { scan-file dep-2.d {\ngcm.cache/m\\:part\.gcm:| dep-2\.o} } }
+// { dg-final { scan-file dep-2.d {\n\.PHONY: m\\:part\.c\+\+-module} } }
 
 // { dg-final { scan-file dep-2.i {\nmodule m:part;\n} } }
diff --git a/libcpp/mkdeps.cc b/libcpp/mkdeps.cc
index a4bea6e47d4..fe620e6c927 100644
--- a/libcpp/mkdeps.cc
+++ b/libcpp/mkdeps.cc
@@ -120,11 +120,30 @@  public:
   unsigned short quote_lwm;
 };
 
-/* Apply Make quoting to STR, TRAIL.  Note that it's not possible to
-   quote all such characters - e.g. \n, %, *, ?, [, \ (in some
-   contexts), and ~ are not properly handled.  It isn't possible to
-   get this right in any current version of Make.  (??? Still true?
-   Old comment referred to 3.76.1.)  */
+/* Apply Make quoting to STR, TRAIL.  GNU make supports escape sequences in
+   rules for characters that are special to make.  Note that it's not possible
+   to quote all such characters and some are not properly handled.  The current
+   status with version 4.4.1 of GNU make is:
+   <newline> - No escape possible.  The character always introduces a line break
+   or a continuation line.
+   <space>, :, # - Escaped spaces are properly handled in targets and
+   prerequisites.
+   <tab> - Escaped tab-characters sometimes decay to a space in targets and
+   prerequisites.
+   =, ; - No escape possible.  These characters always retain their special
+   meaning.
+   % - Quoting is required in targets.  Quoting is not required in
+   prerequisites.
+   | - Quoting is required in prerequisites.  Quoting is not required in
+   targets.
+   *, ?, [ - Quoting required in targets and prerequisites but not always
+   properly handled.  See: https://savannah.gnu.org/bugs/?67517.
+   ~ - This character has no special meaning if it is not used at the beginning
+   of a word.
+   & - Quoting is not required.
+   $ - The special significance of ‘$’ is removed by writing ‘$$’.
+   \ - The quoting of the backslash character depends on the following
+   character.  */
 
 static const char *
 munge (const char *str, const char *trail = nullptr)
@@ -157,18 +176,23 @@  munge (const char *str, const char *trail = nullptr)
 
 	    case ' ':
 	    case '\t':
-	      /* GNU make uses a weird quoting scheme for white space.
-		 A space or tab preceded by 2N+1 backslashes
-		 represents N backslashes followed by space; a space
-		 or tab preceded by 2N backslashes represents N
-		 backslashes at the end of a file name; and
-		 backslashes in other contexts should not be
+	    case '#':
+	    case ':':
+	    case '*':
+	    case '?':
+	    case '[':
+	    case '%':
+	      /* GNU make uses a weird quoting scheme for those special
+		 characters in the context of a target or prerequisite.
+		 A special character preceded by 2N+1 backslashes
+		 represents N backslashes and the following character
+		 loses its special meaning; a special
+		 character preceded by 2N backslashes represents N
+		 backslashes and the special meaning of the character is
+		 retained; and backslashes in other contexts should not be
 		 doubled.  */
 	      while (slashes--)
 		buf[dst++] = '\\';
-	      /* FALLTHROUGH  */
-
-	    case '#':
 	      buf[dst++] = '\\';
 	      /* FALLTHROUGH  */