複数行の文字列であるメイクファイル変数を作成したい(たとえば、電子メールのリリースアナウンスの本文)。何かのようなもの
ANNOUNCE_BODY="
Version $(VERSION) of $(PACKAGE_NAME) has been released
It can be downloaded from $(DOWNLOAD_URL)
etc, etc"
しかし、私はこれを行う方法を見つけることができないようです。出来ますか?
はい、defineキーワードを使用して、次のように複数行の変数を宣言できます。
_define ANNOUNCE_BODY
Version $(VERSION) of $(PACKAGE_NAME) has been released.
It can be downloaded from $(DOWNLOAD_URL).
etc, etc.
endef
_
トリッキーな部分は、メイクファイルから複数行の変数を戻すことです。 「echo $(ANNOUNCE_BODY)」を使用して明らかなことを行うと、他の人がここに投稿した結果が表示されます。シェルは変数の2行目以降をコマンドとして処理しようとします。
ただし、変数値をそのままシェルに環境変数としてエクスポートし、それをシェルから環境変数として参照することができます(メイク変数ではありません)。例えば:
_export ANNOUNCE_BODY
all:
@echo "$$ANNOUNCE_BODY"
_
通常のmake変数参照である$(ANNOUNCE_BODY)
ではなく、シェル環境変数参照を示す_$$ANNOUNCE_BODY
_の使用に注意してください。また、変数参照を引用符で囲み、改行がシェル自体によって解釈されないようにします。
もちろん、この特定のトリックはプラットフォームおよびシェルに依存する可能性があります。 Ubuntu LinuxでGNU bash 3.2.13; YMMVでテストしました。
「Makefileから複数行の変数を元に戻す」別のアプローチ(Eric Melskiによって「トリッキーな部分」と呼ばれます)は、subst
関数を使用して、導入された改行を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})'
最終エコーの単一引用符が重要であることに注意してください。
変数の内容のみを標準出力に出力したい場合、別の解決策があります:
do-echo:
$(info $(YOUR_MULTILINE_VAR))
はい。 \
で改行をエスケープします:
VARIABLE="\
THIS IS A VERY LONG\
TEXT STRING IN A MAKE VARIABLE"
ああ、改行が欲しい?それでは、Vanilla Makeに方法はないと思います。ただし、コマンド部分では常にヒアドキュメントを使用できます
[これは機能しません。MadScientistのコメントを参照]
foo:
echo <<EOF
Here is a multiple line text
with embedded newlines.
EOF
Eric Melskiの答えへの追記:コマンドの出力をテキストに含めることができますが、シェル構文「$(foo)」ではなくMakefile構文「$(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
Alhadisの答えが一番好きです。ただし、列の書式を維持するには、もう1つ追加します。
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
GNU Make、.ONESHELL
オプションは、複数行のシェルスニペットに関してはあなたの友人です。他の答えからのヒントをまとめると、私は得る:
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
ターゲットが壊れます!
.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/
このアプローチにより、いくつかの追加機能が可能になります。
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
これらのシェルオプションは次のことを行います。
他の興味深い可能性は、おそらく自分自身を示唆しています。
"define/endef" Make構文を使用する必要があります。
define ANNOUNCE_BODY
Version $(VERSION) of $(PACKAGE_NAME) has been released.
It can be downloaded from $(DOWNLOAD_URL).
etc, etc.
endef
次に、この変数の値をシェルコマンドに渡す必要があります。ただし、変数の置換を使用してこれを行うと、コマンドが複数に分割されます。
ANNOUNCE.txt:
echo $(ANNOUNCE_BODY) > $@ # doesn't work
Qoutingも助けにはなりません。
値を渡す最良の方法は、環境変数を介して渡すことです。
ANNOUNCE.txt: export ANNOUNCE_BODY:=$(ANNOUNCE_BODY)
ANNOUNCE.txt:
echo "$${ANNOUNCE_BODY}" > $@
通知:
OPに完全に関連するわけではありませんが、将来的に誰かの助けになることを願っています。 (この質問は、Google検索で最もよく出てくる質問です)。
Makefileでは、ファイルの内容をdocker buildコマンドに渡したいと思いました。
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)
以下の例を参照してください。
nb:私の特定のケースでは、 https://vsupalov.com/build-docker-image-clone-private-repo-ssh-の例を使用して、イメージのビルド中にsshキーを渡したいkey / (マルチステージドッカービルドを使用してgitリポジトリを複製し、ビルドの2番目のステージで最終イメージからsshキーをドロップします)
...
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
...
これはヒアドキュメントを提供しませんが、パイプに適した方法で複数行のメッセージを表示します。
=====
MSG = this is a\\n\
multi-line\\n\
message
method1:
@$(Shell) -c "echo '$(MSG)'" | sed -e 's/^ //'
=====
Gnuの呼び出し可能マクロを使用することもできます:
=====
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
---
$
=====
文字列の\ 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"
別の方法として、printfコマンドを使用できます。これは、OSXまたは機能の少ない他のプラットフォームで役立ちます。
単に複数行のメッセージを出力するには:
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)'`"
GNU Makefileでは、次のようなことができます。それはisいですし、あなたがそれをすべきだとは言いませんが、特定の状況ではそうします。
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ファイルが存在しない場合に作成します。
このソリューションは、アプリケーションがPOSIXシェル環境でGNU Makefileのみを使用する場合に使用されました。このプロジェクトは、プラットフォームの互換性が問題となるオープンソースプロジェクトではありません。
目標は、特定の種類のワークスペースのセットアップと使用の両方を容易にするMakefileを作成することでした。 Makefileは、別の特別なアーカイブなどを必要とせずに、さまざまな単純なリソースをもたらします。ある意味では、シェルアーカイブです。プロシージャは、このMakefileを作業するフォルダにドロップするなどのことを言うことができます。ワークスペースをセットアップするにはmake workspace
、その後何とかするには、make blah
など.
トリッキーになるのは、シェルが何を引用するかを考え出すことです。上記は仕事をし、Makefileでヒアドキュメントを指定するという考えに近いです。一般的な使用に適しているかどうかは、まったく別の問題です。
それは私のために働いた:
ANNOUNCE_BODY="first line\\nsecond line"
all:
@echo -e $(ANNOUNCE_BODY)
クロスプラットフォームで使用する場合の最も安全な答えは、行ごとに1つのエコーを使用することだと思います。
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" >> $@
これにより、使用可能なエコーのバージョンに関する仮定が行われなくなります。
本当に役立つ答えではありませんが、Axeの回答どおりに「定義」が機能しないことを示すためだけです(コメントに収まりませんでした)。
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'が見つからないというエラーが発生するため、ANNOUNCE BODYの2行目をコマンドとして解釈しようとします。
string substitution を使用します。
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'
)と交換するために機能します。同等のシェルスクリプト呼び出しは、次のようなものと考えることができます。
前:
$ 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"
$'\n'
がPOSIX以外のシステムで使用できるかどうかはわかりませんが、単一の改行文字にアクセスできる場合(外部ファイルから文字列を読み取っても)、基本的な原則は同じです。
このようなメッセージが多数ある場合は、 マクロ を使用してノイズを減らすことができます。
print = $(subst | ,$$'\n',$(1))
このように呼び出す場所:
@$(call print,$(ANNOUNCE_BODY))
これが誰かを助けることを願っています。 =)