#!/bin/sh . ./testing.sh unset MAKEFLAGS rm -rf make.tempdir # testing "test name" "command" "expected result" "file input" "stdin" testing "make basic makefile" \ "make -f -" "target\n" "" ' target: @echo target ' testing "make .DEFAULT rule for prerequisite" \ "make -f - 2>/dev/null" "source\n" "" ' .DEFAULT: @echo $@ target: source ' mkdir make.tempdir && cd make.tempdir || exit 1 touch target.xyz testing "make empty command overrides inference rule" \ "make -f - target 2>/dev/null" "" "" ' .SUFFIXES: .xyz .xyz: @echo xyz target: ; ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Macros should be expanded before suffix substitution. The suffixes # can be obtained by macro expansion. testing "make macro expansion and suffix substitution" \ "make -f -" "src1.o src2.o\n" "" ' DOTC = .c DOTO = .o SRC1 = src1.c SRCS = $(SRC1) src2.c target: @echo $(SRCS:$(DOTC)=$(DOTO)) ' # Indeed, everything after the can be obtained by macro # macro expansion. testing "make macro expansion and suffix substitution 2" \ "make -f -" "src1.o src2.o\n" "" ' DOTS = .c=.o SRC1 = src1.c SRCS = $(SRC1) src2.c target: @echo $(SRCS:$(DOTS)) ' # It should be possible for an inference rule to determine that a # prerequisite can be created using an explicit rule. mkdir make.tempdir && cd make.tempdir || exit 1 testing "make inference rule with explicit rule for prerequisite" \ "make -f -" "touch x.p\ncat x.p >x.q\n" "" ' .SUFFIXES: .p .q x.q: x.p: touch $@ .p.q: cat $< >$@ ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Austin Group defect report 875 clarifies certain aspects of the # behaviour of inference rules. Study of this resulted in a number # of changes to pdpmake, though this test passed anyway. mkdir make.tempdir && cd make.tempdir || exit 1 touch test.j test.k testing "make proper handling of inference rules 1" \ "make -f -" \ ".j.l\n" "" ' .SUFFIXES: .j .k .l .j.l: @echo .j.l .k.l: @echo .k.l test.l: test.k test.j: test.k: ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Check that single-suffix inference rules work. mkdir make.tempdir && cd make.tempdir || exit 1 touch test.sh testing "make single-suffix inference rule works" \ "make -f - -s; echo $?" \ "0\n" "" ' test: ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # There was a bug where the failure of a build command didn't result # in make returning a non-zero exit status. testing "make return error if command fails" \ "make -f - >/dev/null 2>&1; test \$? -gt 0 && echo OK" "OK\n" "" ' target: @exit 42 ' # An equal sign in a command on a target rule was detected as a # macro assignment. testing "make equal sign in inline command" \ "make -f -" "a = a\n" "" ' a = a target:;@echo a = $(a) ' # Ensure an inline command on a target rule can be detected even if # the semicolon is obfuscated. testing "make equal sign in obfuscated inline command" \ "make -f -" "a = a\n" "" ' a = a semi = ; target:$(semi)@echo a = $(a) ' # When a build command fails and the '-k' option has been provided # (continue execution on error) no further commands should be executed # for the current target. testing "make failure of build command with -k" \ "make -k -f - 2>/dev/null" "OK\n" "" ' all: bar baz bar: @echo OK @false @echo Not reached baz: @: ' # Build commands with a '+' prefix are executed even with the -q option. testing "make execute build command with + prefix and -q" \ "make -q -f - 2>/dev/null" "OK\n" "" ' all: bar bar: @+echo OK ' # The -t option touches files that are out-of-date unless the target # has no commands or they're already up-to-date. mkdir make.tempdir && cd make.tempdir || exit 1 touch baz testing "make check -t option" \ "make -t -f - 2>/dev/null" "touch bar\n" "" ' all: foo bar baz foo: bar: @echo bar baz: @echo baz ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Build commands with a '+' prefix are executed even with the -t option. mkdir make.tempdir && cd make.tempdir || exit 1 testing "make execute build command with + prefix and -t" \ "make -t -f - 2>/dev/null" "OK\n" "" ' all: bar bar: @+echo OK ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # A macro created using ::= remembers it's of type immediate-expansion. # Immediate expansion also occurs when += is used to append to such a macro. testing "make appending to immediate-expansion macro" \ "make -f -" \ "hello 1 2 3\nhello 4 4\n" "" ' world = 1 hello ::= hello $(world) world = 2 hello += $(world) world = 3 hello += $(world) world = 4 world = 1 reset ::= hello $(world) world = 2 # No longer immediate-expansion reset = hello $(world) world = 3 reset += $(world) world = 4 target: @echo $(hello) @echo $(reset) ' # Since GNU make and bmake interpret := macro assignments differently, # POSIX has ::= for the GNU variant and :::= for BSD. testing "make different styles of := macro assignment" \ "make -f -" \ '65 a a $A\n' "" ' A = a GNU ::= $A BSD1 :::= $A BSD2 :::= $$A A = 65 target: @echo '\''$(A) $(GNU) $(BSD1) $(BSD2)'\'' ' # Similar to the above but for macro assignments on the command line. # POSIX has ::= for the GNU variant and :::= for BSD. testing "make := macro assignment on command line" \ "make -f - A=a 'GNU::=\$A' 'BSD1:::=\$A' 'BSD2:::=\$\$A' A=65" \ '65 a a $A\n' "" ' target: @echo '\''$(A) $(GNU) $(BSD1) $(BSD2)'\'' ' # basic pattern macro expansion testing "make basic pattern macro expansion" \ "make -f -" \ "obj/util.o obj/main.o\n" "" ' SRC = src/util.c src/main.c OBJ = $(SRC:src/%.c=obj/%.o) target: @echo $(OBJ) ' # pattern macro expansion; match any value testing "make pattern macro expansion; match any value" \ "make -f -" \ "any_value.o\n" "" ' SRC = any_value OBJ = $(SRC:%=%.o) target: @echo $(OBJ) ' # pattern macro expansion with empty rvalue testing "make pattern macro expansion with empty rvalue" \ "make -f -" \ "\n" "" ' SRC = util.c main.c OBJ = $(SRC:%.c=) target: @echo $(OBJ) ' # pattern macro expansion with multiple in rvalue # POSIX requires the first to be expanded, others # may or may not be expanded. Permit either case. testing "make pattern macro expansion with multiple in rvalue" \ "make -f - | sed 's/mainmainmain/main%%/'" \ "main%%\n" "" ' SRC = main.c OBJ = $(SRC:%.c=%%%) target: @echo $(OBJ) ' # pattern macro expansion; zero match testing "make pattern macro expansion; zero match" \ "make -f -" \ "nsnp\n" "" ' WORD = osop REPL = $(WORD:os%op=ns%np) target: @echo $(REPL) ' # Check that MAKE will contain argv[0], e.g make in this case testing "make basic MAKE macro expansion" \ "make -f -" \ "make\n" "" ' target: @echo $(MAKE) ' # Check that MAKE defined as environment variable will overwrite default MAKE testing "make MAKE macro expansion; overwrite with env macro" \ "MAKE=hello make -f -" \ "hello\n" "" ' target: @echo $(MAKE) ' # Check that MAKE defined on the command-line will overwrite MAKE defined in # Makefile testing "make MAKE macro expansion; overwrite with command-line macro" \ "make -f - MAKE=hello" \ "hello\n" "" ' MAKE = test target: @echo $(MAKE) ' # POSIX draft states that if make was invoked using relative path, MAKE must # contain absolute path, not just argv[0] testing "make MAKE macro expansion; turn relative path into absolute" \ "../runtest-tempdir-links/make -f -" \ "ok\n" "" ' target: @test -e "$(MAKE)" && test "$(MAKE)" = "$$(env which make)" && echo ok ' # $? contains prerequisites newer than target, file2 in this case # $^ has all prerequisites, file1 and file2 touch -t 202206171200 file1 touch -t 202206171201 target touch -t 202206171202 file2 testing "make compare \$? and \$^ internal macros" \ "make -f -" \ "file2\nfile1 file2\n" "" ' target: file1 file2 @echo $? @echo $^ ' rm -f target file1 file2 # Phony targets are executed (once) even if a matching file exists. # A .PHONY target with no prerequisites is ignored. touch -t 202206171201 target testing "make phony target" \ "make -f -" \ "phony\n" "" ' .PHONY: target .PHONY: target: @echo phony ' rm -f target # Phony targets aren't touched with -t testing "make phony target not touched" \ "make -t -f - >/dev/null && test -f target && echo target" \ "" "" ' .PHONY: target target: @: ' rm -f target # Include files are created or brought up-to-date mkdir make.tempdir && cd make.tempdir || exit 1 testing "make create include file" \ "make -f -" \ "made\n" "" ' target: @echo $(VAR) mk: @echo "VAR = made" >mk include mk ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Include files are created or brought up-to-date even when the -n # option is given. mkdir make.tempdir && cd make.tempdir || exit 1 testing "make create include file even with -n" \ "make -n -f -" \ "echo made\n" "" ' target: @echo $(VAR) mk: @echo "VAR = made" >mk include mk ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Failure to create an include file isn't an error. (Provided the # include line is ignoring non-existent files.) testing "make failure to create include file is OK" \ "make -f -" \ "OK\n" "" ' target: @echo OK mk: @: -include mk ' # $^ skips duplicate prerequisites, $+ doesn't mkdir make.tempdir && cd make.tempdir || exit 1 touch file1 file2 file3 testing "make skip duplicate entries in \$^ but not \$+" \ "make -f -" \ "file1 file2 file3\nfile1 file2 file2 file3 file3\n" "" ' target: file1 file2 file2 file3 file3 @echo $^ @echo $+ ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Assign the output of a shell command to a macro. testing "make shell assignment" \ "make -f -" \ "1 2 3 4\n" "" ' hello != echo 1; echo 2; echo 3; echo; echo target: @echo "$(hello) 4" ' # Nested macro expansion is allowed. This should be compatible # with other implementations. testing "make nested macro expansion" \ "make -f -" "0 bc\n1 d\n2\n3\n4 bcd\n5 bcd\n" "" ' a = b b = c c = d $(a:.q=.v)$(b:.z=.v) = bc bcd = bcd target: @echo 0 $(bc) @echo 1 $($($(a))) @echo 2 $($(a) $(b) $(c)) @echo 3 $($a $b $c) @echo 4 $($(a)$(b)$(c)) @echo 5 $($a$b$c) ' # .WAIT is allowed as a prerequisite. Since parallel builds aren't # implemented it doesn't have any effect. mkdir make.tempdir && cd make.tempdir || exit 1 touch file1 file2 testing "make .WAIT is allowed as a prerequisite" \ "make -f -" \ "file1 file2\n" "" ' target: file1 .WAIT file2 @echo $? ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Escaped newlines inside macro expansions in commands get different # treatment than those outside. In POSIX 2017 the output is 'a b ab'. testing "make replace escaped NL in macro in command with space" \ "make -f -" \ "a b a b\n" "" ' M=word N=${M:word=a\\ b} target: @echo ${N} ${M:word=a\\ b} ' # The CURDIR macro is supported in POSIX 2024. testing "make CURDIR macro" \ "make -f -" \ "OK\n" "" ' target: @test "$(CURDIR)" = "$$(pwd -P)" && echo OK ' # The CURDIR environment variable doesn't affect the macro export CURDIR=/you/are/here testing "make CURDIR macro not affected by environment" \ "make -f -" \ "OK\n" "" ' target: @test "$(CURDIR)" != "/you/are/here" && echo OK ' # The -e option makes the CURDIR macro match the environment testing "make with -e CURDIR macro is affected by the environment" \ "make -e -f -" \ "/you/are/here\n" "" ' target: @echo $(CURDIR) ' unset CURDIR # The fix for an equal sign in an inline command on a target rule broke # a complex chain of macro assignments generated by autotools. testing "make complex chain of macro assignments" \ "make -f -" "flag 1\n" "" ' FLAG_ = $(FLAG_$(VALUE)) FLAG_0 = flag 0 FLAG_1 = flag 1 MYFLAG = $(FLAG_$(VALUE)) VALUE = 1 target: @echo $(MYFLAG) ' # POSIX 2024 permits additional characters in macro and target names testing "make allow - and / in target names, - in macro names" \ "make -f -" \ "/hello\nhel-lo\nmac-ro\n" "" ' target: ./hello hel-lo @echo $(mac-ro) ./hello: @echo /hello hel-lo: @echo hel-lo mac-ro = mac-ro ' testing "make double-colon rule" \ "make -f -" "target1\ntarget2\n" "" ' target:: @echo target1 target:: @echo target2 ' # There was a bug whereby the modification time of a file created by # double-colon rules wasn't correctly updated. This test checks that # the bug is now fixed. mkdir make.tempdir && cd make.tempdir || exit 1 touch -t 202206171200 file1 touch -t 202206171201 intermediate touch -t 202206171202 target touch -t 202206171203 file2 testing "make target depends on prerequisite updated by double-colon rule" \ "make -f -" \ "file2\n" "" ' target: intermediate @cat intermediate intermediate:: file1 @echo file1 >>intermediate intermediate:: file2 @echo file2 >>intermediate ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Use chained inference rules to determine prerequisites. mkdir make.tempdir && cd make.tempdir || exit 1 touch target.p testing "make chained inference rules" \ "make -s -f - target.s" \ "target.q\ntarget.r\ntarget.s\n" "" ' .SUFFIXES: .p .q .r .s .p.q: @cp $< $*.q @echo $*.q .q.r: @cp $< $*.r @echo $*.r .r.s: @cp $< $*.s @echo $*.s ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Suffixes with multiple periods are supported mkdir make.tempdir && cd make.tempdir || exit 1 touch x.c.c testing "make support multi-period suffixes" \ "make -f -" "cat x.c.c >x.o.o\nx\n" "" ' .SUFFIXES: .c.c .o.o x.o.o: .c.c.o.o: cat $< >$@ @echo $* ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Suffixes with no periods are supported mkdir make.tempdir && cd make.tempdir || exit 1 touch filex testing "make support suffixes without any periods" \ "make -f -" "cp filex fileh\nfile\ncp filex filez\nfile\n" "" ' .SUFFIXES: x h z all: fileh filez fileh: filez: filex cp filex filez @echo $* xh: cp $< $@ @echo $* ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # make supports *, ? and [] wildcards in targets and prerequisites mkdir make.tempdir && cd make.tempdir || exit 1 touch -t 202206171201 t1a t2aa t3b touch s1a s2aa s3b testing "make expand wildcards in filenames" \ "make -f - t1a t2aa t3b" \ "t1a s1a s2aa s3b\nt2aa s1a s2aa s3b\nt3b s1a s2aa s3b\n" "" ' t1? t2* t3[abc]: s1? s2* s3[abc] @echo $@ $? ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # A '#' character in a macro expansion doesn't start a comment testing "make hash in macro expansion isn't a comment" \ "make -f -" \ ": hash # hash\n" "" ' HASH = hash hash = $(HASH:hash=#) target: : hash $(hash) hash ' # A '#' character can be escaped with a backslash testing "make backslash-escaped hash isn't a comment" \ "make -f -" \ ": hash # hash\n" "" ' hash = \\# target: : hash $(hash) hash ' # A '#' character in a command line doesn't start a comment testing "make hash in command line isn't a comment" \ "make -f -" \ ": hash # hash\n" "" ' target: : hash # hash ' # Austin Group defect report 875 (mentioned above) actually used # suffixes '.a .b .c'. This doesn't matter in POSIX mode but it # caused a failure (now fixed) when chained inference rules were # allowed. The '.a.c' and the built-in '.c.a' inference rules # resulted in a loop. mkdir make.tempdir && cd make.tempdir || exit 1 touch test.a test.b testing "make proper handling of inference rules 2" \ "make -f -" \ ".a.c\n" "" ' .SUFFIXES: .a .b .c .a.c: @echo .a.c .b.c: @echo .b.c test.c: test.b test.a: test.b: ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Don't use the shell -e option when running commands. testing "make no shell -e option when running commands" \ "make -f -" "OK\n" "" ' target: @false; echo OK ' # Macros and targets may be mixed on the command line testing "make allow mixed macros and targets" \ "make -f - FOO=foo foo BAR=bar bar" "foo\nbar\nfoo\nbar\n" "" ' foo: @echo $(FOO) @echo $(BAR) bar: @echo $(FOO) @echo $(BAR) ' # $* and $< are supported for target rules mkdir make.tempdir && cd make.tempdir || exit 1 touch src.c src.h testing 'make support $* and $< for target rules' \ "make -f -" "src.c src.h\nsrc.o\nsrc\nsrc.c\n" "" ' src.o: src.c src.h @echo "$?" @echo "$@" @echo "$*" @echo "$<" ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # ifeq/ifneq conditionals are supported testing 'make support ifeq and ifneq conditionals' \ "make -f -" "A OK\nB OK\n" "" ' A = a B = b target: ifeq ($(A),a) @echo A OK else @echo A not OK endif ifneq "a" "$B" @echo B OK endif ' # An empty original suffix indicates that every word should have # the new suffix added. If neither suffix is provided the words # remain unchanged. testing "make macro expansion and suffix substitution 3" \ "make -f -" "src1.c src2.c\nsrc1 src2\n" "" ' SRCS = src1 src2 target: @echo $(SRCS:=.c) @echo $(SRCS:=) ' # Skip duplicate entries in $? and $^ mkdir make.tempdir && cd make.tempdir || exit 1 touch -t 202206171200 file1 file3 touch -t 202206171201 target touch -t 202206171202 file2 testing "make skip duplicate entries in \$? and \$^" \ "make -f -" \ "file2\nfile1 file2 file3\n" "" ' target: file1 file2 file2 file3 file3 @echo $? @echo $^ ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Skip duplicate entries in $? and $^, with each double-colon rule # handled separately mkdir make.tempdir && cd make.tempdir || exit 1 touch -t 202206171200 file1 file3 touch -t 202206171201 target touch -t 202206171202 file2 testing "make skip duplicate entries: double-colon rules" \ "make -f -" \ "file2\nfile1 file3 file2\nfile2\nfile2 file3\n" "" ' target:: file1 file3 file1 file2 file3 @echo $? @echo $^ target:: file2 file3 file3 @echo $? @echo $^ ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # Skip duplicate entries in $? and $^, with each double-colon rule # handled separately. No prerequisites out-of-date in the first. mkdir make.tempdir && cd make.tempdir || exit 1 touch -t 202206171200 file1 file3 touch -t 202206171201 target touch -t 202206171202 file2 testing "make skip duplicate entries: double-colon rules, only second invoked" \ "make -f -" \ "file2\nfile2 file3\n" "" ' target:: file1 file3 file1 file3 @echo $? @echo $^ target:: file2 file3 file3 @echo $? @echo $^ ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # .DEFAULT rules with no commands or some prerequisites are ignored. # .DEFAULT rules with commands can be redefined. testing "make: .DEFAULT rule" \ "make -f - default" "default2\n" "" ' .DEFAULT: ignored .DEFAULT: @echo default1 .DEFAULT: @echo default2 target: ' testing "make: double-colon rule" \ "make -f -" "target1\ntarget2\n" "" ' target:: @echo target1 target:: @echo target2 ' # Double-colon rules didn't work properly if their target was phony: # - they didn't ignore the presence of a file matching the target name; # - they were also invoked as if they were a single-colon rule. mkdir make.tempdir && cd make.tempdir || exit 1 touch -t 202206171200 file1 touch -t 202206171201 target testing "make phony target of double-colon rule" \ "make -f - 2>&1" \ "unconditional\nconditional\n" "" ' .PHONY: target target:: @echo unconditional target:: file1 @echo conditional file1: @touch file1 ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null # GNU make and BSD make don't allow the use of inference rules # for phony targets. In POSIX mode the output is "phony.xyz\n". mkdir make.tempdir && cd make.tempdir || exit 1 touch phony.xyz testing "make don't use inference rule for phony target" \ "make -f -" "make: nothing to be done for phony\n" "" ' .PHONY: phony .SUFFIXES: .xyz .xyz: @echo $< phony: ' cd .. || exit 1; rm -rf make.tempdir 2>/dev/null exit $FAILCOUNT