How to Send Input on Stdin to a Python Script Defined Inside a Makefile

How to send input on stdin to a python script defined inside a Makefile?

For me the easiest way to circumvent all the command line quoting problems is to write the code to a file with GNUmake's $(file ) function. You can even use # as Python comment inside of defined variables:

VERSION := $(shell bash --version)

# With this, you can use any form of quotes inside your python code
define PYTHON_VERSION_CODE :=
# -*- coding: iso-8859-15 -*-
import re, sys;
program_version = "${VERSION}"
match = re.search("Copyright[^\d]+(\d+)", program_version);

if match:
if int( match.group(1) ) >= 2018:
sys.stdout.write("1")
else:
sys.stdout.write( "match:" + match.group(1) )
else:
sys.stdout.write("0")
endef

$(file > test.py,$(PYTHON_VERSION_CODE))

PYTHON_SCRIPT_RESULTS := $(shell python test.py)

.PHONY: all
all:
@echo $(PYTHON_SCRIPT_RESULTS)

Embed Python in Makefile to set make variables

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:%=%/.)) .)

Makefile as an executable script with shebang?

#!/usr/bin/make -f

main:
@echo Hello World!

Is normally all you need in a standard make file. The filename is implicitly passed as the last argument. /dev/stdin here is (usually) the tty. You can do the whole env thing if there's a reason to, but often there's no need.

ajw@rapunzel:~/code/videocc/tools > vi Makefile                       
ajw@rapunzel:~/code/videocc/tools > chmod a+x Makefile
ajw@rapunzel:~/code/videocc/tools > ./Makefile
Hello World!

how to have command as input for a shell launched inside a python subprocess

Using the subprocess module, it could be achieved this way. You can use the stdin stream to write your command to execute once the actual environment has been set.

start_workspace.perl

print "Perl: Setting some env variables\n";
$ENV{"SOME_VAR"} = "some value";
print "Perl: Starting bash\n";
exec('bash');

In python:

import subprocess 
p = subprocess.Popen( "perl start_workspace.perl", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
p.stdin.write('echo "Python: $SOME_VAR"\n')
p.stdin.write("make\n")
(stdoutdata, stderrdata) = p.communicate()
print stdoutdata
print stderrdata

Output is

Perl: Setting some env variables
Perl: Starting bash
Python: some value

make: *** No targets specified and no makefile found. Stop.

Piping stdout and stderr inside a Makefile rule

It doesn't explain why the straightforward approaches don't work, but it does the trick:

[Makefile]
test:
python test.py >test.out 2>&1; pyrg <test.out

How to get the exit status and the output of a $(shell command) before make 4.2?

I just can't understand why you're trying to make use of $(shell ...) at all. This is so overly complex, and so meaningless. The only effect of $(shell ...) is that it expands before any recipe is run. But you never try to make use of that. On the contrary, it looks only natural to move the script directly inside the recipe.

For example, consider this:

unused = """
ifeq (true,false)
"""

# some python stuff
print("Hello world")
exit(1)

unused = """
endif

# here comes make stuff
.ONESHELL:
.PHONY: all
all:
@/usr/bin/python $(lastword $(MAKEFILE_LIST))
ec=$$?
echo "script exitcode is $$ec"
exit $$ec

#"""

Maybe even better solution is simply to make python a custom SHELL for a single rule. Although, in this case it could be tricky to mix python and shell scripts in the same rule (but I don't think it's really essential).

How can pass the value of a variable to the standard input of a command?

Simple, but error-prone: using echo

Something as simple as this will do the trick:

echo "$blah" | my_cmd

Do note that this may not work correctly if $blah contains -n, -e, -E etc; or if it contains backslashes (bash's copy of echo preserves literal backslashes in absence of -e by default, but will treat them as escape sequences and replace them with corresponding characters even without -e if optional XSI extensions are enabled).

More sophisticated approach: using printf

printf '%s\n' "$blah" | my_cmd

This does not have the disadvantages listed above: all possible C strings (strings not containing NULs) are printed unchanged.

How to pass argument to Makefile from command line?

You probably shouldn't do this; you're breaking the basic pattern of how Make works. But here it is:

action:
@echo action $(filter-out $@,$(MAKECMDGOALS))

%: # thanks to chakrit
@: # thanks to William Pursell

EDIT:
To explain the first command,

$(MAKECMDGOALS) is the list of "targets" spelled out on the command line, e.g. "action value1 value2".

$@ is an automatic variable for the name of the target of the rule, in this case "action".

filter-out is a function that removes some elements from a list. So $(filter-out bar, foo bar baz) returns foo baz (it can be more subtle, but we don't need subtlety here).

Put these together and $(filter-out $@,$(MAKECMDGOALS)) returns the list of targets specified on the command line other than "action", which might be "value1 value2".



Related Topics



Leave a reply



Submit