2

I have a complex logic for detecting path for Makefile variable. I gave up on doing this in make language, so I coded it in Python, and want to embed the code into Makefile to set variable. However, this doesn't work:

define DETECT_SDK
import os
locations = [
  "../google_appengine",
  "/usr/local/google_appengine",
  "../.locally/google_appengine",
]
for path in locations:
  if os.path.exists(path):
    print(path)
    break
else:
  print(".")
endef

SDK_PATH ?= $(shell python -c $(DETECT_SDK))

default:
        python -c 'import sys; print(sys.argv)' $(SDK_PATH)

UPDATE: Updated multiline definition from Is it possible to create a multi-line string variable in a Makefile Previously it was failing with Makefile:2: *** missing separator. Stop.. Now it fails with another error:

/bin/sh: 1: Syntax error: "(" unexpected
python -c 'import sys; print(sys.argv)' 
['-c']
Community
  • 1
  • 1
anatoly techtonik
  • 19,847
  • 9
  • 124
  • 140

2 Answers2

2

You have to quote the string you pass to Python:

SDK_PATH ?= $(shell python -c '$(DETECT_SDK)')

Otherwise the shell will be confused trying to parse the Python script.

EDIT:

I don't understand your Python script though. Either your indentation is wrong, so the else is supposed to be attached to the if (grrr...) or else you're missing a break statement... or possibly the else is just useless. As it's written, it will generate a newline-separated list of paths that exist, plus ".". If you describe what you're really trying to do, rather than just say you gave up, we can help.

For example, if what you want to do is print the first existing path in that list or "." if none exist (that is, your Python loop is missing a break after the print), then you can easily do this in GNU make:

SDK_PATH_LIST = ../google/appengine /usr/local/google_appengine ../.locally/google_appengine
SDK_PATH ?= $(firstword $(wildcard $(SDK_PATH_LIST:%=%/.)) .)
MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • for .. else is a [feature of Python](http://docs.python.org/2/reference/compound_stmts.html#for), but you were right - my code was missing `break`. – anatoly techtonik Jun 16 '13 at 20:32
  • I am not sure that this magic string does `$(firstword $(wildcard $(SDK_PATH_LIST:%=%/.)) .)`, but I know that my Python code works. – anatoly techtonik Jun 16 '13 at 20:39
  • 1
    I know for-else is a feature of Python. If the indentation was wrong so the else was attached to the if, then it would print "." for every path that didn't exist. Since you didn't say what you expected the code to do, that was a possible solution. The make code says: take every path in the SDK_PATH_LIST and append `/.` to it, so it's treated as a directory, then run `wildcard` on it which will return the paths that exist and not the ones that don't, then take the first word of the returned list. And we add `.` at the end so that if none exist, that will be the first word. – MadScientist Jun 16 '13 at 23:23
  • Nice hack. I find the Python code more readable anyway, so I'd prefer pythonic approach. – anatoly techtonik Jun 17 '13 at 15:25
  • That's fine. Add the quotes as I suggested and it should work. If not you might have to add semicolons to separate statements: Python's reliance on whitespace as statement separators and block definitions is (IMO) a serious deficiency when it comes to writing inline scripts like this, but whatever gets your job done... – MadScientist Jun 17 '13 at 15:34
1

Due to some make bug, which tried to execute multiline strings, the following worked for me:

define NEWLINE


endef

define DETECT_SDK
import os
locations = [
  "../google_appengine",
  "/usr/local/google_appengine",
  "../.locally/google_appengine",
]
for path in locations:
  if os.path.exists(path):
    print(path)
    break
endef

SDK_PATH ?= $(shell echo '$(subst $(NEWLINE),@NEWLINE@,${DETECT_SDK})' | sed 's/@NEWLINE@/\n/g' | python -)

default:
        @echo 'SDK Path Detected: $(SDK_PATH)'
anatoly techtonik
  • 19,847
  • 9
  • 124
  • 140
  • How to pipe arguments to python stdin? i.e., `echo arguments | python -c 'import sys; print(sys.stdin.read())'` I asked a new question for this here: https://stackoverflow.com/questions/55681576/how-to-send-input-on-stdin-to-a-python-script-defined-inside-a-makefile – Evandro Coan Apr 15 '19 at 01:28