URL:
  <https://savannah.gnu.org/bugs/?67750>

                 Summary: removal of special prefix chars under .ONESHELL
breaks multi-line strings
                   Group: make
               Submitter: edquist
               Submitted: Mon 01 Dec 2025 12:48:47 PM UTC
                Severity: 3 - Normal
                Priority: 5 - Normal
              Item Group: Bug
                  Status: None
                 Privacy: Public
             Assigned to: None
             Open/Closed: Open
         Discussion Lock: Unlocked
       Component Version: 4.4.1
        Operating System: POSIX-Based
           Fixed Release: None
           Triage Status: None


    _______________________________________________________

Follow-up Comments:


-------------------------------------------------------
Date: Mon 01 Dec 2025 12:48:47 PM UTC By: Carl <edquist>
Hi there!

For POSIX-style shells, one use case for .ONESHELL is to allow passing
multi-line string arguments to a program, or similarly, to use a multi-line
here-doc as input for a program.

However, it appears that any combination of leading whitespace and special
prefix chars gets removed from multi-line string literals or here-docs.
(effectively, s/^[ \t@+-]*//gm)

This breaks correct behavior and is never what a user would want.

(A user adding .ONESHELL might lazily leave superfluous '@' prefix chars
for each command, but would never have put them for echo suppression in
the middle of a multi-line string or here-doc in the first place.)


This "special feature" is explained in the documentation for .ONESHELL
(5.3.1 Using One Shell).  It says:

> As a special feature, if SHELL is determined to be a POSIX-style shell,
> the special prefix characters in "internal" recipe lines will be
> removed before the recipe is processed. This feature is intended to
> allow existing makefiles to add the .ONESHELL special target and still
> run properly without extensive modifications. Since the special prefix
> characters are not legal at the beginning of a line in a POSIX shell
> script this is not a loss in functionality.

Now first of all, special prefix characters (@, +, -) _are_ legal at the
beginning of lines -- you can have an executable in your PATH that starts
with one of these characters, for instance.

But more troubling, multi-line string literals or here-docs can include
program text for other interpreters (perl, python), where stripping these
characters (or leading whitespace) will break the program.

So there is definitely loss of functionality.


Version / platform info:

$ make --version
GNU Make 4.4.1
Built for x86_64-pc-linux-gnu



Here are makefile examples showing how removing leading whitespace in a
here-doc sent to python breaks the script, and likewise how removing @+-
characters in a multi-line string argument breaks a perl invocation.

(Makefile examples also attached.)

Python example:


$ cat oneshell.py.mk

SHELL = /bin/bash

.ONESHELL:

example:
        python <<'EOT'
        for x in "abc":
            print(x)
        EOT



Breaks because indentation matters:


$ make -f oneshell.py.mk
python <<'EOT'
for x in "abc":
print(x)
EOT
  File "<stdin>", line 2
    print(x)
    ^^^^^
IndentationError: expected an indented block after 'for' statement on line 1
make: *** [oneshell.py.mk:7: example] Error 1


Perl example:


$ cat oneshell.pl.mk

SHELL = /bin/bash

.ONESHELL:

example:
        echo abc | perl -lwne '
        /(a)?(b)?(c)?(d)?(e)?(f)?(g)?/;
        print
        @+ - @-, " unmatched capture buffers"
        '




Breaks, removing the entire "@+ - @-" expression:


$ make -f oneshell.pl.mk
echo abc | perl -lwne '
/(a)?(b)?(c)?(d)?(e)?(f)?(g)?/;
print
, " unmatched capture buffers"
'
Useless use of a constant (" unmatched capture buffers") in void context at -e
line 3.
abc



Again, the problem is that make determines that bash is a POSIX-style shell,
and it assumes that means that all lines in its program text are for the
shell.  But that is a bad assumption because the shell can contain text
for other interpreters or filters.

... My current work-around is to trick make into thinking that the SHELL
is not POSIX-style, by invoking bash indirectly through env:



SHELL       = /usr/bin/env
.SHELLFLAGS = /bin/bash -c


Then you get the multi-line input verbatim, and the programs execute
properly.

Here is the correct output, using the 'env bash' workaround:


$ make -f oneshell.py.workaround.mk
python <<'EOT'
for x in "abc":
    print(x)
EOT
a
b
c



$ make -f oneshell.pl.workaround.mk
echo abc | perl -lwne '
/(a)?(b)?(c)?(d)?(e)?(f)?(g)?/;
print
@+ - @-, " unmatched capture buffers"
'
4 unmatched capture buffers


...

I would suggest that this is both a documentation mistake (the special
feature _does_ result in loss of functionality of the modified recipes,
and the docs don't even mention that whitespace is also removed),
_and a bug_ (it breaks what should be valid multi-line POSIX-shell recipies).

It would have been better if this "special feature" of stripping
special prefix chars was opt-in (lazy programmers adding .ONESHELL
could have also made a .ONESHELL_REMOVE_LEADING_SPECIAL_PREFIX_CHARS,
or something, if they didn't want to manually remove the '@'
echo-suppresion prefix from internal recipe lines).

But supposing it's too late for that, and you don't want to break existing
lazily-authored makefiles in the wild, perhaps you could at least add an
opt-out target.  Maybe something like .ONESHELL_VERBATIM ...

For now, the 'env bash' trick does work around the issue, but it feels
kind of silly :)

...

Thanks!






    _______________________________________________________
File Attachments:

Example makefiles, with and without &quot;env bash&quot; workaround

Name: oneshell.pl.mk                 Size: 149B
    <https://file.savannah.gnu.org/file/oneshell.pl.mk?file_id=57887>

Name: oneshell.pl.workaround.mk      Size: 185B

<https://file.savannah.gnu.org/file/oneshell.pl.workaround.mk?file_id=57888>

Name: oneshell.py.mk                 Size: 94B
    <https://file.savannah.gnu.org/file/oneshell.py.mk?file_id=57885>

Name: oneshell.py.workaround.mk      Size: 130B

<https://file.savannah.gnu.org/file/oneshell.py.workaround.mk?file_id=57886>



    AGPL NOTICE

These attachments are served by Savane. You can download the corresponding
source code of Savane at
https://savannah.gnu.org/source/savane-2753d22959ed8b20bdaba68a262c3d3b8776f354.tar.gz

    _______________________________________________________

Reply to this item at:

  <https://savannah.gnu.org/bugs/?67750>

_______________________________________________
Message sent via Savannah
https://savannah.gnu.org/

Attachment: signature.asc
Description: PGP signature

Reply via email to