Variables 是否可以在Makefile中创建多行字符串变量
我想创建一个多行字符串的makefile变量(例如,电子邮件发布公告的正文)。差不多Variables 是否可以在Makefile中创建多行字符串变量,variables,makefile,Variables,Makefile,我想创建一个多行字符串的makefile变量(例如,电子邮件发布公告的正文)。差不多 ANNOUNCE_BODY=" Version $(VERSION) of $(PACKAGE_NAME) has been released It can be downloaded from $(DOWNLOAD_URL) etc, etc" 但我似乎找不到一个方法来做这件事。可能吗?是的。用\转义换行符: VARIABLE="\ THIS IS A VERY LONG\ TEXT STRING IN
ANNOUNCE_BODY="
Version $(VERSION) of $(PACKAGE_NAME) has been released
It can be downloaded from $(DOWNLOAD_URL)
etc, etc"
但我似乎找不到一个方法来做这件事。可能吗?是的。用
\
转义换行符:
VARIABLE="\
THIS IS A VERY LONG\
TEXT STRING IN A MAKE VARIABLE"
更新
啊,你想要新台词吗?那就不,我想香草口味是没有办法的。但是,您始终可以在命令部分使用here文档
[这不起作用,请参见疯狂科学家的评论]
foo:
echo <<EOF
Here is a multiple line text
with embedded newlines.
EOF
foo:
echo不是一个真正有用的答案,只是为了表明“define”并不像Ax所回答的那样有效(不适合在评论中):
它给出了一个无法找到命令“It”的错误,因此它尝试将annound BODY的第二行解释为命令。是的,您可以使用define关键字声明多行变量,如下所示:
define ANNOUNCE_BODY
Version $(VERSION) of $(PACKAGE_NAME) has been released.
It can be downloaded from $(DOWNLOAD_URL).
etc, etc.
endef
@$(call print,$(ANNOUNCE_BODY))
define TEST
printf '>\n%s\n' "Hello
World\n/$$$$/"
endef
all:
$(call oneshell,$(TEST))
all:
/some/command "`printf '%s\n' 'Version $(VERSION) has been released' '' 'You can download from URL $(URL)'`"
棘手的部分是将多行变量从makefile中取出。如果您只是使用“echo$(ANNOUNCE_BODY)”,您将看到其他人在这里发布的结果——shell尝试将变量的第二行和后续行作为命令本身处理
但是,可以将变量值原样导出到shell作为环境变量,然后将其作为环境变量(而不是make变量)从shell引用。例如:
export ANNOUNCE_BODY
all:
@echo "$$ANNOUNCE_BODY"
注意使用了$$annound\u BODY
,表示shell环境变量引用,而不是$(annound\u BODY)
,后者将是常规的make变量引用。还要确保在变量引用周围使用引号,以确保换行符不会被shell本身解释
当然,这个特殊的技巧可能是平台和外壳敏感的。我用GNU bash 3.2.13在Ubuntu Linux上测试了它;YMMV.为什么不使用字符串中的\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_BODY="first line\\nsecond line"
all:
@echo -e $(ANNOUNCE_BODY)
只是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
“将多行变量从makefile中取出”的另一种方法(Eric Melski称之为“棘手的部分”)是计划使用subst
函数将多行字符串中引入的换行符替换为define
。然后使用-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})'
请注意,最后一个echo上的单引号非常重要。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
如果一个.profile文件不存在,则创建一个.profile文件
此解决方案用于应用程序仅在POSIX shell环境中使用GNU Makefile的情况。该项目不是一个开源项目,平台兼容性是一个问题
目标是创建一个Makefile,以方便设置和使用特定类型的工作区。Makefile带来了各种简单的资源,而不需要其他特殊的归档等。从某种意义上说,它是一个shell归档。然后,一个过程可以说,将这个Makefile放在文件夹中进行工作。设置您的工作区输入makeworkspace
,然后要执行这些操作,请输入makeblah
,等等
更棘手的是要弄清楚该引用什么。上面的代码完成了这项工作,接近于在Makefile中指定here文档的想法。通用性是否好是另一个问题。我认为跨平台使用最安全的答案是每条线路使用一个回音:
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" >> $@
这避免了对echo版本进行任何假设。假设您只想在标准输出上打印变量的内容,还有另一种解决方案:
do-echo:
$(info $(YOUR_MULTILINE_VAR))
您应该使用“定义/结束”构造:
define ANNOUNCE_BODY
Version $(VERSION) of $(PACKAGE_NAME) has been released.
It can be downloaded from $(DOWNLOAD_URL).
etc, etc.
endef
然后您应该将这个变量的值传递给shell命令。但是,如果使用Make variable substitution执行此操作,将导致命令拆分为多个:
ANNOUNCE.txt:
echo $(ANNOUNCE_BODY) > $@ # doesn't work
Qouting也帮不上忙
传递值的最佳方式是通过环境变量传递:
ANNOUNCE.txt: export ANNOUNCE_BODY:=$(ANNOUNCE_BODY)
ANNOUNCE.txt:
echo "$${ANNOUNCE_BODY}" > $@
注意:
变量是为这个特定的目标导出的,这样您就可以重用这个环境,而不会受到太多的污染李>
使用环境变量(变量名周围的双QOUTS和花括号)李>
在变量周围使用引号。没有它们,换行符将丢失,所有文本将显示在一行上
对于GNU Make 3.82及更高版本,当涉及多行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))"
VERSION=1.2.3
包名称=foo-bar
下载\u URL=$(包\u名称).somewhere.net
定义nwln
恩德夫
定义实体
$(软件包名称)的版本$(版本)已发布。
它可以从$(下载URL)下载。
等等等等。
恩德夫
.ONESHELL:
#注意主角
版本:
@printf“$(subst$(nwln),\n,$(公布正文))”
在将上述示例复制和粘贴到编辑器中时,请确保保留所有
字符,否则版本
目标将中断
请注意,.ONESHELL
将导致Makefile中的所有目标对其所有命令使用单个shell。这不会给出一个here文档,但它会以适合管道的方式显示多行消息
=====
$ make method1 method2
Method 1:
this is a
multi-line
message
---
Method 2:
this is a
multi-line
message
---
$
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))
$ 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"
print = $(subst | ,$$'\n',$(1))
@$(call print,$(ANNOUNCE_BODY))
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))
>
Hello
World\n/27801/
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
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
all:
@printf '%s\n' \
'Version $(VERSION) has been released' \
'' \
'You can download from URL $(URL)'
all:
/some/command "`printf '%s\n' 'Version $(VERSION) has been released' '' 'You can download from URL $(URL)'`"
base64 encode the contents in the Makefile (so that I could have a single line and pass them as a docker build arg...)
base64 decode the contents in the Dockerfile (and write them to a file)
...
MY_VAR_ENCODED=$(shell cat /path/to/my/file | base64)
my-build:
@docker build \
--build-arg MY_VAR_ENCODED="$(MY_VAR_ENCODED)" \
--no-cache \
-t my-docker:build .
...
...
ARG MY_VAR_ENCODED
RUN mkdir /root/.ssh/ && \
echo "${MY_VAR_ENCODED}" | base64 -d > /path/to/my/file/in/container
...