* function.c (func_file): Support reading from files.

* NEWS: Add information about reading files.
* make.texi (File Function): Describe reading files.
* tests/scripts/functions/file: Test new features for $(file ...)
This commit is contained in:
Paul Smith 2016-03-19 17:26:08 -04:00
parent fc2ddebdae
commit 2b9dd215d5
4 changed files with 127 additions and 29 deletions

4
NEWS
View File

@ -20,6 +20,10 @@ http://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=106&set
successful or not "0" if not successful. The variable value is unset if no successful or not "0" if not successful. The variable value is unset if no
!= or $(shell ...) function has been invoked. != or $(shell ...) function has been invoked.
* The $(file ...) function can now read from a file with $(file <FILE).
The function is expanded to the contents of the file. The contents are
expanded verbatim except that the final newline, if any, is stripped.
* VMS-specific changes: * VMS-specific changes:
* Perl test harness now works. * Perl test harness now works.

View File

@ -7521,13 +7521,22 @@ no?), but it is more likely to be a mistake.
@findex file @findex file
@cindex writing to a file @cindex writing to a file
@cindex file, writing to @cindex file, writing to
@cindex reading from a file
@cindex file, reading from
The @code{file} function allows the makefile to write to a file. Two The @code{file} function allows the makefile to write to or read from
modes of writing are supported: overwrite, where the text is written a file. Two modes of writing are supported: overwrite, where the text
to the beginning of the file and any existing content is lost, and is written to the beginning of the file and any existing content is
append, where the text is written to the end of the file, preserving lost, and append, where the text is written to the end of the file,
the existing content. In all cases the file is created if it does not preserving the existing content. In both cases the file is created if
exist. it does not exist. It is a fatal error if the file cannot be opened
for writing, or if the write operation fails. The @code{file}
function expands to the empty string when writing to a file.
When reading from a file, the @code{file} function expands to the
verbatim contents of the file, except that the final newline (if there
is one) will be stripped. Attempting to read from a non-existent file
expands to the empty string.
The syntax of the @code{file} function is: The syntax of the @code{file} function is:
@ -7535,21 +7544,23 @@ The syntax of the @code{file} function is:
$(file @var{op} @var{filename}[,@var{text}]) $(file @var{op} @var{filename}[,@var{text}])
@end example @end example
The operator @var{op} can be either @code{>} which indicates overwrite When the @code{file} function is evaluated all its arguments are
mode, or @code{>>} which indicates append mode. The @var{filename} expanded first, then the file indicated by @var{filename} will be
indicates the file to be written to. There may optionally be opened in the mode described by @var{op}.
The operator @var{op} can be @code{>} to indicate the file will be
overwritten with new content, @code{>>} to indicate the current
contents of the file will be appended to, or @code{<} to indicate the
contents of the file will be read in. The @var{filename} specifies
the file to be written to or read from. There may optionally be
whitespace between the operator and the file name. whitespace between the operator and the file name.
When the @code{file} function is expanded all its arguments are When reading files, it is an error to provide a @var{text} value.
expanded first, then the file indicated by @var{filename} will be
opened in the mode described by @var{op}. Finally @var{text} will be
written to the file. If @var{text} does not already end in a newline,
even if empty, a final newline will be written. If the @var{text}
argument is not given, nothing will be written. The result of
evaluating the @code{file} function is always the empty string.
It is a fatal error if the file cannot be opened for writing, or if When writing files, @var{text} will be written to the file. If
the write operation fails. @var{text} does not already end in a newline a final newline will be
written (even if @var{text} is the empty string). If the @var{text}
argument is not given at all, nothing will be written.
For example, the @code{file} function can be useful if your build For example, the @code{file} function can be useful if your build
system has a limited command line size and your recipe runs a command system has a limited command line size and your recipe runs a command

View File

