Можно ли создать многострочную строковую переменную в файле Makefile
Я хочу создать переменную makefile, которая является многострочной строкой (например, тело объявления о выпуске электронной почты). что-то вроде
ANNOUNCE_BODY="
Version $(VERSION) of $(PACKAGE_NAME) has been released
It can be downloaded from $(DOWNLOAD_URL)
etc, etc"
но я не могу найти способ сделать это. Возможно ли это?
19 ответов:
Да, вы можете использовать ключевое слово define для объявления многострочной переменной, например:
define ANNOUNCE_BODY Version $(VERSION) of $(PACKAGE_NAME) has been released. It can be downloaded from $(DOWNLOAD_URL). etc, etc. endefсложная часть заключается в получении вашей многострочной переменной обратно из файла makefile. Если вы просто сделаете очевидную вещь использования "echo $(ANNOUNCE_BODY)", вы увидите результат, который другие опубликовали здесь-оболочка пытается обрабатывать вторую и последующие строки переменной как команды сами.
однако вы можете экспортировать значение переменной как есть в оболочку как переменная среды, а затем ссылаться на нее из оболочки в качестве переменной среды (не сделать переменную). Например:
export ANNOUNCE_BODY all: @echo "$$ANNOUNCE_BODY"обратите внимание на использование
$$ANNOUNCE_BODY, указывая ссылку на переменную среды оболочки, а не$(ANNOUNCE_BODY), который был бы регулярным сделать ссылку на переменную. Также обязательно используйте кавычки вокруг ссылки на переменную, чтобы убедиться, что новые строки не интерпретируются самой оболочкой.конечно, этот конкретный трюк может быть платформой и раковина чувствительная. Я тестировал его на Ubuntu Linux с GNU bash 3.2.13; YMMV.
другой подход к " получению вашей многострочной переменной обратно из файла makefile "(отмеченный Эриком Мельски как "сложная часть"), заключается в планировании использования
substфункция для замены новых строк, введенных сdefineв строку с\n. Затем используйте -e сechoинтерпретировать их. Возможно, вам потребуется установить.SHELL=bash, чтобы получить эхо, которое делает это.преимущество этого подхода заключается в том, что вы также помещаете другие такие escape-символы в свой текст и пусть их уважают.
этот вид синтезирует все подходы, упомянутые до сих пор...
вы заканчиваете с:
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"обновление
- Ах, вы хотите, чтобы строки? Тогда нет, я не думаю, что есть какой-то способ сделать ваниль. Однако, вы всегда можете использовать здесь-документ в командной части
[это не работает, см. комментарий от MadScientist]
foo: echo <<EOF Here is a multiple line text with embedded newlines. EOF
просто постскриптум к ответу Эрика Мельски: вы можете включить вывод команд в текст, но вы должны использовать синтаксис Makefile "$(shell foo)", а не синтаксис оболочки "$(foo)". Например:
define ANNOUNCE_BODY As of $(shell date), version $(VERSION) of $(PACKAGE_NAME) has been released. It can be downloaded from $(DOWNLOAD_URL). endef
почему бы вам не использовать символ \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" >> $@это позволяет избежать каких-либо предположений о версии echo.
вы должны использовать "define / endef" Make construct:
define ANNOUNCE_BODY Version $(VERSION) of $(PACKAGE_NAME) has been released. It can be downloaded from $(DOWNLOAD_URL). etc, etc. endefзатем вы должны передать значение этой переменной в командной оболочке. Но, если вы сделаете это с помощью Make variable substitution, это приведет к разделению команды на несколько:
ANNOUNCE.txt: echo $(ANNOUNCE_BODY) > $@ # doesn't workQouting тоже не поможет.
лучший способ передать значение, чтобы передать его через переменную окружения:
ANNOUNCE.txt: export ANNOUNCE_BODY:=$(ANNOUNCE_BODY) ANNOUNCE.txt: echo "$${ANNOUNCE_BODY}" > $@обратите внимание:
- переменная экспортируется для этой конкретной цели, так что вы можете повторно использовать эту среду не будет сильно загрязнена;
- используйте переменную окружения (двойные qoutes и фигурные скобки вокруг имени переменной);
- использование кавычек вокруг переменной. Без них новые строки будут потеряны, и весь текст появится на одной линии.
С GNU Make, the
.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цель сломать!
Это не дает здесь документ, но он отображает многострочное сообщение таким образом, который подходит для труб.
=====
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 ''" | 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 оспариваемые среды:
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эти параметры оболочка:
- печать каждой команды по мере ее выполнения
- выход на Первом не удалось команда
- рассматривать использование неопределенных переменных оболочки как ошибку
другие интересные возможности, вероятно, предложить себя.
мне нравится ответить alhadis лучше. Но чтобы сохранить форматирование столбцов, добавьте еще одну вещь.
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
не очень полезный ответ, но просто указать, что "define" не работает, как ответил 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 " не может быть найдена, поэтому он пытается интерпретировать вторую строку тела объявления как команду.
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создает a .файл профиля, если он не существует.это решение было использовано там, где приложение будет использовать только GNU Makefile в среде оболочки POSIX. Проект не является проектом с открытым исходным кодом, где совместимость платформы является проблемой.
цель состояла в том, чтобы создать 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'). Вы можете думать об эквивалентных вызовах shell-script как о чем-то вроде этого:перед:
$ 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)'`"
не совсем связано с ОП, но, надеюсь, это поможет кому-то в будущем. (поскольку этот вопрос является тем, который чаще всего встречается в поисковых системах google).
В моем файле Makefile я хотел передать содержимое файла команде сборки docker, после долгих раздумий я решил:
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: в моем конкретном случае я хотел передать ключ ssh во время сборки образа, используя пример из https://vsupalov.com/build-docker-image-clone-private-repo-ssh-key/ (используя многоступенчатую сборку docker для клонирования репозитория git, затем удалите ключ ssh из окончательного изображения на 2-м этапе сборки)
Makefile
... 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 . ...Dockerfile
... ARG MY_VAR_ENCODED RUN mkdir /root/.ssh/ && \ echo "${MY_VAR_ENCODED}" | base64 -d > /path/to/my/file/in/container ...
Comments