Makefile 中的 Heredoc?

发布于 2024-11-05 00:29:30 字数 374 浏览 1 评论 0原文

这到底有可能吗?如何实现?

更新:我需要这个,因为我从动态和静态数据创建一个文件。

用例:我有一个测试目录。每个 C 文件都会生成一个测试可执行文件。 我可以根据

SRCS = $(wildcard [a-z]*.c)

需要添加新的测试,并且 make 会找到新的测试,编译、运行并 valgrind 它们。我也用git。我希望 .gitignore 包含可执行文件。

就这样。如何创建 .gitignore 并包含静态数据,即我想要忽略的文件(*.odepend)以及可执行文件动态地?

Is this possible at all and how?

Update: I need this because I create a file both from dynamic and static data.

Use case: I have a test directory. Each C file produces a test executable. With

SRCS = $(wildcard [a-z]*.c)

I can add new tests as needed and make will find the new tests, compile, run and valgrind them. I also use git. I would like .gitignoreto include the executables.

So there. How to create .gitignore and include static data, i.e. the files I want to be ignored (*.o and depend) and also the executables dynamically?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

青春如此纠结 2024-11-12 00:29:30

另一个 GNU Make 解决方案。

您可以使用 defineexport 命令来完成此操作,如下所示:

define GITIGNOREDS
*.o
depend
endef

SRCS = $(wildcard [a-z]*.c)
EXES = $(SRCS:.c=)


export GITIGNOREDS
.gitignore: $(SRCS)
    echo $(EXES) | sed 's/ /\n/g' > $@
    echo "$GITIGNOREDS" >> $@

您必须小心内部的 make 扩展(即 $(x))不过定义块。

Another GNU Make solution.

You can do it using the define and export commands as follows:

define GITIGNOREDS
*.o
depend
endef

SRCS = $(wildcard [a-z]*.c)
EXES = $(SRCS:.c=)


export GITIGNOREDS
.gitignore: $(SRCS)
    echo $(EXES) | sed 's/ /\n/g' > $@
    echo "$GITIGNOREDS" >> $@

You have to be careful of make expansions (i.e. $(x)) inside the define block though.

请别遗忘我 2024-11-12 00:29:30

是的,你可以。正如其他人指出的那样,您可能不应该这样做,但您可以。 Ash 的答案有一种涉及定义命令的解决方案,但这很麻烦,并且可能会使将变量扩展到正确的值变得棘手。另一个技巧是使用 .ONESHELL :特殊目标

有时您希望将配方中的所有行传递给 shell 的单次调用。通常在两种情况下这是有用的:首先,它可以通过避免额外的进程来提高由许多命令行组成的 makefile 的性能。其次,您可能希望在配方命令中包含换行符(例如,您可能使用非常不同的解释器作为 SHELL)。如果 .ONESHELL 特殊目标出现在 makefile 中的任何位置,则每个目标的所有配方行都将提供给 shell 的单次调用。配方行之间的换行符将被保留。

警告几句:

  • 这将影响 Makefile 中所有配方的工作方式。
  • 用于抑制输出或忽略错误的行前缀运算符(例如 -@)仅适用于所有配方的第一行并对所有行生效下列的。
  • 一些旧版本或 GNU Make(例如在 Travis 的 CI 系统上默认运行的版本,甚至在新容器中)可能会忽略此指令,从而导致奇怪的错误和陷阱。确保您了解您的目标环境。

排除了这一点,生成 Markdown 文件的方式如下:

SHELL = bash
.ONESHELL:
MYVAR = "Some Title"

file.md:
    cat <<- EOF > $@
        $(MYVAR)
        ========

        This stuff will all be written to the target file. Be sure
        to escape dollar-signs and backslashes as Make will be scanning
        this text for variable replacements before bash scans it for its
        own strings.

        Otherwise formatting is just as in any other bash heredoc. Note
        I used the <<- operator which allows for indentation. This markdown
        file will not have whitespace at the start of lines.

        Here is a programmatic way to generate a markdwon list all PDF files
        in the current directory:

        `find -maxdepth 1 -name '*.pdf' -exec echo " + {}" \;`
    EOF

请注意,另外一个问题是 Make 会跳过空行。如果你的定界文档内容中有一个空行很重要,你需要确保用适当的空白级别缩进该行以匹配定界文档,否则 Make 会吃掉它,甚至不会将它传递给 cat!

Yes, you can. As others note, you probably shouldn't, but you can. Ash's answer has one solution involving define commands, but that is combersome and can make it tricky to get variables expanded to the right values. Another trick is to use the .ONESHELL: special target.