@ -2208,27 +2208,67 @@ func_file (char *o, char **argv, const char *funcname UNUSED)
} }
fn = next_token (fn); fn = next_token (fn);
fp = fopen (fn, mode); if (fn[0] == '\0')
O (fatal, *expanding_var, _("file: missing filename"));
ENULLLOOP (fp, fopen (fn, mode));
if (fp == NULL) if (fp == NULL)
{ OSS (fatal, reading_file, _("open: %s: %s"), fn, strerror (errno));
const char *err = strerror (errno);
OSS (fatal, reading_file, _("open: %s: %s"), fn, err);
}
if (argv[1]) if (argv[1])
{ {
int l = strlen (argv[1]); int l = strlen (argv[1]);
int nl = l == 0 || argv[1][l-1] != '\n'; int nl = l == 0 || argv[1][l-1] != '\n';
if (fputs (argv[1], fp) == EOF || (nl && fputc ('\n', fp) == EOF)) if (fputs (argv[1], fp) == EOF || (nl && fputc ('\n', fp) == EOF))
{ OSS (fatal, reading_file, _("write: %s: %s"), fn, strerror (errno));
const char *err = strerror (errno);
OSS (fatal, reading_file, _("write: %s: %s"), fn, err);
}
} }
fclose (fp); if (fclose (fp))
OSS (fatal, reading_file, _("close: %s: %s"), fn, strerror (errno));
}
else if (fn[0] == '<')
{
char *preo = o;
FILE *fp;
fn = next_token (++fn);
if (fn[0] == '\0')
O (fatal, *expanding_var, _("file: missing filename"));
if (argv[1])
O (fatal, *expanding_var, _("file: too many arguments"));
ENULLLOOP (fp, fopen (fn, "r"));
if (fp == NULL)
{
if (errno == ENOENT)
return o;
OSS (fatal, reading_file, _("open: %s: %s"), fn, strerror (errno));
}
while (1)
{
char buf[1024];
size_t l = fread (buf, 1, sizeof (buf), fp);
if (l > 0)
o = variable_buffer_output (o, buf, l);
if (ferror (fp))
if (errno != EINTR)
OSS (fatal, reading_file, _("read: %s: %s"), fn, strerror (errno));
if (feof (fp))
break;
}
if (fclose (fp))
OSS (fatal, reading_file, _("close: %s: %s"), fn, strerror (errno));
/* Remove trailing newline. */
if (o > preo && o[-1] == '\n')
if (--o > preo && o[-1] == '\r')
--o;
} }
else else
OS (fatal, reading_file, _("Invalid file operation: %s"), fn); OS (fatal, *expanding_var, _("file: invalid file operation: %s"), fn);
return o; return o;
} }

View File

@ -115,4 +115,47 @@ x:;@cat file.out
unlink('file.out'); unlink('file.out');
# Reading files
run_make_test(q!
$(file >file.out,A = foo)
X1 := $(file <file.out)
$(file >>file.out,B = bar)
$(eval $(file <file.out))
x:;@echo '$(X1)'; echo '$(A)'; echo '$(B)'
!,
'', "A = foo\nfoo\nbar\n");
unlink('file.out');
# Reading from non-existent file
run_make_test(q!
X1 := $(file <file.out)
x:;@echo '$(X1)';
!,
'', "\n");
# Extra arguments in read mode
run_make_test(q!
X1 := $(file <file.out,foo)
x:;@echo '$(X1)';
!,
'', "#MAKEFILE#:2: *** file: too many arguments. Stop.\n", 512);
# Missing filename
run_make_test('$(file >)', '',
"#MAKEFILE#:1: *** file: missing filename. Stop.\n", 512);
run_make_test('$(file >>)', '',
"#MAKEFILE#:1: *** file: missing filename. Stop.\n", 512);
run_make_test('$(file <)', '',
"#MAKEFILE#:1: *** file: missing filename. Stop.\n", 512);
# Bad call
run_make_test('$(file foo)', '',
"#MAKEFILE#:1: *** file: invalid file operation: foo. Stop.\n", 512);
1; 1;