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 .gitignore
to 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?
Here document (Heredoc) is an input or file stream literal that is treated as a special block of code. This block of code will be passed to a command for processing. Heredoc originates in UNIX shells and can be found in popular Linux shells like sh, tcsh, ksh, bash, zsh, csh.
To use here-document in any bash script, you have to use the symbol << followed by any delimiting identifier after any bash command and close the HereDoc by using the same delimiting identifier at the end of the text.
Recipes in makefile rules must start with a tab (per definition). If a single tab (interpreted as 8 spaces wide) is not enough to put the recipe clearly indented (meaning at least 4 spaces difference) from the code around it, use additional tabs.
The most common syntax for here documents, originating in Unix shells, is << followed by a delimiting identifier (often the word EOF or END), followed, starting on the next line, by the text to be quoted, and then closed by the same delimiting identifier on its own line.
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.
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:
-
or @
to suppress output or ignore errors only work on the first line of all recipes and take effect for all lines following.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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With