#! /bin/sh # $NetBSD: subst.sh,v 1.49 2020/06/11 20:10:53 rillig Exp $ # # Tests for mk/subst.mk. # set -eu . './test.subr' test_case_set_up() { create_file 'prepare-subst.mk' <<-EOF # The tools that are used by subst.mk AWK= awk CHMOD= chmod CMP= cmp DIFF= diff ECHO= echo MKDIR= mkdir -p MV= mv RM= rm RMDIR= rmdir SED= sed TEST= test TOUCH= touch TOUCH_FLAGS= # none TR= tr TRUE= true # Commands that are specific to pkgsrc RUN= @set -e; STEP_MSG= echo '=>' DO_NADA= : do-nada INFO_MSG= echo 'info:' WARNING_MSG= echo 'warning:' FAIL_MSG= sh $pkgsrcdir/mk/scripts/fail echo 'fail:' 1>&2 WRKDIR= $PWD WRKSRC= $PWD EOF create_file_lines "$mocked_pkgsrcdir/show-pkg-fail-reason.mk" \ 'show-pkg-fail-reasons: .PHONY' \ '.if ${PKG_FAIL_REASON:M*}' \ ' @printf "fail reason: %s\n" ${PKG_FAIL_REASON} 1>&2 && exit 1' \ '.endif' } if test_case_begin 'single file'; then # A single file is patched successfully. create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= subst-single.txt SUBST_SED.class= -e 's,before,after,' .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines 'subst-single.txt' \ 'before' run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in subst-single.txt' assert_that 'subst-single.txt' --file-is-lines \ 'after' test_case_end fi if test_case_begin 'several individual files'; then # Several individual files are patched successfully. create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= first second third SUBST_SED.class= -e 's,file,example,' .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines 'first' 'the first file' create_file_lines 'second' 'the second file' create_file_lines 'third' 'the third file' run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in first second third' assert_that 'first' --file-is-lines 'the first example' assert_that 'second' --file-is-lines 'the second example' assert_that 'third' --file-is-lines 'the third example' test_case_end fi if test_case_begin 'several files by pattern'; then # Several files are patched successfully. # The filenames are given by a pattern. create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= pattern-* SUBST_SED.class= -e 's,file,example,' .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines 'pattern-first' 'the first file' create_file_lines 'pattern-second' 'the second file' create_file_lines 'pattern-third' 'the third file' run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in pattern-*' assert_that 'pattern-first' --file-is-lines 'the first example' assert_that 'pattern-second' --file-is-lines 'the second example' assert_that 'pattern-third' --file-is-lines 'the third example' test_case_end fi if test_case_begin 'pattern with 1 noop, no-op ok'; then # Several files are given via a pattern. # Most of the files are patched, but one stays the same. # Since it is easier to give a too broad pattern like *.py # than to exclude a few files from such a pattern, # only an info is logged. # This is not an error. create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= pattern-* SUBST_SED.class= -e 's,file,example,' SUBST_NOOP_OK.class= yes .include "prepare-subst.mk" .include "mk/subst.mk" all: subst-class EOF create_file_lines 'pattern-first' 'the first file' create_file_lines 'pattern-second' 'the second is already an example' create_file_lines 'pattern-third' 'the third file' run_bmake 'testcase.mk' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in pattern-*' \ 'info: [subst.mk:class] Nothing changed in "pattern-second".' assert_that 'pattern-first' --file-is-lines 'the first example' assert_that 'pattern-second' --file-is-lines 'the second is already an example' assert_that 'pattern-third' --file-is-lines 'the third example' test_case_end fi if test_case_begin 'pattern with 1 noop, no-op not ok'; then # Several files are given via a pattern. # Most of the files are patched, but one stays the same. # Since it is easier to give a too broad pattern like *.py # than to exclude a few files from such a pattern, # only a warning is logged. # This is not an error. create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= pattern-* SUBST_SED.class= -e 's,file,example,' SUBST_NOOP_OK.class= no .include "prepare-subst.mk" .include "mk/subst.mk" all: subst-class EOF create_file_lines 'pattern-first' 'the first file' create_file_lines 'pattern-second' 'the second is already an example' create_file_lines 'pattern-third' 'the third file' run_bmake 'testcase.mk' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in pattern-*' \ 'warning: [subst.mk:class] Nothing changed in "pattern-second".' assert_that 'pattern-first' --file-is-lines 'the first example' assert_that 'pattern-second' --file-is-lines 'the second is already an example' assert_that 'pattern-third' --file-is-lines 'the third example' test_case_end fi if test_case_begin 'single file noop, noop_ok=yes'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= single SUBST_SED.class= -e 's,file,example,' SUBST_NOOP_OK.class= yes .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines 'single' 'already an example' run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in single' \ 'info: [subst.mk:class] Nothing changed in "single".' assert_that 'single' --file-is-lines 'already an example' test_case_end fi if test_case_begin 'single file noop, noop_ok=no'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= single SUBST_SED.class= -e 's,file,example,' SUBST_NOOP_OK.class= no .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines 'single' 'already an example' run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in single' \ 'warning: [subst.mk:class] Nothing changed in "single".' \ 'fail: [subst.mk:class] The filename pattern "single" has no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" assert_that 'single' --file-is-lines 'already an example' assert_that "$exitcode" --equals '1' test_case_end fi if test_case_begin 'single file nonexistent'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= nonexistent SUBST_SED.class= -e 's,file,example,' SUBST_NOOP_OK.class= no .include "prepare-subst.mk" .include "mk/subst.mk" EOF run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in nonexistent' \ 'warning: [subst.mk:class] Ignoring nonexistent file "nonexistent".' \ 'fail: [subst.mk:class] The filename pattern "nonexistent" has no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" assert_that "$exitcode" --equals '1' test_case_end fi if test_case_begin 'single file nonexistent ok'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= nonexistent SUBST_SED.class= -e 's,file,example,' SUBST_NOOP_OK.class= yes .include "prepare-subst.mk" .include "mk/subst.mk" EOF run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in nonexistent' \ 'info: [subst.mk:class] Ignoring nonexistent file "nonexistent".' assert_that "$exitcode" --equals '0' test_case_end fi if test_case_begin 'several filename patterns, 1 nonexistent, no-op ok'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= *exist* *not-found* SUBST_SED.class= -e 's,file,example,' SUBST_NOOP_OK.class= yes .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines 'exists' 'this file exists' run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in *exist* *not-found*' \ 'info: [subst.mk:class] Ignoring nonexistent file "./*not-found*".' assert_that 'exists' --file-is-lines 'this example exists' assert_that "$exitcode" --equals '0' test_case_end fi if test_case_begin 'several filename patterns, 1 nonexistent, no-op not ok'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= *exist* *not-found* SUBST_SED.class= -e 's,file,example,' SUBST_NOOP_OK.class= no .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines 'exists' 'this file exists' run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in *exist* *not-found*' \ 'warning: [subst.mk:class] Ignoring nonexistent file "./*not-found*".' \ 'fail: [subst.mk:class] The filename pattern "*not-found*" has no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" assert_that 'exists' --file-is-lines 'this example exists' assert_that "$exitcode" --equals '1' test_case_end fi if test_case_begin 'multiple missing files, all are reported at once, no-op not ok'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= does not exist SUBST_SED.class= -e 'sahara' SUBST_NOOP_OK.class= no .include "prepare-subst.mk" .include "mk/subst.mk" EOF run_bmake 'testcase.mk' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in does not exist' \ 'warning: [subst.mk:class] Ignoring nonexistent file "does".' \ 'warning: [subst.mk:class] Ignoring nonexistent file "not".' \ 'warning: [subst.mk:class] Ignoring nonexistent file "exist".' \ 'fail: [subst.mk:class] The filename patterns "does not exist" have no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" assert_that "$exitcode" --equals '1' test_case_end fi if test_case_begin 'multiple missing files, all are reported at once, no-op ok'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= does not exist SUBST_SED.class= -e 'sahara' SUBST_NOOP_OK.class= yes .include "prepare-subst.mk" .include "mk/subst.mk" EOF run_bmake 'testcase.mk' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in does not exist' \ 'info: [subst.mk:class] Ignoring nonexistent file "does".' \ 'info: [subst.mk:class] Ignoring nonexistent file "not".' \ 'info: [subst.mk:class] Ignoring nonexistent file "exist".' assert_that "$exitcode" --equals '0' test_case_end fi if test_case_begin 'multiple no-op files, all are reported at once, no-op not ok'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= first second third SUBST_SED.class= -e 's,from,to,' SUBST_NOOP_OK.class= no .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines 'first' 'text' create_file_lines 'second' 'second' create_file_lines 'third' 'third' run_bmake 'testcase.mk' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in first second third' \ 'warning: [subst.mk:class] Nothing changed in "first".' \ 'warning: [subst.mk:class] Nothing changed in "second".' \ 'warning: [subst.mk:class] Nothing changed in "third".' \ 'fail: [subst.mk:class] The filename patterns "first second third" have no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" assert_that "$exitcode" --equals '1' test_case_end fi if test_case_begin 'multiple no-op files, all are reported at once, no-op ok'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= first second third SUBST_SED.class= -e 's,from,to,' SUBST_NOOP_OK.class= yes .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines 'first' 'text' create_file_lines 'second' 'second' create_file_lines 'third' 'third' run_bmake 'testcase.mk' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in first second third' \ 'info: [subst.mk:class] Nothing changed in "first".' \ 'info: [subst.mk:class] Nothing changed in "second".' \ 'info: [subst.mk:class] Nothing changed in "third".' assert_that "$exitcode" --equals '0' test_case_end fi if test_case_begin 'late evaluation of SUBST_FILES'; then # Ensure that SUBST_FILES is evaluated as late as possible. # Evaluating it early breaks cases like pkglocaledir where the # list of files is generated by a shell command. # See mk/configure/replace-localedir.mk. # If SUBST_FILES were to be evaluated early, it would expand to # "too-early", since that file exists when testcase.mk is parsed, # as opposed to "first" "second" "third", which only exist when # the subst-class target is run. create_file 'testcase.mk' < '\${WRKSRC}/first' \${RUN} \${ECHO} 'from' > '\${WRKSRC}/second' \${RUN} \${ECHO} 'from' > '\${WRKSRC}/third' EOF create_file_lines 'package-1.0/too-early' \ 'from' run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in first third' assert_that "package-1.0/first" --file-is-lines 'to' assert_that "package-1.0/second" --file-is-lines 'from' assert_that "package-1.0/third" --file-is-lines 'to' assert_that "$exitcode" --equals '0' test_case_end fi if test_case_begin 'special characters in filenames'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= * SUBST_SED.class= -e s,before,after, SUBST_NOOP_OK.class= yes .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines ' !"#$%&'\''()*+,-.' 'before' create_file_lines '0123456789:;<=>?' 'before' create_file_lines '@ABCDEFGHIJKLMNO' 'before' create_file_lines 'PQRSTUVWXYZ[\]^_' 'before' create_file_lines '`abcdefghijklmno' 'before' create_file_lines 'pqrstuvwxyz{|}~' 'before' create_file_lines '--no-option' 'before' create_file_lines '.hidden' 'before' run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in *' \ 'info: [subst.mk:class] Nothing changed in "prepare-subst.mk".' assert_that ' !"#$%&'\''()*+,-.' --file-is-lines 'after' assert_that '0123456789:;<=>?' --file-is-lines 'after' assert_that '@ABCDEFGHIJKLMNO' --file-is-lines 'after' assert_that 'PQRSTUVWXYZ[\]^_' --file-is-lines 'after' assert_that '`abcdefghijklmno' --file-is-lines 'after' assert_that 'pqrstuvwxyz{|}~' --file-is-lines 'after' assert_that '--no-option' --file-is-lines 'after' assert_that '.hidden' --file-is-lines 'before' test_case_end fi if test_case_begin 'brackets in filename patterns'; then create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= class SUBST_STAGE.class= pre-configure SUBST_FILES.class= [*] SUBST_SED.class= -e s,before,after, SUBST_NOOP_OK.class= yes .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines 'any' 'before' create_file_lines 'x' 'before' create_file_lines '*' 'before' create_file_lines '[*]' 'before' run_bmake 'testcase.mk' 'subst-class' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "class" in [*]' assert_that "$exitcode" --equals '0' assert_that 'any' --file-is-lines 'before' assert_that 'x' --file-is-lines 'before' assert_that '*' --file-is-lines 'after' assert_that '[*]' --file-is-lines 'before' test_case_end fi if test_case_begin 'duplicate SUBST class'; then create_file 'testcase.mk' <&2 .include "prepare-subst.mk" .include "mk/subst.mk" EOF run_bmake 'testcase.mk' 'all' \ 1> "$tmpdir/stdout" \ 2> "$tmpdir/stderr" \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/stdout" --file-is-empty assert_that "$tmpdir/stderr" --file-is-lines \ 'fail reason: [subst.mk] duplicate SUBST class in: one one two' assert_that "$exitcode" --equals '0' test_case_end fi if test_case_begin 'several SUBST classes'; then # It's ok to have several SUBST classes that apply to the same file. # The order of execution is not guaranteed though. create_file_lines 'file' 'zero one two three four' create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= one SUBST_STAGE.one= pre-configure SUBST_FILES.one= file SUBST_SED.one= -e 's,one,I,' SUBST_CLASSES+= two SUBST_STAGE.two= pre-configure SUBST_FILES.two= file SUBST_SED.two= -e 's,two,II,' SUBST_CLASSES+= three SUBST_STAGE.three= pre-configure SUBST_FILES.three= file SUBST_SED.three= -e 's,three,III,' .include "prepare-subst.mk" .include "mk/subst.mk" EOF run_bmake 'testcase.mk' 'pre-configure' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? # The order of the above output is not guaranteed. LC_ALL=C sort < "$tmpdir/output" > "$tmpdir/output-sorted" assert_that 'file' --file-is-lines 'zero I II III four' assert_that "$tmpdir/output-sorted" --file-is-lines \ '=> Substituting "one" in file' \ '=> Substituting "three" in file' \ '=> Substituting "two" in file' test_case_end fi if test_case_begin 'show diff'; then create_file_lines 'file' 'one' 'two' 'three' create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= two SUBST_STAGE.two= pre-configure SUBST_FILES.two= file SUBST_SED.two= -e 's,two,II,' SUBST_SHOW_DIFF.two= yes .include "prepare-subst.mk" .include "mk/subst.mk" EOF LC_ALL=C \ run_bmake 'testcase.mk' 'pre-configure' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? awk '{ if (/^(---|\+\+\+) /) { print $1 " " $2 " (filtered timestamp)" } else { print $0 } }' \ < "$tmpdir/output" > "$tmpdir/output-filtered" assert_that 'file' --file-is-lines 'one' 'II' 'three' assert_that "$tmpdir/output-filtered" --file-is-lines \ '=> Substituting "two" in file' \ '--- file (filtered timestamp)' \ '+++ file.subst.sav (filtered timestamp)' \ '@@ -1,3 +1,3 @@' \ ' one' \ '-two' \ '+II' \ ' three' test_case_end fi if test_case_begin 'global show diff'; then create_file_lines 'file' \ 'one' \ 'two' \ 'three' create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= two SUBST_STAGE.two= pre-configure SUBST_FILES.two= file SUBST_SED.two= -e 's,two,II,' SUBST_SHOW_DIFF= yes .include "prepare-subst.mk" .include "mk/subst.mk" EOF run_bmake 'testcase.mk' 'pre-configure' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? awk '{ if (/^(---|\+\+\+) /) { print $1 " " $2 " (filtered timestamp)" } else { print $0 } }' \ < "$tmpdir/output" > "$tmpdir/output-filtered" assert_that 'file' --file-is-lines \ 'one' \ 'II' \ 'three' assert_that "$tmpdir/output-filtered" --file-is-lines \ '=> Substituting "two" in file' \ '--- file (filtered timestamp)' \ '+++ file.subst.sav (filtered timestamp)' \ '@@ -1,3 +1,3 @@' \ ' one' \ '-two' \ '+II' \ ' three' test_case_end fi if test_case_begin 'SUBST_VARS'; then create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= vars' \ 'SUBST_STAGE.vars= pre-configure' \ 'SUBST_FILES.vars= vars.txt' \ 'SUBST_VARS.vars= PLAIN DQUOT SQUOT DELIM PRINTABLE' \ 'SUBST_VARS.vars+= UNDEFINED' \ '' \ 'PLAIN= plain' \ 'DQUOT= "double quoted"' \ 'SQUOT= '\''single quoted'\''' \ 'DELIM= hello, world' \ 'PRINTABLE= !"\#$$%&'\''()*+,-./09:;<=>?@AZ[\]^_`az{|}' \ '#UNDEFINED= # undefined' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'vars.txt' \ '@PLAIN@' \ '@DQUOT@' \ '@SQUOT@' \ '@DELIM@' \ '@PRINTABLE@' \ '@UNDEFINED@' run_bmake 'testcase.mk' 'pre-configure' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? # The double quotes and single quotes are kept since the variables # are treated as simple string variables, not as lists of shell # words. In these string variables, the quotes are part of the value. assert_that 'vars.txt' --file-is-lines \ 'plain' \ '"double quoted"' \ "'single quoted'" \ 'hello, world' \ '!"#$%&'\''()*+,-./09:;<=>?@AZ[\]^_`az{|}' \ '' assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "vars" in vars.txt' test_case_end fi if test_case_begin 'SUBST_VARS with surrounding whitespace'; then # Ensure that leading and trailing whitespace is preserved # in the variable values. create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= vars' \ 'SUBST_STAGE.vars= pre-configure' \ 'SUBST_FILES.vars= vars.txt' \ 'SUBST_VARS.vars= SPACE TAB NEWLINE' \ '' \ 'SPACE= ${:U }between spaces${:U }' \ 'TAB= ${:U }between tabs${:U }' \ 'NEWLINE= ${.newline}between newlines${.newline}' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'vars.txt' \ '@SPACE@' \ '@TAB@' \ '@NEWLINE@' run_bmake 'testcase.mk' 'pre-configure' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? space=' ' tab=' ' newline=' ' assert_that 'vars.txt' --file-is-lines \ "$space"'between spaces'"$space" \ "$tab"'between tabs'"$tab" \ "$newline"'between newlines'"$newline" assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "vars" in vars.txt' test_case_end fi if test_case_begin 'SUBST_VARS with backslashes'; then create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= bs' \ 'SUBST_STAGE.bs= pre-configure' \ 'SUBST_FILES.bs= backslash.txt' \ 'SUBST_VARS.bs= BACKSLASHES' \ '' \ 'BACKSLASHES= \" \, \\, \" \'\'' \0\000 \x40 \089 \a \$$' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'backslash.txt' '@BACKSLASHES@' run_bmake 'testcase.mk' 'pre-configure' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that 'backslash.txt' --file-is-lines \ '\" \, \\, \" \'\'' \0\000 \x40 \089 \a \$' assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "bs" in backslash.txt' test_case_end fi if test_case_begin 'SUBST_VARS for variables with regex characters'; then # Ensure that special regex characters like dots and parentheses # may appear in variable names and are properly escaped. # Variable names containing a dollar are not supported. # Bmake behaves very surprisingly when a $ is expanded inside a :C # modifier. Nobody needs this feature anyway, it was just an # experiment to see whether this would be theoretically possible. # Variable names ending with a backslash are not supported. # The backslash may only occur in the middle of the variable name. create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= vars' \ 'SUBST_STAGE.vars= pre-configure' \ 'SUBST_FILES.vars= vars.txt' \ 'SUBST_VARS.vars= VAR...... VAR.abcde' \ 'SUBST_VARS.vars+= VAR.() VAR.<> VAR.[]' \ 'SUBST_VARS.vars+= VAR.$$x VAR.^ VAR.\x' \ '' \ 'VAR......= dots' \ 'VAR.abcde= letters' \ 'VAR.()= parentheses' \ 'VAR.<>= angle brackets' \ 'VAR.[]= square brackets' \ 'VAR.$$x= dollar' \ 'VAR.^= circumflex' \ 'VAR.\x= backslash' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'vars.txt' \ '@VAR......@' \ '@VAR.abcde@' \ '@VAR.()@' \ '@VAR.<>@' \ '@VAR.[]@' \ '@VAR.$x@' \ '@VAR.^@' \ '@VAR.\x@' run_bmake 'testcase.mk' 'pre-configure' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that 'vars.txt' --file-is-lines \ 'dots' \ 'letters' \ 'parentheses' \ 'angle brackets' \ 'square brackets' \ '@VAR.$x@' \ 'circumflex' \ 'backslash' assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "vars" in vars.txt' test_case_end fi if test_case_begin 'pattern matches directory'; then # When a pattern matches a directory, that directory is silently # skipped. # # In this test case, the pattern also matches a regular file that # is actually modified. Therefore the pattern has an effect, and # there is no error message. create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= dir' \ 'SUBST_STAGE.dir= pre-configure' \ 'SUBST_FILES.dir= sub*' \ 'SUBST_VARS.dir= VAR' \ 'SUBST_NOOP_OK.dir= no' \ '' \ 'VAR= value' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'subdir/subfile' \ '@VAR@' create_file_lines 'subst-file' \ '@VAR@' run_bmake 'testcase.mk' 'pre-configure' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that 'subst-file' --file-is-lines 'value' assert_that 'subdir/subfile' --file-is-lines '@VAR@' # unchanged assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "dir" in sub*' test_case_end fi if test_case_begin 'pattern matches only directory'; then # When a pattern matches a directory, that directory is silently # skipped. create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= dir' \ 'SUBST_STAGE.dir= pre-configure' \ 'SUBST_FILES.dir= sub*' \ 'SUBST_VARS.dir= VAR' \ 'SUBST_NOOP_OK.dir= no' \ '' \ 'VAR= value' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'subdir/subfile' \ '@VAR@' run_bmake 'testcase.mk' 'pre-configure' \ 1> "$tmpdir/stdout" \ 2> "$tmpdir/stderr" \ && exitcode=0 || exitcode=$? assert_that 'subdir/subfile' --file-is-lines '@VAR@' # unchanged assert_that "$tmpdir/stdout" --file-is-lines \ '=> Substituting "dir" in sub*' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" assert_that "$tmpdir/stderr" --file-is-lines \ 'fail: [subst.mk:dir] The filename pattern "sub*" has no effect.' assert_that "$exitcode" --equals 1 test_case_end fi if test_case_begin 'two filename patterns have no effect'; then # All patterns of SUBST_FILES should be applied before erroring out, # to give a complete picture of the situation. create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_STAGE.id= pre-configure' \ 'SUBST_FILES.id= file1 file2' \ 'SUBST_VARS.id= A B' \ 'SUBST_NOOP_OK.id= no' \ 'A= a-value' \ 'B= b-value' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'file1' 'nothing to replace' create_file_lines 'file2' 'nothing to replace' run_bmake 'testcase.mk' 'pre-configure' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file1 file2' \ 'warning: [subst.mk:id] Nothing changed in "file1".' \ 'warning: [subst.mk:id] Nothing changed in "file2".' \ 'fail: [subst.mk:id] The filename patterns "file1 file2" have no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" test_case_end fi if test_case_begin 'empty SUBST_FILES'; then # An empty SUBST_FILES section is ok. # It may have been produced by a shell command like find(1). create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_STAGE.id= pre-configure' \ 'SUBST_FILES.id= # none' \ 'SUBST_SED.id= -e s,from,to,' \ 'SUBST_NOOP_OK.id= no' \ '' \ 'all:' \ ' @printf "%s\n" ${PKG_FAIL_REASON:Uok}' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' run_bmake 'testcase.mk' 'pre-configure' 'all' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in ' \ 'ok' test_case_end fi if test_case_begin 'empty SUBST_SED'; then create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_STAGE.id= pre-configure' \ 'SUBST_FILES.id= file' \ 'SUBST_SED.id= # none' \ 'SUBST_NOOP_OK.id= no' \ '' \ 'all:' \ ' @printf "%s\n" ${PKG_FAIL_REASON:Uok}' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' run_bmake 'testcase.mk' 'subst-id' 'all' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' \ 'warning: [subst.mk:id] Ignoring nonexistent file "file".' \ 'fail: [subst.mk:id] The filename pattern "file" has no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" test_case_end fi if test_case_begin 'typo in SUBST_CLASSES'; then # Look closely. The SUBST_CLASSES line contains a typo. # subst.mk does not catch this, but pkglint does. create_file_lines 'testcase.mk' \ 'SUBST_CLASSES=+ id' \ 'SUBST_STAGE.id= pre-configure' \ 'SUBST_FILES.id= file' \ 'SUBST_SED.id= # none' \ 'SUBST_NOOP_OK.id= no' \ '' \ 'all:' \ ' @printf "%s\n" ${PKG_FAIL_REASON:Uok}' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' run_bmake 'testcase.mk' 'pre-configure' 'all' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' \ 'warning: [subst.mk:id] Ignoring nonexistent file "file".' \ 'fail: [subst.mk:id] The filename pattern "file" has no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" test_case_end fi if test_case_begin 'executable bit is preserved'; then create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_STAGE.id= pre-configure' \ 'SUBST_FILES.id= cmd data' \ 'SUBST_VARS.id= VAR' \ 'VAR= replaced' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'cmd' \ '@VAR@' create_file_lines 'data' \ '@VAR@' chmod +x 'cmd' run_bmake 'testcase.mk' 'pre-configure' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in cmd data' assert_that 'cmd' --file-is-lines 'replaced' assert_that 'data' --file-is-lines 'replaced' [ -x 'cmd' ] \ || assert_fail 'cmd must still be executable\n' [ -x 'data' ] \ && assert_fail 'data must not be executable\n' test_case_end fi if test_case_begin 'unreadable file'; then create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_STAGE.id= pre-configure' \ 'SUBST_FILES.id= unreadable-file' \ 'SUBST_SED.id= -e s,before,after,' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'unreadable-file' \ 'before' chmod 0000 'unreadable-file' run_bmake 'testcase.mk' 'pre-configure' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in unreadable-file' \ 'sh: cannot open unreadable-file: permission denied' \ 'sh: cannot open unreadable-file: permission denied' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" test_case_end fi if test_case_begin 'identity substitution implementation'; then assert_identity() { _ai_expected="$1"; shift awk -f "$pkgsrcdir/mk/scripts/subst-identity.awk" -- ${1+"$@"} \ && _ai_actual='yes' || _ai_actual='no' [ "$_ai_actual" = "$_ai_expected" ] \ || assert_fail 'expected "%s", got "%s" for %s\n' "$_ai_expected" "$_ai_actual" "$*" } # If there is no SUBST_SED at all, this is not the situation # that is targeted by this test for identity substitution. assert_identity 'no' # no substitutions at all # Even though this is an identity substitution, it is missing # the -e option and thus does not follow the usual format. # Therefore it is considered just a normal substitution. assert_identity 'no' 's,from,from,' # The following are typical identity substitutions. # It does not matter whether the g modifier is there or not. # Unknown modifiers are not allowed though. assert_identity 'yes' -e 's,from,from,' assert_identity 'yes' -e 's;from;from;' assert_identity 'yes' -e 's,from,from,g' assert_identity 'no' -e 's,from,from,gunknown' # The identity substitution may include characters other than # A-Za-z0-9, but no characters that have a special meaning in # basic regular expressions. assert_identity 'yes' -e 's,/dev/audio,/dev/audio,' assert_identity 'yes' -e 's!/dev/audio!/dev/audio!' # There may be several identity substitutions in the same # SUBST_SED. As long as any of these substitutions is an # identity substitution, it can happen that a file stays the # same. It still might be modified in a different pkgsrc # configuration. assert_identity 'yes' -e 's;from;from;' -e 's!second!second!' assert_identity 'yes' -e 's,changing,x,' -e 's,id,id,' assert_identity 'yes' -e 's,id,id,' -e 's,changing,x,' assert_identity 'no' -e 's,changing,x,' -e 's,changing,x,' # A demonstration of all ASCII characters that may appear in an # identity substitution. # # The # and $ are excluded since they are interpreted specially # in Makefiles and would thus be confusing to the human reader. # # The characters *.?[\]^ have a special meaning in the pattern of the # substitution. # The & has a special meaning in the replacement of the # substitution. specials='!"%'\''()+,-/:;<=>@_`{|}~' assert_identity 'yes' -e "sX${specials}X${specials}X" # Regular expression meta-characters may be escaped using a # backslash or be enclosed in square brackets. assert_identity 'yes' -e 's,library\.so,library.so,g' assert_identity 'yes' -e 's,library[.]so,library.so,g' assert_identity 'yes' -e 's,[*],*,' assert_identity 'yes' -e 's,[$],$,' # When this happens, it is probably a mistake. assert_identity 'no' -e 's,,,' # Backslashes are not considered identity substitutions since # there might be tricky corner cases. assert_identity 'no' -e 's,\\,\\,' # Back-references are not considered identity substitutions. assert_identity 'no' -e 's,\1,\1,' # The & is interpreted specially in the replacement string. assert_identity 'no' -e 's,&&&,&&&,' assert_identity 'no' -e 's,\&,&,' assert_identity 'no' -e 's,[&],&,' assert_identity 'no' -e 's,&,\&,' # this would be an identity # References to Makefile variables can be identical too. # See converters/help2man for an example. assert_identity 'yes' -e 's,\$(var),$(var),' # POSIX 2004 and 2018 both define in section "9.3.8 BRE Expression # Anchoring" that a dollar-sign at the end of the string means # end-of-string. # # A dollar-sign followed by \) may or may not be an anchor. # In all other cases the dollar is an ordinary character. assert_identity 'yes' -e 's,$(var),$(var),' # Since this dollar-sign may or may not be an anchor, treat the # whole regular expression as not-an-identity. # # Since a regular expression with a subexpression must contain # \( and \), it does not count as an identity substitution anyway, # which makes the implementation simple. assert_identity 'no' -e 's,aaa\(aaa$\),aaa\(aaa$\),' assert_identity 'yes' -e 's,$a,$a,' assert_identity 'no' -e 's,a$,a$,' # Same for the circumflex. assert_identity 'yes' -e 's,a^,a^,' assert_identity 'no' -e 's,^a,^a,' assert_identity 'no' -e 's,\(^aaa\)aaa,\(^aaa\)aaa,' # Seen in games/bastet before 2020-05-16. assert_identity 'yes' -e 's,a,a,1' test_case_end fi if test_case_begin 'identity substitution, found in file'; then # There are many situations in which a fixed text is replaced # with a dynamic value that may or may not be equal to the # original text. # # Typical examples are s|man|${PKGMANDIR}|, s|/usr/pkg|${PREFIX}|, # s|/dev/audio|${DEVOSSAUDIO}|. # # It is not an error if these substitutions result in a no-op, # provided that the text is actually found in the file. # # Alternatives for this special exception would be: # # 1. Mark these blocks as SUBST_NOOP_OK. This would not detect # outdated definitions. Since this detection is the main goal # of SUBST_NOOP_OK, this is out of the question. # # 2. Surround these blocks with a condition like ".if ${VAR} != # fixed-value ... .endif". This pattern only works if VAR is # definitely assigned, which often requires a corresponding # .include line, leading to code bloat. It would also mean that # variables defined in bsd.pkg.mk could not be used in SUBST # blocks like these. create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_FILES.id= file' \ 'SUBST_SED.id= -e s,before,before,' \ 'SUBST_SED.id+= -e "s,before,before,"' \ "SUBST_SED.id+= -e 's,before,before,'" \ 'SUBST_NOOP_OK.id= no' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'file' \ 'before' run_bmake 'testcase.mk' 'subst-id' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' if [ -f "file.subst.sav" ]; then assert_fail 'temporary file still exists\n' fi test_case_end fi if test_case_begin 'identity substitution, not found in file'; then create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_FILES.id= file' \ 'SUBST_SED.id= s,before,before,' \ 'SUBST_NOOP_OK.id= no' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'file' \ 'other' run_bmake 'testcase.mk' 'subst-id' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' \ 'warning: [subst.mk:id] Nothing changed in "file".' \ 'fail: [subst.mk:id] The filename pattern "file" has no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" test_case_end fi if test_case_begin 'identity + effective substitution'; then create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_FILES.id= file' \ 'SUBST_SED.id= -e s,no-op,no-op,g' \ 'SUBST_SED.id+= -e s,from,to,' \ 'SUBST_NOOP_OK.id= no' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'file' \ 'from' run_bmake 'testcase.mk' 'subst-id' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' assert_that 'file' --file-is-lines \ 'to' test_case_end fi if test_case_begin 'identity + no-op substitution'; then # If there were only an identity substitution, it wouldn't be an # error. But since there is a regular substitution as well, # that substitution is an unexpected no-op and is therefore # flagged as an error. create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_FILES.id= file' \ 'SUBST_SED.id= -e s,no-op,no-op,g' \ 'SUBST_SED.id+= -e s,from,to,' \ 'SUBST_NOOP_OK.id= no' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'file' \ 'other' run_bmake 'testcase.mk' 'subst-id' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' \ 'warning: [subst.mk:id] Nothing changed in "file".' \ 'fail: [subst.mk:id] The filename pattern "file" has no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" assert_that 'file' --file-is-lines \ 'other' test_case_end fi if test_case_begin 'SUBST_FILTER_CMD + SUBST_SED in NOOP_OK=no mode'; then # If SUBST_FILTER_CMD is defined for a SUBST class, the # corresponding SUBST_SED and SUBST_VARS are ignored. To avoid # redundant variable definitions, this case fails fast. create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_FILES.id= file' \ 'SUBST_FILTER_CMD.id= tr -d "0-9"' \ 'SUBST_SED.id= -e s,x,x,' \ 'SUBST_NOOP_OK.id= no' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'file' \ 'letters 123 letters' create_file_lines "$tmpdir/main.mk" \ "PKGSRCDIR= $pkgsrcdir" \ ".PATH: $mocked_pkgsrcdir" \ ".PATH: $pkgsrcdir" \ '.include "testcase.mk"' \ '' \ 'all: subst-id' \ ' @printf '\''fail reason: %s\n'\'' ${PKG_FAIL_REASON} 1>&2' "$make" -f "$tmpdir/main.mk" 'all' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' \ 'fail reason: [subst.mk:id] SUBST_FILTER_CMD and SUBST_SED/SUBST_VARS cannot be combined.' assert_that 'file' --file-is-lines \ 'letters letters' test_case_end fi if test_case_begin 'SUBST_FILTER_CMD + empty SUBST_SED'; then # If SUBST_FILTER_CMD is defined for a SUBST class, # the corresponding SUBST_SED and SUBST_VARS are ignored. # To avoid redundant variable definitions, this case fails fast. # # This happens even if SUBST_SED or SUBST_VARS are empty since # there is no point in setting these to empty values. # In most cases they are constant and non-empty anyway. create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_FILES.id= file' \ 'SUBST_FILTER_CMD.id= tr -d "0-9"' \ 'SUBST_SED.id= # empty' \ 'SUBST_VARS.id= # empty' \ 'SUBST_NOOP_OK.id= no' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' \ '.include "show-pkg-fail-reason.mk"' run_bmake 'testcase.mk' 'show-pkg-fail-reasons' 'subst-id' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ 'fail reason: [subst.mk:id] SUBST_FILTER_CMD and SUBST_SED/SUBST_VARS cannot be combined.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" test_case_end fi if test_case_begin 'effective SUBST_FILTER_CMD in NOOP_OK=no mode'; then create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_FILES.id= file' \ 'SUBST_FILTER_CMD.id= tr -d "0-9"' \ 'SUBST_NOOP_OK.id= no' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'file' \ 'letters 123 letters' run_bmake 'testcase.mk' 'subst-id' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' assert_that 'file' --file-is-lines \ 'letters letters' test_case_end fi if test_case_begin 'no-op SUBST_FILTER_CMD in NOOP_OK=no mode'; then create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_FILES.id= file' \ 'SUBST_FILTER_CMD.id= tr -d "0-9"' \ 'SUBST_NOOP_OK.id= no' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'file' \ 'only letters' run_bmake 'testcase.mk' 'subst-id' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' \ 'warning: [subst.mk:id] Nothing changed in "file".' \ 'fail: [subst.mk:id] The filename pattern "file" has no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" assert_that 'file' --file-is-lines \ 'only letters' test_case_end fi if test_case_begin 'backtick in SUBST_SED'; then create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_FILES.id= file' \ "SUBST_SED.id= -e 's,\"\\\\\`,\"\\\\\`,'" \ 'SUBST_NOOP_OK.id= yes' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'file' \ 'from`' run_bmake 'testcase.mk' 'subst-id' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' \ 'info: [subst.mk:id] Nothing changed in "file".' test_case_end fi if test_case_begin 'multiple sed commands with semicolon'; then # From PR pkg/55226: # ===> Configuring for perl-5.30.2 # sh: 1: Syntax error: Word "/d"p" unexpected (expecting ")") create_file_lines 'testcase.mk' \ 'SUBST_CLASSES+= id' \ 'SUBST_FILES.id= file' \ 'SUBST_SED.id= -e "s/755/755/g;/umask(/d"' \ 'SUBST_NOOP_OK.id= no' \ '' \ '.include "prepare-subst.mk"' \ '.include "mk/subst.mk"' create_file_lines 'file' \ '$mode = 755;' run_bmake 'testcase.mk' 'subst-id' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' \ 'warning: [subst.mk:id] Nothing changed in "file".' \ 'fail: [subst.mk:id] The filename pattern "file" has no effect.' \ '*** Error code 1' \ '' \ 'Stop.' \ "$make: stopped in $PWD" test_case_end fi if test_case_begin 'several substitution, only one applies to file'; then # https://mail-index.netbsd.org/pkgsrc-changes/2020/06/06/msg215480.html create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= id SUBST_FILES.id= first second SUBST_SED.id= -e 's,first,first-modified,' SUBST_SED.id+= -e 's,second,second,' SUBST_NOOP_OK.id= no .include "prepare-subst.mk" .include "mk/subst.mk" EOF create_file_lines 'first' 'first' create_file_lines 'second' 'second' run_bmake 'testcase.mk' 'subst-id' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? # Each of the files contains at least one of the sed patterns, # therefore the substitutions _could_ have an effect, depending # on the pkgsrc configuration. assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in first second' assert_that 'first' --file-contains-exactly 'first-modified' assert_that 'second' --file-contains-exactly 'second' test_case_end fi if test_case_begin 'identity substitution with newline'; then # Ensures that the adjusted sed command line in the "found=" line # in mk/subst.mk does not create shell syntax errors. # This is the sed command after the "found=" line in mk/subst.mk. # It tests whether any of the patterns is found. # It only outputs the actually found lines (-n) by appending a "p" # to the usual "s,from,to," commands. mock_sed=$( newline=' ' args='-n' # In this "usual" sed command, the "p" is added. args="$args -e s,identity,identity,p" # This is considered an "unusual" sed command because of # the leading 1, therefore no "p" is added. # # Ideally this should be considered a "usual" sed command, # even though it only applies to some of the lines. # To do this, mk/scripts/subst-identity.awk has to parse # sed addresses, in addition to substitutions. args="$args -e 1s,first line,first line," # In the Makefile, the additional quotes at the beginning # make this an "unusual" sed command, and the :C modifier # in subst.mk doesn't see that after unquoting the word, # the sed command is quite usual. This is an edge case # that doesn't occur in practice. args="$args -e s,unusual,unusual,g" # No "p" is added since this is not a "usual" substitution. # If it had been found, the file would have changed anyway, # and this sed command line would not be executed. args="$args -e /not found/d" # Same here. Just make sure that the generated sed command # line does not lead to a syntax error in the shell. args="$args -e /not found/a\\${newline}added${newline}" args="$args file" mock_cmd 'mock-sed' \ --when-args "$args" --then-output 'identity' ) create_file 'testcase.mk' <<-EOF SUBST_CLASSES+= id SUBST_FILES.id= file SUBST_SED.id= -e 's,identity,identity,' SUBST_SED.id+= -e '1s,first line,first line,' SUBST_SED.id+= -e '''''s,unusual,unusual,g' SUBST_SED.id+= -e '/not found/d' SUBST_SED.id+= -e '/not found/a\\\${.newline}added\${.newline}' # Use the standard sed for the main part. SUBST_FILTER_CMD.id= LC_ALL=C sed \${SUBST_SED.id} .include "prepare-subst.mk" # Use the mocked sed for the "found=" part. SED= $mock_sed .include "mk/subst.mk" # ignore PKG_FAIL_REASON (SUBST_SED + SUBST_FILTER_CMD) EOF create_file 'file' <<-EOF identity EOF run_bmake 'testcase.mk' 'subst-id' 1> "$tmpdir/output" 2>&1 \ && exitcode=0 || exitcode=$? assert_that "$tmpdir/output" --file-is-lines \ '=> Substituting "id" in file' assert_that 'file' --file-is-lines \ 'identity' test_case_end fi