4

I have the following Makefile:

build: clean
    ${GOPATH}/bin/dep ensure
    env GOOS=linux go build -o ./bin/status ./lib/status/main.go
    elm-app build

init:
    ${GOPATH}/bin/dep init -v

test:
    env GOOS=linux go test -v ./lib/status

strip:
    strip ./bin/status

clean:
    if [ -f ./bin/status ]; then
        rm -f ./bin/status
    fi

but I get

if [ -f ./bin/status ]; then
/bin/sh: 1: Syntax error: end of file unexpected
Makefile:16: recipe for target 'clean' failed
make: *** [clean] Error 2

what am i missing?

any advice is much appreciated

user2226755
  • 12,494
  • 5
  • 50
  • 73
khinester
  • 3,398
  • 9
  • 45
  • 88
  • 2
    easier solution: just `rm -f ./bin/status` option `-f` ignore nonexistent files, check man for _rm_ – Blueman Nov 23 '18 at 21:00
  • Does this answer your question? [Test whether a directory exists inside a makefile](https://stackoverflow.com/questions/20763629/test-whether-a-directory-exists-inside-a-makefile) – Melebius May 03 '23 at 10:57

1 Answers1

11

Each line of a makefile is run in a separate shell. This means your rule here:

clean:
        if [ -f ./bin/status ]; then
            rm -f ./bin/status
        fi

actually runs the following commands:

/bin/sh -c "if [ -f ./bin/status ]; then"
/bin/sh -c "rm -f ./bin/status"
/bin/sh -c "fi"

You can see why you get this message. To ensure that all lines are send into a single shell you need to use backslashes to continue the lines like this:

clean:
        if [ -f ./bin/status ]; then \
            rm -f ./bin/status; \
        fi

Note this means you also need a semicolon after the rm command so separate it from the ending fi.

Now you get a shell invocation like this:

/bin/sh -c "if [ -f ./bin/status ]; then \
        rm -f ./bin/status; \
    fi"
user2226755
  • 12,494
  • 5
  • 50
  • 73
MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • This explained so much. Thanks a lot buddy! I solved it by adding it as a one-liner. Just wanted to add that this is possible too. – PatrikJ Jul 28 '22 at 14:27
  • For a one-liner, this way of writing a condition might be useful: `[ -f ./bin/status ] && rm -f ./bin/status` – Melebius May 03 '23 at 10:55
  • IMO there's no real point in writing this. If you use `rm -f` it won't fail if the file doesn't exist, so there's no practical difference between `[ -f ./bin/status ] && rm -f ./bin/status` and just writing `rm -f ./bin/status` by itself. This answer was more about why the original didn't work, than the best way to write it :) – MadScientist May 03 '23 at 12:46
  • Actually there is one BIG difference. In fact your change is not right. The problem with your formulation is that `[ -f ./bin/status ] && rm -f ./bin/status` will exit with an error code if `./bin/status` does not exist. That error code will lead make to believe the recipe failed, and it will fail the make. In general it's usually wrong to use `&&` shortcuts in make recipes for this reason. You can use `||` though: rewrite as `[ ! -f ./bin/status ] || rm -f ./bin/status` and it will work. But again, it seems useless when just `rm -f ./bin/status` does the same thing, with less effort. – MadScientist May 03 '23 at 12:49