回答:
はい、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行目以降をコマンドとして処理しようとします。
ただし、変数値をそのまま環境変数としてシェルにエクスポートし、それをシェルから環境変数(make変数ではない)として参照できます。例えば:
export ANNOUNCE_BODY
all:
@echo "$$ANNOUNCE_BODY"
$$ANNOUNCE_BODY
ではなく、シェル環境変数の参照を示すの使用に注意してください$(ANNOUNCE_BODY)
通常のmake変数参照であるでは。また、変数参照を引用符で囲み、改行がシェル自体によって解釈されないようにしてください。
もちろん、この特定のトリックはプラットフォームとシェルに依存する可能性があります。私はそれをGNU bash 3.2.13を備えたUbuntu Linuxでテストしました。YMMV。
ANNOUNCE_BODY
他の変数定義で使用する場合は、他のmake変数と同じように参照してください。たとえば、OTHER=The variable ANNOUNCE_BODY is $(ANNOUNCE_BODY)
。もちろん、コマンドで抜け出しexport
たい場合は、まだトリックが必要ですOTHER
。
「Makefileから複数行の変数を取り戻す」別のアプローチ(Eric Melskiによって「注意が必要な部分」と記されています)は、subst
関数を使用しdefine
て、複数行の文字列で導入された改行をで置き換えることを計画すること\n
です。次に、-e with 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})'
最後のエコーの単一引用符は重要です。
=
アフターdefine ANNOUNCE_BODY
を削除する必要がありました。
変数の内容を標準出力に出力するだけの場合、別の解決策があります。
do-echo:
$(info $(YOUR_MULTILINE_VAR))
make: 'do-echo' is up to date.
。「何のOPを」使用しないことで、私はそれを沈黙することができましたCOMAND:@: $(info $(YOUR_MULTILINE_VAR))
.PHONY
、Makefileにそのルールをチェックするものがないことを伝えるために使用できます。Makefileはもともとコンパイラー用でしたが、誤解make
しない限り、ルールが何も変更されないことを予期していないため、理解できない魔法をかけているため、ルールは「完了」していると見なされます。.PHONY do-echo
ファイルに追加すると、make
これを無視してとにかくコードを実行するように指示されます。
$(info ...)
makeルールの外に配置できます。それでも出力は生成されます。
はい。あなたは改行をエスケープし\
ます:
VARIABLE="\
THIS IS A VERY LONG\
TEXT STRING IN A MAKE VARIABLE"
ああ、あなたは改行が欲しいですか?では、いいえ、バニラメイクには方法がないと思います。ただし、コマンド部分では常にヒアドキュメントを使用できます
[これは機能しません。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
これはヒアドキュメントを提供しませんが、パイプに適した方法で複数行のメッセージを表示します。
=====
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"
$(subst \n ,\n,$(TEXT))
はもっと良い方法があればいいのにと思っていますが、私はテキストを通しました
"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}" > $@
通知:
.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
これらのシェルオプションは次のとおりです。
他の興味深い可能性は、おそらく彼ら自身を示唆するでしょう。
アラディスの答えが一番好きです。ただし、列の書式を維持するには、もう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
make
と、通常、ビルドプロセスの開始を期待して実行します。
OPとは完全には関係ありませんが、これが将来誰かを助けることを願っています。(この質問はグーグル検索で最もよく出てくる質問なので)。
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-key/( git repoのクローンを作成し、ビルドの第2ステージの最終イメージからsshキーをドロップするマルチステージDockerビルド)
...
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
...
GNU Make 3.82 .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
により、Makefile内のすべてのターゲットがすべてのコマンドに対して単一のシェルを使用するようになります。
make version printf "Version 1.2.3 of foo-bar has been released. /bin/sh: 1: Syntax error: Unterminated quoted string make: *** [version] Error 2
(GNU make 3,81)
@printf ...
ステートメントの前にある空白ではなく、先行するTAB文字の存在を確認してください
.ONESHELL
メイク3.82の新機能です。
*** missing separator. Stop.
。
本当に役立つ回答ではありませんが、「定義」が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」が見つからないというエラーが発生するため、ANNOUNCE BODYの2行目をコマンドとして解釈しようとします。
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ファイルが存在しない場合は作成します。
このソリューションは、アプリケーションがPOSIXシェル環境でGNU Makefileのみを使用する場合に使用されました。このプロジェクトは、プラットフォームの互換性が問題となるオープンソースプロジェクトではありません。
目標は、特定の種類のワークスペースのセットアップと使用の両方を容易にするMakefileを作成することでした。Makefileは、別の特別なアーカイブなどを必要とせずに、さまざまな単純なリソースをもたらします。ある意味では、シェルアーカイブです。手順は、ドロップのようなものを言うことができるで動作するようにフォルダ内のこのメイクファイル。ワークスペースを設定入力しmake workspace
、その後、何とかを行うには、入力make blah
、など
トリッキーになることができるのは、何を引用するかを理解することです。上記は仕事をし、Makefileでヒアドキュメントを指定するという考えに近いです。一般的な使用に適しているかどうかは、まったく別の問題です。
文字列置換を使用:
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))
これが誰かを助けることを願っています。=)
代わりに、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)'`"
export ANNOUNCE_BODY
ルール内の変数のみを設定します。$$ ANNOUNCE_BODYを参照して他の変数を定義することはできません。