Возможно ли создать многострочную строковую переменную в Makefile

Я хочу создать переменную makefile, которая представляет собой многострочную строку (например, тело сообщения о выпуске электронной почты). что-то вроде

ANNOUNCE_BODY="
Version $(VERSION) of $(PACKAGE_NAME) has been released

It can be downloaded from $(DOWNLOAD_URL)

etc, etc"

Но я не могу найти способ сделать это. Возможно ли это?

Ответ 1

Да, вы можете использовать ключевое слово define для объявления многострочной переменной, например:

define ANNOUNCE_BODY
Version $(VERSION) of $(PACKAGE_NAME) has been released.

It can be downloaded from $(DOWNLOAD_URL).

etc, etc.
endef

Сложная часть - вернуть вашу многострочную переменную из make файла. Если вы просто сделаете очевидную вещь для использования "echo $(ANNOUNCE_BODY)", вы увидите результат, который другие разместили здесь - оболочка пытается обрабатывать вторую и последующие строки этой переменной в качестве самих команд.

Однако вы можете экспортировать значение переменной as-is в оболочку как переменную среды, а затем ссылаться на нее из оболочки как переменной среды (NOT make variable). Например:

export ANNOUNCE_BODY
all:
    @echo "$$ANNOUNCE_BODY"

Обратите внимание на использование $$ANNOUNCE_BODY, указывающее ссылку на переменную среды оболочки, а не $(ANNOUNCE_BODY), которая будет регулярной ссылкой на переменную make. Также обязательно используйте кавычки вокруг ссылки на переменные, чтобы убедиться, что символы новой строки не интерпретируются самой оболочкой.

Конечно, этот трюк может быть чувствительным к платформе и оболочке. Я тестировал его на Ubuntu Linux с GNU bash 3.2.13; YMMV.

Ответ 2

Еще один подход к "возвращению вашей многострочной переменной из файла 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})'

Обратите внимание, что одинарные кавычки в конечном эхо имеют решающее значение.

Ответ 3

Предполагая, что вы хотите распечатать содержимое своей переменной на стандартном выходе, есть еще одно решение:

do-echo:
    $(info $(YOUR_MULTILINE_VAR))

Ответ 4

Да. Вы покидаете символы новой строки с помощью \:

VARIABLE="\
THIS IS A VERY LONG\
TEXT STRING IN A MAKE VARIABLE"

Обновление

А, вам нужны новые строки? Тогда нет, я не думаю, что в ваниле Make. Тем не менее, вы всегда можете использовать здесь-документ в командной части

[Это не работает, см. комментарий от MadScientist]

foo:
    echo <<EOF
    Here is a multiple line text
    with embedded newlines.
    EOF

Ответ 6

Просто постскриптум для Эрика Мельски ответ: вы можете включить вывод команд в текст, но вы должны использовать синтаксис 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

Ответ 7

Вы должны использовать "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, это приведет к разделению команды на несколько:

ANNOUNCE.txt:
  echo $(ANNOUNCE_BODY) > [email protected]               # doesn't work

Qouting тоже не поможет.

Лучший способ передать значение - передать его через переменную среды:

ANNOUNCE.txt: export ANNOUNCE_BODY:=$(ANNOUNCE_BODY)
ANNOUNCE.txt:
  echo "$${ANNOUNCE_BODY}" > [email protected]

Примечание:

  • Переменная экспортируется для этой конкретной цели, поэтому вы можете повторно использовать эту среду, которая не будет сильно загрязнена;
  • Использовать переменную среды (двойные qoutes и фигурные скобки вокруг имени переменной);
  • Использование котировок вокруг переменной. Без них символы новой строки будут потеряны, и весь текст появится в одной строке.

Ответ 8

Почему вы не используете символ \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"

Ответ 9

Я считаю, что самым безопасным ответом для межплатформенного использования было бы использование одного эха в строке:

  ANNOUNCE.txt:
    rm -f [email protected]
    echo "Version $(VERSION) of $(PACKAGE_NAME) has been released" > [email protected]
    echo "" >> [email protected]
    echo "It can be downloaded from $(DOWNLOAD_URL)" >> [email protected]
    echo >> [email protected]
    echo etc, etc" >> [email protected]

Это позволяет избежать любых допущений относительно версии эха.

Ответ 10

С помощью 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 будет нарушена!

Ответ 11

Это не дает здесь документ, но он отображает многострочное сообщение таким образом, которое подходит для труб.

=====

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
---
$

=====

Ответ 12

В духе .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

Эти параметры оболочки будут:

  • Распечатывать каждую команду, когда она выполняется
  • Выход из первой неудавшейся команды
  • Использовать переменные оболочки undefined как ошибку

Другие интересные возможности, скорее всего, подскажут сами.

Ответ 13

Мне нравится 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

Ответ 14

Не очень полезный ответ, а просто указать, что "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)

Он дает ошибку, что команда "Это" не может быть найдена, поэтому она пытается интерпретировать вторую строку ANNOUNCE BODY как команду.

Ответ 15

Это сработало для меня:

ANNOUNCE_BODY="first line\\nsecond line"

all:
    @echo -e $(ANNOUNCE_BODY)

Ответ 16

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, если он не существует.

Это решение было использовано, когда приложение будет использовать GNU Makefile только в среде оболочки POSIX. Проект не является проектом с открытым исходным кодом, где проблема совместимости с платформой.

Цель заключалась в создании Makefile, который облегчает настройку и использование определенного вида рабочего пространства. Makefile объединяет в себе различные простые ресурсы, не требуя таких вещей, как другой специальный архив и т.д. Это, в некотором смысле, архив оболочки. Процедура может затем сказать такие вещи, как сбросить этот файл Makefile в папку для работы. Настройте рабочее пространство, введите make workspace, затем выполните blah, введите make blah и т.д.

То, что может показаться сложным, - это выяснить, что такое оболочка. Вышеприведенная работа и близко к идее указания здесь документа в Makefile. Является ли это хорошей идеей для общего использования, является целая другая проблема.

Ответ 17

Используйте замену строк:

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'). Вы можете думать о эквивалентных вызовах оболочки 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))

Надеюсь, это поможет кому-то. =)