Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

I'm trying to run a Python snippet inside a Make target, but I'm having trouble trying to figure out how these things work in Make.

Here is my attempt so far:

define BROWSER_PYSCRIPT
import os, webbrowser, sys
try:
    from urllib import pathname2url
except:
    from urllib.request import pathname2url

webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1])))
endef
BROWSER := $(shell python -c '$(BROWSER_PYSCRIPT)')

I wanted to use $(BROWSER) in a target like:

docs:
    #.. compile docs
    $(BROWSER) docs/index.html

Is this really a bad idea?

share|improve this question

2 Answers 2

up vote 1 down vote accepted

Related: http://stackoverflow.com/q/649246/4937930

You cannot recall a multi-line variable as is in a single recipe, it rather gets expanded to multiple recipes and causes syntax error.

A possible workaround would be:

export BROWSER_PYSCRIPT
BROWSER := python -c "$$BROWSER_PYSCRIPT"

docs:
        #.. compile docs
        $(BROWSER) docs/index.html
share|improve this answer
    
yay, it works, thank you very much! :) so, I assume that export command turns it into a variable in the environment, and then we use shell expansion to get it later? could you explain at which point "$$BROWSER_PYSCRIPT" is evaluated? –  elias Aug 13 at 12:21
    
$$ is an escaped $ so it becomes $BROWSER_PYSCRIPT in $(BROWSER). Make leaves it untouched, just passes a recipe string python -c "$BROWSER_PYSCRIPT" docs/index.html as-is to $(SHELL) (usually /bin/sh), along with BROWSER_SCRIPT exported in the environment variables. –  yaegashi Aug 14 at 9:41
    
cool, neat trick! :) –  elias Aug 14 at 13:18

Yaegashi's approach will work, but it won't be easy to give it on-the-fly values. The 'export' command is evaluated while the Makefile is being parsed, and sets a shell environment variable to the recipe. Then the environment variable is evaluated during execution of the 'docs' recipe.

If your snippet needs to have any target-dependent variables filled in, I'd recommend an approach like this:

Quick Approach

If you just need to run a couple of one-liners, this pattern will work pretty well.

run_script = python -c \
"import time ;\
print 'Hello world!' ;\
print '%d + %d = %d' %($1,$2,$1+$2) ;\
print 'Running target \'%s\' at time %s' %('$3', time.ctime())"

test:
    $(call run_script,4,3,$@)

Fancy Approach

If you want to use weird characters and functions, for-loops, or other multi-line constructs, here's a fancy pattern that will work beautifully.

#--------------------------- Python Script Runner ----------------------------#

define \n


endef

escape_shellstring = \
$(subst `,\`,\
$(subst ",\",\
$(subst $$,\$$,\
$(subst \,\\,\
$1))))

escape_printf = \
$(subst \,\\,\
$(subst %,%%,\
$1))

create_string = \
$(subst $(\n),\n,\
$(call escape_shellstring,\
$(call escape_printf,\
$1)))

python_script = printf "$(call create_string,$($(1)))" | python

#------------------------------- User Scripts --------------------------------#

define my_script

def good_times():
    print "good times!"

import time
print 'Hello world!'
print '%d + %d = %d' %($2,$3,$2+$3)
print 'Runni`ng $$BEEF \ttarget \t\n\n"%s" at time %s' %('$4', time.ctime())
good_times()

endef

#--------------------------------- Recipes -----------------------------------#

test:
    $(call python_script,my_script,1,2,$@)
share|improve this answer
    
interesting, thanks! I did try to use parameterized macros, but found them a bit cumbersome. in my case, I will only use under targets, so the export trick is cleaner to me. thank you anyway! :) –  elias Aug 14 at 13:20

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.