Sometimes you would prefer that all the lines in the recipe be passed to a single invocation of the shell. There are generally two situations where this is useful: first, it can improve performance in makefiles where recipes consist of many command lines, by avoiding extra processes. Second, you might want newlines to be included in your recipe command (for example perhaps you are using a very different interpreter as your SHELL). If the .ONESHELL special target appears anywhere in the makefile then all recipe lines for each target will be provided to a single invocation of the shell. Newlines between recipe lines will be preserved.

A couple words of warning:

  • This will affect how all recipes in your Makefile work.
  • Line prefix operators such as - or @ to suppress output or ignore errors only work on the first line of all recipes and take effect for all lines following.
  • Some old versions or GNU Make (such as that running by default on Travis's CI system even in the new containers) may ignore this directive leading to strange bugs and gotchas. Make sure you know your target environments.

With that out of the way, here's how it might look to generate a markdown file:

SHELL = bash
.ONESHELL:
MYVAR = "Some Title"

file.md:
    cat <<- EOF > $@
        $(MYVAR)
        ========

        This stuff will all be written to the target file. Be sure
        to escape dollar-signs and backslashes as Make will be scanning
        this text for variable replacements before bash scans it for its
        own strings.

        Otherwise formatting is just as in any other bash heredoc. Note
        I used the <<- operator which allows for indentation. This markdown
        file will not have whitespace at the start of lines.

        Here is a programmatic way to generate a markdwon list all PDF files
        in the current directory:

        `find -maxdepth 1 -name '*.pdf' -exec echo " + {}" \;`
    EOF

Note one additional gotcha is that Make skips blank lines. If having a blank line in the content of your heredoc is important, you need to make sure to indent that line with the appropriate level of whitespace to match the heredoc or Make will eat it and not even pass it to cat!

眼角的笑意。 2024-11-12 00:29:30

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 "$($(@))" | sed -e 's/^[ ]//' >$(@)

如果 .profile 文件不存在,make .profile 将创建一个 .profile 文件。

该解决方案用于应用程序仅在 POSIX shell 环境中使用 GNU Makefile 的情况。该项目不是一个开源项目,平台兼容性是一个问题。

目标是创建一个 Makefile,以方便特定类型工作区的设置和使用。 Makefile 带来了各种简单的资源,而不需要诸如另一个特殊存档等之类的东西。从某种意义上说,它是一个 shell 存档。然后,过程可以说诸如将此 Makefile 放入要工作的文件夹中之类的内容。设置工作空间,输入 makeworkspace,然后要执行 blah,输入 make blah 等。

棘手的是弄清楚要引用什么内容。上面的代码完成了这项工作,并且接近在 Makefile 中指定此处文档的想法。对于一般用途来说这是否是一个好主意是另一个问题。

GNU Makefile can do things like the following. It is ugly, and I won't say you should do it, but I do in certain situations.

.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 "$($(@))" | sed -e 's/^[ ]//' >$(@)

make .profile creates a .profile file if one does not exist.

This solution was used where the application will only use GNU Makefile in a POSIX shell environment. The project is not an open source project where platform compatibility is an issue.

The goal was to create a Makefile that facilitates both setup and use of a particular kind of workspace. The Makefile brings along with it various simple resources without requiring things like another special archive, etc. It is, in a sense, a shell archive. A procedure can then say things like drop this Makefile in the folder to work in. Set up your workspace enter make workspace, then to do blah, enter make blah, etc.

What can get tricky is figuring out what to shell quote. The above does the job and is close to the idea of specifying a here document in the Makefile. Whether it is a good idea for general use is a whole other issue.

怀里藏娇 2024-11-12 00:29:30

这取决于你的决心有多大。最好假设它不会起作用。在 makefile 中编写要启动的 shell 脚本,而不是此处的文档。

失败 1

heredoc:
    cat - <<!
    This is the heredoc.
    !

这会产生:

cat - <<!
This is the heredoc.
make: This: No such file or directory
make: *** [heredoc] Error 1

每行单独执行 - 哎呀。

失败 2

heredoc:
    cat - <<! \
    This is the heredoc.\
    !

生成的:

cat: This: No such file or directory
cat: is: No such file or directory
cat: the: No such file or directory
cat: heredoc.!: No such file or directory
make: *** [heredoc] Error 1

可能有使用特定版本的 make 的方法(例如,我相信 GNU make 可以在单个子 shell 中执行操作的所有命令),但是您必须指定您的可移植性要求。对于常规(例如 POSIX 兼容)make,假设此处的文档不起作用。

It depends on how determined you are. It is best to assume it won't work. Write a shell script to be launched instead of a here document in the makefile.

Fails 1

heredoc:
    cat - <<!
    This is the heredoc.
    !

This produces:

cat - <<!
This is the heredoc.
make: This: No such file or directory
make: *** [heredoc] Error 1

Each line is executed separately - oops.

Fails 2

heredoc:
    cat - <<! \
    This is the heredoc.\
    !

This generated:

cat: This: No such file or directory
cat: is: No such file or directory
cat: the: No such file or directory
cat: heredoc.!: No such file or directory
make: *** [heredoc] Error 1

There may be methods using a specific version of make (GNU make, for example, can execute all commands for an action in a single subshell, I believe), but then you have to specify your portability requirements. For regular (say POSIX-compliant) make, assume here docs do not work.

八巷 2024-11-12 00:29:30

如果您使用的是 Gnu Make,请使用“定义”创建多行文本变量,并使用规则将其回显到文件中。请参见 6.8 定义 https://www.gnu 的多行变量。 org/software/make/manual/make.html#Appending

像这样:

  define myvar
  # line 1\nline 2\nline 3\n#etc\n
  endef

  myfile.txt:
         /bin/echo -e "$(myvar)) >myfile.txt

要创建这个,它有助于使用编辑器,创建您想要的文件,将“\n”附加到每行的末尾,然后将它们全部连接成一个字符串。将其粘贴到您的 makefile 中。

在 Linux 上使用 GNU Make 3.81 进行了测试。

If you're using Gnu Make, use a 'define' to create a multi-line text variable, and a rule to echo it into your file. See 6.8 Defining Multi-Line Variables of https://www.gnu.org/software/make/manual/make.html#Appending

Like this:

  define myvar
  # line 1\nline 2\nline 3\n#etc\n
  endef

  myfile.txt:
         /bin/echo -e "$(myvar)) >myfile.txt

To create this, it helps to use an editor, create the file you want to have, append "\n" to the end of every line, and then join them all into a single string. Paste that into your makefile.

Tested with GNU Make 3.81 on linux.

暮年慕年 2024-11-12 00:29:30

尽可能接近我能得到的heredoc:

Makefile:

switch-version:
    @printf "services:\n\
      myservice:\n\
        build:\n\
          args:\n\
            my_version: $(version)\n\
            some_other_arg: foo\n\
        image: my_image\n\
    " > docker-compose.override.yml

结果:

$ make switch-version version=1.2.3
$ cat docker-compose.override.yml 
services:
  myservice:
    build:
      args:
        my_version: 1.2.3
        some_other_arg: foo
    image: my_image
$ 

As close to a heredoc as I could get:

Makefile:

switch-version:
    @printf "services:\n\
      myservice:\n\
        build:\n\
          args:\n\
            my_version: $(version)\n\
            some_other_arg: foo\n\
        image: my_image\n\
    " > docker-compose.override.yml

Result:

$ make switch-version version=1.2.3
$ cat docker-compose.override.yml 
services:
  myservice:
    build:
      args:
        my_version: 1.2.3
        some_other_arg: foo
    image: my_image
$ 
望笑 2024-11-12 00:29:30
SRCS = (wildcard [a-z]*.c)
EXES = $(SRCS:.c=)
GITIGNOREDS = *.o depend

.gitignore: $(SRCS)
        echo $(EXES) | sed 's/ /\n/g' > $@
        echo $(GITIGNOREDS) | sed 's/ /\n/g' > $@

重要的是 GITIGNOREDS 行。我更喜欢像heredoc这样的东西

GITIGNOREDS = <<EOT
*.o
depend
EOT

,但我也对用空格分隔的文件列表和将空格转换为换行符的sed脚本感到满意。

编辑 根据 Ryan V. Bissell 的建议:使用添加到 gitignore 的单独测试子目录。一切都井然有序。这很简单。

SRCS = (wildcard [a-z]*.c)
EXES = $(SRCS:.c=)
GITIGNOREDS = *.o depend

.gitignore: $(SRCS)
        echo $(EXES) | sed 's/ /\n/g' > $@
        echo $(GITIGNOREDS) | sed 's/ /\n/g' > $@

Important is the GITIGNOREDS line. I would have preferred a heredoc like

GITIGNOREDS = <<EOT
*.o
depend
EOT

but am also happy with a file list, separated by spaces, and a sed script to translate the space into newlines.

Edit As proposed by Ryan V. Bissell: Use a separate test subdirectory which you add to gitignore. And everything falls into place. It's simple.

极度宠爱 2024-11-12 00:29:30

看来没有办法在 Makefile 中创建此处文档。不过,有一个可能的解决方法。使用 echo 发送此处文档数据:

all:
    echo "some text" | myScript.sh

It appears there is no way to do a here-document in Makefile. However, there is a possible workaround. Use echo to send the here-document data:

all:
    echo "some text" | myScript.sh
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文