如何在 make 4.2 之前获取 $(shell 命令) 的退出状态和输出?
How to get the exit status and the output of a $(shell command) before make 4.2?
在 make version 4.2 {2016} 上添加了变量 .SHELLSTATUS
。但我最 linux 正在使用 make 版本 4.1
。我怎样才能在旧的 make 版本上获得 make $(shell ...) 命令的退出状态和输出结果?
使用 make 4.2,我可以这样做:
newmake:
printf '%s\n' "$(shell command && \
( printf 'Successfully created the zip file!\n'; exit 0 ) || \
( printf 'Error: Could not create the zip file!\n'; exit 1 ) )"
printf 'Exit status: '%s'\n' "${.SHELLSTATUS}"
哪个 运行 正确:
printf '%s\n' "Successfully created the zip file!"
Successfully created the zip file!
printf 'Exit status: '%s'\n' "0"
Exit status: 0
但是,如果我使用的是 make 4.1 或更早版本,则没有 .SHELLSTATUS
变量。然后,我尝试定义自己的 .SHELLSTATUS
变量,如下所示:
oldmake:
printf '%s\n' "$(shell command && \
( printf 'Successfully created the zip file!\n'; $(eval exit_status := 0) echo ${exit_status} > /dev/null ) || \
( printf 'Error: Could not create the zip file!\n'; $(eval exit_status := 1) echo ${exit_status} > /dev/null ) )"
printf 'Exit status: '%s'\n' "${exit_status}"
但是,它 运行 不正确:
printf '%s\n' "Successfully created the zip file!"
Successfully created the zip file!
printf 'Exit status: '%s'\n' "1"
Exit status: 1
如您所见,打印了正确的消息,但退出状态应该是0
,而不是1
。
通知
消息 Successfully created the zip file!
和 Error: Could not create the zip file!
纯粹是说明性的,不能在 $(shell ...)
之外使用。 shell 里面有一个功能齐全的 Python
脚本,它将结果输出到屏幕:
define NEWLINE
endef
define RELEASE_CODE
from __future__ import print_function
import os
import zipfile
version = "${version}"
if not version:
print( "Error: You need pass the release version. For example: make release version=1.1", end="@NEWNEWLINE@" )
exit(1)
CURRENT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )
print( "Packing files on %s" % CURRENT_DIRECTORY, end="@NEWNEWLINE@" )
file_names = []
initial_file_names = [
"Makefile",
"build.bat",
"fc-portuges.def",
os.path.join("setup", "makefile.mk"),
os.path.join("setup", "scripts", "timer_calculator.sh"),
]
for direcory_name, dirs, files in os.walk(CURRENT_DIRECTORY):
if ".git" in direcory_name:
continue
for filename in files:
filepath = os.path.join( direcory_name, filename )
if ".git" in filepath or not ( filepath.endswith( ".tex" )
or filepath.endswith( ".bib" )
or filepath.endswith( ".pdf" ) ):
continue
file_names.append( filepath )
for filename in initial_file_names:
filepath = os.path.join( CURRENT_DIRECTORY, filename )
file_names.append( filepath )
zipfilepath = os.path.join( CURRENT_DIRECTORY, version + ".zip" )
zipfileobject = zipfile.ZipFile(zipfilepath, mode="w")
zipfilepathreduced = os.path.dirname( os.path.dirname( zipfilepath ) )
try:
for filename in file_names:
relative_filename = filename.replace( zipfilepathreduced, "" )
print( relative_filename, end="@NEWNEWLINE@" )
zipfileobject.write( filename, relative_filename, compress_type=zipfile.ZIP_DEFLATED )
except Exception as error:
print( "", end="@NEWNEWLINE@" )
print( "An error occurred: %s" % error, end="@NEWNEWLINE@" )
exit(1)
finally:
zipfileobject.close()
print( "", end="@NEWNEWLINE@" )
print( "Successfully created the release version on:@NEWNEWLINE@ %s!" % zipfilepath , end="" )
endef
all:
printf '%s\n' "$(shell echo \
'$(subst ${NEWLINE},@NEWLINE@,${RELEASE_CODE})' | \
sed 's/@NEWLINE@/\n/g' | python - && \
printf 'Successfully created the zip file!\n' || \
printf 'Error: Could not create the zip file!\n' )" | sed 's/@NEWNEWLINE@/\n/g'
现在它 运行 是正确的,但我缺少 python 退出代码,可以用这个 .SHELLSTATUS
:
修复
all:
printf '%s\n' "$(shell echo \
'$(subst ${NEWLINE},@NEWLINE@,${RELEASE_CODE})' | \
sed 's/@NEWLINE@/\n/g' | python - && \
( printf 'Successfully created the zip file!\n'; exit 0 ) || \
( printf 'Error: Could not create the zip file!\n'; exit 1 ) )" | sed 's/@NEWNEWLINE@/\n/g'
exit "${.SHELLSTATUS}"
但是,它仅适用于 make 版本 4.2
或更高版本。
相关:
- https://askubuntu.com/questions/93499/assign-standard-exit-status-to-a-makefile-variable
- Returning value from called function in a shell script
- How to get exit status of a shell command used in GNU Makefile?
- Shell status codes in make
- Zip Multiple files with multiple result in Python
- python/zip: How to eliminate absolute path in zip archive if absolute paths for files are provided?
我设法通过创建一个文件来做到这一点:
fixedmake:
printf '%s\n' "$(shell command && \
printf 'Successfully created the zip file!\n' || \
( printf 'Error: Could not create the zip file!\n'; \
printf 'error\n' > /tmp/has_errors ) )"
if [ -e /tmp/has_errors ]; \
then printf 'Exit with errror\n'; \
rm /tmp/has_errors; \
else \
printf 'Exit with success\n'; \
fi;
-->
printf '%s\n' "Successfully created the zip file!"
Successfully created the zip file!
if [ -e /tmp/has_errors ]; \
then printf 'Exit with errror\n'; \
rm /tmp/has_errors; \
else \
printf 'Exit with success\n'; \
fi;
Exit with success
还有更好的选择吗?
我完全不明白您为什么要尝试使用 $(shell ...)
。这太复杂了,而且毫无意义。 $(shell ...)
的唯一作用是它在任何配方 运行 之前展开。但你永远不会尝试利用它。相反,直接把脚本移到recipe里面,看起来很自然。
例如,考虑这个:
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
#"""
也许更好的解决方案是简单地使 python
成为单个规则的自定义 SHELL
。虽然,在这种情况下,在同一规则中混合使用 python 和 shell 脚本可能会很棘手(但我认为这不是必需的)。
在 make version 4.2 {2016} 上添加了变量 .SHELLSTATUS
。但我最 linux 正在使用 make 版本 4.1
。我怎样才能在旧的 make 版本上获得 make $(shell ...) 命令的退出状态和输出结果?
使用 make 4.2,我可以这样做:
newmake:
printf '%s\n' "$(shell command && \
( printf 'Successfully created the zip file!\n'; exit 0 ) || \
( printf 'Error: Could not create the zip file!\n'; exit 1 ) )"
printf 'Exit status: '%s'\n' "${.SHELLSTATUS}"
哪个 运行 正确:
printf '%s\n' "Successfully created the zip file!"
Successfully created the zip file!
printf 'Exit status: '%s'\n' "0"
Exit status: 0
但是,如果我使用的是 make 4.1 或更早版本,则没有 .SHELLSTATUS
变量。然后,我尝试定义自己的 .SHELLSTATUS
变量,如下所示:
oldmake:
printf '%s\n' "$(shell command && \
( printf 'Successfully created the zip file!\n'; $(eval exit_status := 0) echo ${exit_status} > /dev/null ) || \
( printf 'Error: Could not create the zip file!\n'; $(eval exit_status := 1) echo ${exit_status} > /dev/null ) )"
printf 'Exit status: '%s'\n' "${exit_status}"
但是,它 运行 不正确:
printf '%s\n' "Successfully created the zip file!"
Successfully created the zip file!
printf 'Exit status: '%s'\n' "1"
Exit status: 1
如您所见,打印了正确的消息,但退出状态应该是0
,而不是1
。
通知
消息 Successfully created the zip file!
和 Error: Could not create the zip file!
纯粹是说明性的,不能在 $(shell ...)
之外使用。 shell 里面有一个功能齐全的 Python
脚本,它将结果输出到屏幕:
define NEWLINE
endef
define RELEASE_CODE
from __future__ import print_function
import os
import zipfile
version = "${version}"
if not version:
print( "Error: You need pass the release version. For example: make release version=1.1", end="@NEWNEWLINE@" )
exit(1)
CURRENT_DIRECTORY = os.path.dirname( os.path.realpath( __file__ ) )
print( "Packing files on %s" % CURRENT_DIRECTORY, end="@NEWNEWLINE@" )
file_names = []
initial_file_names = [
"Makefile",
"build.bat",
"fc-portuges.def",
os.path.join("setup", "makefile.mk"),
os.path.join("setup", "scripts", "timer_calculator.sh"),
]
for direcory_name, dirs, files in os.walk(CURRENT_DIRECTORY):
if ".git" in direcory_name:
continue
for filename in files:
filepath = os.path.join( direcory_name, filename )
if ".git" in filepath or not ( filepath.endswith( ".tex" )
or filepath.endswith( ".bib" )
or filepath.endswith( ".pdf" ) ):
continue
file_names.append( filepath )
for filename in initial_file_names:
filepath = os.path.join( CURRENT_DIRECTORY, filename )
file_names.append( filepath )
zipfilepath = os.path.join( CURRENT_DIRECTORY, version + ".zip" )
zipfileobject = zipfile.ZipFile(zipfilepath, mode="w")
zipfilepathreduced = os.path.dirname( os.path.dirname( zipfilepath ) )
try:
for filename in file_names:
relative_filename = filename.replace( zipfilepathreduced, "" )
print( relative_filename, end="@NEWNEWLINE@" )
zipfileobject.write( filename, relative_filename, compress_type=zipfile.ZIP_DEFLATED )
except Exception as error:
print( "", end="@NEWNEWLINE@" )
print( "An error occurred: %s" % error, end="@NEWNEWLINE@" )
exit(1)
finally:
zipfileobject.close()
print( "", end="@NEWNEWLINE@" )
print( "Successfully created the release version on:@NEWNEWLINE@ %s!" % zipfilepath , end="" )
endef
all:
printf '%s\n' "$(shell echo \
'$(subst ${NEWLINE},@NEWLINE@,${RELEASE_CODE})' | \
sed 's/@NEWLINE@/\n/g' | python - && \
printf 'Successfully created the zip file!\n' || \
printf 'Error: Could not create the zip file!\n' )" | sed 's/@NEWNEWLINE@/\n/g'
现在它 运行 是正确的,但我缺少 python 退出代码,可以用这个 .SHELLSTATUS
:
all:
printf '%s\n' "$(shell echo \
'$(subst ${NEWLINE},@NEWLINE@,${RELEASE_CODE})' | \
sed 's/@NEWLINE@/\n/g' | python - && \
( printf 'Successfully created the zip file!\n'; exit 0 ) || \
( printf 'Error: Could not create the zip file!\n'; exit 1 ) )" | sed 's/@NEWNEWLINE@/\n/g'
exit "${.SHELLSTATUS}"
但是,它仅适用于 make 版本 4.2
或更高版本。
相关:
- https://askubuntu.com/questions/93499/assign-standard-exit-status-to-a-makefile-variable
- Returning value from called function in a shell script
- How to get exit status of a shell command used in GNU Makefile?
- Shell status codes in make
- Zip Multiple files with multiple result in Python
- python/zip: How to eliminate absolute path in zip archive if absolute paths for files are provided?
我设法通过创建一个文件来做到这一点:
fixedmake:
printf '%s\n' "$(shell command && \
printf 'Successfully created the zip file!\n' || \
( printf 'Error: Could not create the zip file!\n'; \
printf 'error\n' > /tmp/has_errors ) )"
if [ -e /tmp/has_errors ]; \
then printf 'Exit with errror\n'; \
rm /tmp/has_errors; \
else \
printf 'Exit with success\n'; \
fi;
-->
printf '%s\n' "Successfully created the zip file!"
Successfully created the zip file!
if [ -e /tmp/has_errors ]; \
then printf 'Exit with errror\n'; \
rm /tmp/has_errors; \
else \
printf 'Exit with success\n'; \
fi;
Exit with success
还有更好的选择吗?
我完全不明白您为什么要尝试使用 $(shell ...)
。这太复杂了,而且毫无意义。 $(shell ...)
的唯一作用是它在任何配方 运行 之前展开。但你永远不会尝试利用它。相反,直接把脚本移到recipe里面,看起来很自然。
例如,考虑这个:
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
#"""
也许更好的解决方案是简单地使 python
成为单个规则的自定义 SHELL
。虽然,在这种情况下,在同一规则中混合使用 python 和 shell 脚本可能会很棘手(但我认为这不是必需的)。