Variables 是否可以在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

我想创建一个多行字符串的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 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
    ...