是否可以在Makefile中创build一个多行stringvariables
我想创build一个多行string的makefilevariables(例如,电子邮件发布公告的正文)。 就像是
ANNOUNCE_BODY=" Version $(VERSION) of $(PACKAGE_NAME) has been released It can be downloaded from $(DOWNLOAD_URL) etc, etc"
但我似乎无法find办法做到这一点。 可能吗?
是的,您可以使用define关键字来声明一个多行variables,如下所示:
define ANNOUNCE_BODY Version $(VERSION) of $(PACKAGE_NAME) has been released. It can be downloaded from $(DOWNLOAD_URL). etc, etc. endef
棘手的部分是让你的多行variables退出生成文件。 如果你只是使用“echo $(ANNOUNCE_BODY)”这个显而易见的东西,你会看到其他人在这里发布的结果 – shell试图把variables的第二行和后续行作为命令本身来处理。
但是,可以将variables值原样导出到shell作为环境variables,然后将其作为环境variables(不是makevariables)从shell引用。 例如:
export ANNOUNCE_BODY all: @echo "$$ANNOUNCE_BODY"
注意使用$$ANNOUNCE_BODY
,表示一个shell环境variables引用,而不是$(ANNOUNCE_BODY)
,这将是一个常规的makevariables引用。 还要确保在variables引用周围使用引号,以确保换行符不被shell本身解释。
当然,这个特殊的技巧可能是平台和shell敏感的。 我在Ubuntu Linux上用GNU bash 3.2.13testing了它; 因人而异。
另一种将多行variables从makefile中取出的方法(Eric Melski指出的“棘手的部分”)是计划使用subst
函数来replace多行string中用define
引入的新行与\n
。 然后用-e和echo
来解释它们。 您可能需要设置.SHELL = bash才能得到回显。
这种方法的一个优点是,你也把其他这样的转义字符放到你的文本中,并使其受到尊重。
这种综合了迄今为止提到的所有方法…
你结束了:
define newline endef define ANNOUNCE_BODY= As of $(shell date), version $(VERSION) of $(PACKAGE_NAME) has been released. It can be downloaded from $(DOWNLOAD_URL). endef someTarget: echo -e '$(subst $(newline),\n,${ANNOUNCE_BODY})'
注意最后的回声中的单引号是至关重要的。
假设您只想在标准输出中打印variables的内容,还有另一个解决scheme:
do-echo: $(info $(YOUR_MULTILINE_VAR))
GNU`make'手册,6.8:定义多行variables
是。 你用\
避开换行符:
VARIABLE="\ THIS IS A VERY LONG\ TEXT STRING IN A MAKE VARIABLE"
更新
啊,你想换行吗? 那么不,我不认为香草制造商有什么办法。 但是,您始终可以在命令部分使用here-document
[这不行,看疯狂科学家的评论]
foo: echo <<EOF Here is a multiple line text with embedded newlines. EOF
只是Eric Melski的回答:你可以在文本中包含命令的输出,但是你必须使用Makefile语法“$(shell foo)”而不是shell语法“$(foo)”。 例如:
define ANNOUNCE_BODY As of $(shell date), version $(VERSION) of $(PACKAGE_NAME) has been released. It can be downloaded from $(DOWNLOAD_URL). endef
你应该使用“define / endef”构造:
define ANNOUNCE_BODY Version $(VERSION) of $(PACKAGE_NAME) has been released. It can be downloaded from $(DOWNLOAD_URL). etc, etc. endef
那么你应该把这个variables的值传递给shell命令。 但是,如果使用Makevariablesreplace,则会导致命令拆分为多个:
ANNOUNCE.txt: echo $(ANNOUNCE_BODY) > $@ # doesn't work
Qouting也无济于事。
传递值的最好方法是通过环境variables传递它:
ANNOUNCE.txt: export ANNOUNCE_BODY:=$(ANNOUNCE_BODY) ANNOUNCE.txt: echo "$${ANNOUNCE_BODY}" > $@
注意:
- variables被导出为这个特定的目标,这样你可以重用的环境不会受到太多的污染;
- 使用环境variables(variables名称周围的两个qoutes和大括号);
- 围绕variables使用引号。 没有它们,换行符就会丢失,所有的文本都会出现在同一行上。
为什么不使用string中的\ n字符来定义行尾? 还要添加额外的反斜杠以将其添加到多行。
ANNOUNCE_BODY=" \n\ Version $(VERSION) of $(PACKAGE_NAME) has been released \n\ \n\ It can be downloaded from $(DOWNLOAD_URL) \n\ \n\ etc, etc"
我相信跨平台使用最安全的答案是每行使用一个回显:
ANNOUNCE.txt: rm -f $@ echo "Version $(VERSION) of $(PACKAGE_NAME) has been released" > $@ echo "" >> $@ echo "It can be downloaded from $(DOWNLOAD_URL)" >> $@ echo >> $@ echo etc, etc" >> $@
这避免了对可用的回声版本的任何假设。
使用GNU Make, .ONESHELL
选项是您的朋友,当涉及到多行shell代码片段。 把其他答案的提示放在一起,我得到:
VERSION = 1.2.3 PACKAGE_NAME = foo-bar DOWNLOAD_URL = $(PACKAGE_NAME).somewhere.net define nwln endef define ANNOUNCE_BODY Version $(VERSION) of $(PACKAGE_NAME) has been released. It can be downloaded from $(DOWNLOAD_URL). etc, etc. endef .ONESHELL: # mind the *leading* <tab> character version: @printf "$(subst $(nwln),\n,$(ANNOUNCE_BODY))"
确保在将以上示例复制并粘贴到编辑器中时,保留了任何<tab>
字符,否则version
目标将会中断!
这并不给出这里的文档,但是它确实显示了一个适合于pipe道的多行消息。
=====
MSG = this is a\\n\ multi-line\\n\ message method1: @$(SHELL) -c "echo '$(MSG)'" | sed -e 's/^ //'
=====
你也可以使用Gnu的可调用的macros:
=====
MSG = this is a\\n\ multi-line\\n\ message method1: @echo "Method 1:" @$(SHELL) -c "echo '$(MSG)'" | sed -e 's/^ //' @echo "---" SHOW = $(SHELL) -c "echo '$1'" | sed -e 's/^ //' method2: @echo "Method 2:" @$(call SHOW,$(MSG)) @echo "---"
=====
这是输出:
=====
$ make method1 method2 Method 1: this is a multi-line message --- Method 2: this is a multi-line message --- $
=====
按照.ONESHELL的精神,在.ONESHELL挑战性的环境中,
define _oneshell_newline_ endef define oneshell @eval "$$(printf '%s\n' '$(strip \ $(subst $(_oneshell_newline_),\n, \ $(subst \,\/, \ $(subst /,//, \ $(subst ','"'"',$(1))))))' | \ sed -e 's,\\n,\n,g' -e 's,\\/,\\,g' -e 's,//,/,g')" endef
一个使用的例子是这样的:
define TEST printf '>\n%s\n' "Hello World\n/$$$$/" endef all: $(call oneshell,$(TEST))
这显示了输出(假设pid 27801):
> Hello World\n/27801/
这种方法确实允许一些额外的function:
define oneshell @eval "set -eux ; $$(printf '%s\n' '$(strip \ $(subst $(_oneshell_newline_),\n, \ $(subst \,\/, \ $(subst /,//, \ $(subst ','"'"',$(1))))))' | \ sed -e 's,\\n,\n,g' -e 's,\\/,\\,g' -e 's,//,/,g')" endef
这些shell选项将会:
- 执行时打印每个命令
- 退出第一个失败的命令
- 将未定义的shellvariables用作错误
其他有趣的可能性将可能表明自己。
我最喜欢阿尔哈迪斯的回答。 但要保持列格式,添加一件事。
SYNOPSIS := :: Synopsis: Makefile\ | ::\ | :: Usage:\ | :: make .......... : generates this message\ | :: make synopsis . : generates this message\ | :: make clean .... : eliminate unwanted intermediates and targets\ | :: make all ...... : compile entire system from ground-up\ endef
输出:
:: Synopsis: Makefile :: :: Usage: :: make .......... : generates this message :: make synopsis . : generates this message :: make clean .... : eliminate unwanted intermediates and targets :: make all ...... : compile entire system from ground-up
不是一个真正有用的答案,而只是表明“定义”不能像Ax回答的那样工作(不适合评论):
VERSION=4.3.1 PACKAGE_NAME=foobar DOWNLOAD_URL=www.foobar.com define ANNOUNCE_BODY Version $(VERSION) of $(PACKAGE_NAME) has been released It can be downloaded from $(DOWNLOAD_URL) etc, etc endef all: @echo $(ANNOUNCE_BODY)
它给出了命令“It”无法find的错误,所以它试图将ANNOUNCE BODY的第二行解释为命令。
它为我工作:
ANNOUNCE_BODY="first line\\nsecond line" all: @echo -e $(ANNOUNCE_BODY)
GNU Makefile可以做如下的事情。 这是丑陋的,我不会说你应该这样做,但我在某些情况下。
PROFILE = \ \#!/bin/sh.exe\n\ \#\n\ \# A MinGW equivalent for .bash_profile on Linux. In MinGW/MSYS, the file\n\ \# is actually named .profile, not .bash_profile.\n\ \#\n\ \# Get the aliases and functions\n\ \#\n\ if [ -f \$${HOME}/.bashrc ]\n\ then\n\ . \$${HOME}/.bashrc\n\ fi\n\ \n\ export CVS_RSH="ssh"\n # .profile: echo -e "$(PROFILE)" | sed -e 's/^[ ]//' >.profile
make .profile
会创build一个.profile文件(如果不存在的话)。
这个解决scheme被用在应用程序只能在POSIX shell环境中使用GNU Makefile的地方。 该项目不是一个平台兼容性问题的开源项目。
目标是创build一个Makefile,以便于设置和使用特定types的工作区。 Makefile带来了各种简单的资源,而不需要像其他特殊的档案等东西。从某种意义上说,这是一个shell归档。 然后程序可以说这样的东西放在文件夹中的这个Makefile工作。设置你的工作区进入make workspace
,然后做等等,进入make blah
。
什么可以变得棘手是找出什么壳报价。 上面做的工作,接近在Makefile中指定here文件的想法。 对于一般用途来说是否是一个好主意是另外一个问题。
使用stringreplace :
VERSION := 1.1.1 PACKAGE_NAME := Foo Bar DOWNLOAD_URL := https://go.get/some/thing.tar.gz ANNOUNCE_BODY := Version $(VERSION) of $(PACKAGE_NAME) has been released. \ | \ | It can be downloaded from $(DOWNLOAD_URL) \ | \ | etc, etc
然后在你的食谱,放
@echo $(subst | ,$$'\n',$(ANNOUNCE_BODY))
这是因为Make代替了所有的|
(注意空格)并换行换行符( $$'\n'
)。 您可以将等效的shell脚本调用看作是这样的:
之前:
$ echo "Version 1.1.1 of Foo Bar has been released. | | It can be downloaded from https://go.get/some/thing.tar.gz | | etc, etc"
后:
$ echo "Version 1.1.1 of Foo Bar has been released. > > It can be downloaded from https://go.get/some/thing.tar.gz > > etc, etc"
我不确定在非POSIX系统上$'\n'
是否可用,但是如果你可以访问单个换行符(甚至通过从外部文件读取一个string),基本原理是一样的。
如果你有很多这样的消息,你可以通过使用macros来减less噪音:
print = $(subst | ,$$'\n',$(1))
你可以像这样调用它:
@$(call print,$(ANNOUNCE_BODY))
希望这有助于某人。 =)