Create a new function $(file ...)

This commit is contained in:
Paul Smith 2012-01-29 18:12:22 +00:00
parent d6e1c6e6c5
commit fca11f6039
9 changed files with 219 additions and 16 deletions

View File

@ -1,8 +1,13 @@
2012-01-29 Paul Smith <psmith@gnu.org> 2012-01-29 Paul Smith <psmith@gnu.org>
* function.c (func_file): Create a new function, $(file ...)
* doc/make.texi (File Function): Document the $(file ..) function.
* NEWS: Announce it.
* gmk-default.scm (to-string-maybe): Use a more portable way to * gmk-default.scm (to-string-maybe): Use a more portable way to
test for unprintable characters. test for unprintable characters.
* configure.in [GUILE]: Guile 1.6 doesn't have pkg-config * configure.in [GUILE]: Guile 1.6 doesn't have pkg-config
* build_w32.bat: Ditto.
2012-01-28 Eli Zaretskii <eliz@gnu.org> 2012-01-28 Eli Zaretskii <eliz@gnu.org>

14
NEWS
View File

@ -22,6 +22,12 @@ http://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=101&set
multiple consecutive backslash/newlines do not condense into one space. multiple consecutive backslash/newlines do not condense into one space.
* In recipes, a recipe prefix following a backslash-newlines is removed. * In recipes, a recipe prefix following a backslash-newlines is removed.
* New command line option: --trace enables tracing of targets. When enabled
the recipe to be invoked is printed even if it would otherwise be suppressed
by .SILENT or a "@" prefix character. Also before each recipe is run the
makefile name and linenumber where it was defined are shown as well as the
prerequisites that caused the target to be considered out of date.
* New feature: The "job server" capability is now supported on Windows. * New feature: The "job server" capability is now supported on Windows.
Implementation contributed by Troy Runkel <Troy.Runkel@mathworks.com> Implementation contributed by Troy Runkel <Troy.Runkel@mathworks.com>
@ -32,16 +38,12 @@ http://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=101&set
interpreted as shell assignment. Change your assignment to add whitespace interpreted as shell assignment. Change your assignment to add whitespace
between the "!" and "=": "variable! = value" between the "!" and "=": "variable! = value"
* New Feature: GNU Guile integration * New feature: GNU Guile integration
This version of GNU make can be compiled with GNU Guile integration. This version of GNU make can be compiled with GNU Guile integration.
GNU Guile serves as an embedded extension language for make. GNU Guile serves as an embedded extension language for make.
See the "Guile Function" section in the GNU Make manual for details. See the "Guile Function" section in the GNU Make manual for details.
* New command line option: --trace enables tracing of targets. When enabled * New function: $(file ...) writes to a file.
the recipe to be invoked is printed even if it would otherwise be suppressed
by .SILENT or a "@" prefix character. Also before each recipe is run the
makefile name and linenumber where it was defined are shown as well as the
prerequisites that caused the target to be considered out of date.
* On failure, the makefile name and linenumber of the recipe that failed are * On failure, the makefile name and linenumber of the recipe that failed are
shown. shown.

View File

@ -47,12 +47,6 @@ if not ERRORLEVEL 1 set /P GUILECFLAGS= < guile.tmp
pkg-config --libs --static --short-errors %PKGMSC% "guile-1.8" > guile.tmp pkg-config --libs --static --short-errors %PKGMSC% "guile-1.8" > guile.tmp
if not ERRORLEVEL 1 set /P GUILELIBS= < guile.tmp if not ERRORLEVEL 1 set /P GUILELIBS= < guile.tmp
if not "%GUILECFLAGS%" == "" GoTo GuileDone if not "%GUILECFLAGS%" == "" GoTo GuileDone
echo "Checking for Guile 1.6"
pkg-config --cflags --short-errors "guile-1.6" > guile.tmp
if not ERRORLEVEL 1 set /P GUILECFLAGS= < guile.tmp
pkg-config --libs --static --short-errors %PKGMSC% "guile-2.0" > guile.tmp
if not ERRORLEVEL 1 set /P GUILELIBS= < guile.tmp
if not "%GUILECFLAGS%" == "" GoTo GuileDone
echo "No Guile found, building without Guile" echo "No Guile found, building without Guile"
GoTo NoGuile GoTo NoGuile
:NoPkgCfg :NoPkgCfg

View File

@ -180,8 +180,7 @@ AC_ARG_WITH([guile], [AS_HELP_STRING([--with-guile],
AS_IF([test "x$with_guile" != xno], AS_IF([test "x$with_guile" != xno],
[ PKG_CHECK_MODULES([GUILE], [guile-2.0], [have_guile=yes], [ PKG_CHECK_MODULES([GUILE], [guile-2.0], [have_guile=yes],
[PKG_CHECK_MODULES([GUILE], [guile-1.8], [have_guile=yes], [PKG_CHECK_MODULES([GUILE], [guile-1.8], [have_guile=yes],
[PKG_CHECK_MODULES([GUILE], [guile-1.6], [have_guile=yes], [have_guile=no])])
[have_guile=no])])])
]) ])
AS_IF([test "$have_guile" = yes], AS_IF([test "$have_guile" = yes],

View File

@ -265,6 +265,7 @@ Functions for Transforming Text
* File Name Functions:: Functions for manipulating file names. * File Name Functions:: Functions for manipulating file names.
* Conditional Functions:: Functions that implement conditions. * Conditional Functions:: Functions that implement conditions.
* Foreach Function:: Repeat some text with controlled variation. * Foreach Function:: Repeat some text with controlled variation.
* File Function:: Write text to a file.
* Call Function:: Expand a user-defined function. * Call Function:: Expand a user-defined function.
* Value Function:: Return the un-expanded value of a variable. * Value Function:: Return the un-expanded value of a variable.
* Eval Function:: Evaluate the arguments as makefile syntax. * Eval Function:: Evaluate the arguments as makefile syntax.
@ -6563,6 +6564,7 @@ be substituted.
* File Name Functions:: Functions for manipulating file names. * File Name Functions:: Functions for manipulating file names.
* Conditional Functions:: Functions that implement conditions. * Conditional Functions:: Functions that implement conditions.
* Foreach Function:: Repeat some text with controlled variation. * Foreach Function:: Repeat some text with controlled variation.
* File Function:: Write text to a file.
* Call Function:: Expand a user-defined function. * Call Function:: Expand a user-defined function.
* Value Function:: Return the un-expanded value of a variable. * Value Function:: Return the un-expanded value of a variable.
* Eval Function:: Evaluate the arguments as makefile syntax. * Eval Function:: Evaluate the arguments as makefile syntax.
@ -7214,7 +7216,7 @@ the result of the expansion is the expansion of the last argument.
@end table @end table
@node Foreach Function, Call Function, Conditional Functions, Functions @node Foreach Function, File Function, Conditional Functions, Functions
@section The @code{foreach} Function @section The @code{foreach} Function
@findex foreach @findex foreach
@cindex words, iterating over @cindex words, iterating over
@ -7302,7 +7304,69 @@ might be useful if the value of @code{find_files} references the variable
whose name is @samp{Esta-escrito-en-espanol!} (es un nombre bastante largo, whose name is @samp{Esta-escrito-en-espanol!} (es un nombre bastante largo,
no?), but it is more likely to be a mistake. no?), but it is more likely to be a mistake.
@node Call Function, Value Function, Foreach Function, Functions @node File Function, Call Function, Foreach Function, Functions
@section The @code{file} Function
@findex file
@cindex writing to a file
@cindex file, writing to
The @code{file} function allows the makefile to write to a file. Two
modes of writing are supported: overwrite, where the text is written
to the beginning of the file and any existing content is lost, and
append, where the text is written to the end of the file, preserving
the existing content. In all cases the file is created if it does not
exist.
The syntax of the @code{file} function is:
@example
$(file @var{op} @var{filename},@var{text})
@end example
The operator @var{op} can be either @code{>} which indicates overwrite
mode, or @code{>>} which indicates append mode. The @var{filename}
indicates the file to be written to. There may optionally be
whitespace between the operator and the file name.
When the @code{file} function is expanded all its arguments are
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,
a final newline 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
the write operation fails.
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
that can accept arguments from a file as well. Many commands use the
convention that an argument prefixed with an @code{@@} specifies a
file containing more arguments. Then you might write your recipe in
this way:
@example
@group
program: $(OBJECTS)
$(file >$@@.in,$^)
$(CMD) $(CMDFLAGS) @@$@@.in
@@rm $@@.in
@end group
@end example
If the command required each argument to be on a separate line of the
input file, you might write your recipe like this:
@example
@group
program: $(OBJECTS)
$(file >$@@.in,) $(foreach O,$^,$(file >>$@@.in,$O))
$(CMD) $(CMDFLAGS) @@$@@.in
@@rm $@@.in
@end group
@end example
@node Call Function, Value Function, File Function, Functions
@section The @code{call} Function @section The @code{call} Function
@findex call @findex call
@cindex functions, user defined @cindex functions, user defined

View File

@ -2104,6 +2104,45 @@ func_realpath (char *o, char **argv, const char *funcname UNUSED)
return o; return o;
} }
static char *
func_file (char *o, char **argv, const char *funcname UNUSED)
{
char *fn = argv[0];
if (fn[0] == '>')
{
FILE *fp;
const char *mode = "w";
/* We are writing a file. */
++fn;
if (fn[0] == '>')
{
mode = "a";
++fn;
}
fn = next_token (fn);
fp = fopen (fn, mode);
if (fp == NULL)
fatal (reading_file, _("open: %s: %s"), fn, strerror (errno));
else
{
int l = strlen (argv[1]);
int nl = (l == 0 || argv[1][l-1] != '\n');
if (fputs (argv[1], fp) == EOF || (nl && fputc('\n', fp) == EOF))
fatal (reading_file, _("write: %s: %s"), fn, strerror (errno));
fclose (fp);
}
}
else
fatal (reading_file, _("Invalid file operation: %s"), fn);
return o;
}
static char * static char *
func_abspath (char *o, char **argv, const char *funcname UNUSED) func_abspath (char *o, char **argv, const char *funcname UNUSED)
{ {
@ -2191,6 +2230,7 @@ static struct function_table_entry function_table_init[] =
{ STRING_SIZE_TUPLE("and"), 1, 0, 0, func_and}, { STRING_SIZE_TUPLE("and"), 1, 0, 0, func_and},
{ STRING_SIZE_TUPLE("value"), 0, 1, 1, func_value}, { STRING_SIZE_TUPLE("value"), 0, 1, 1, func_value},
{ STRING_SIZE_TUPLE("eval"), 0, 1, 1, func_eval}, { STRING_SIZE_TUPLE("eval"), 0, 1, 1, func_eval},
{ STRING_SIZE_TUPLE("file"), 1, 2, 1, func_file},
#ifdef EXPERIMENTAL #ifdef EXPERIMENTAL
{ STRING_SIZE_TUPLE("eq"), 2, 2, 1, func_eq}, { STRING_SIZE_TUPLE("eq"), 2, 2, 1, func_eq},
{ STRING_SIZE_TUPLE("not"), 0, 1, 1, func_not}, { STRING_SIZE_TUPLE("not"), 0, 1, 1, func_not},

View File

@ -1,3 +1,7 @@
2012-01-29 Paul Smith <psmith@gnu.org>
* scripts/functions/file: Test the new $(file ...) function.
2012-01-12 Paul Smith <psmith@gnu.org> 2012-01-12 Paul Smith <psmith@gnu.org>
* scripts/functions/guile: New regression tests for Guile support. * scripts/functions/guile: New regression tests for Guile support.

View File

@ -0,0 +1,94 @@
# -*-perl-*-
$description = 'Test the $(file ...) function.';
# Test > and >>
run_make_test(q!
define A
a
b
endef
B = c d
$(file >file.out,$(A))
$(foreach L,$(B),$(file >> file.out,$L))
x:;@echo hi; cat file.out
!,
'', "hi\na\nb\nc\nd");
unlink('file.out');
# Test >> to a non-existent file
run_make_test(q!
define A
a
b
endef
$(file >> file.out,$(A))
x:;@cat file.out
!,
'', "a\nb");
unlink('file.out');
# Test > to a read-only file
touch('file.out');
chmod(0444, 'file.out');
# Find the error that will be printed
my $e;
open(my $F, '>', 'file.out') and die "Opened read-only file!\n";
$e = "$!";
run_make_test(q!
define A
a
b
endef
$(file > file.out,$(A))
x:;@cat file.out
!,
'', "#MAKEFILE#:6: *** open: file.out: $e. Stop.",
512);
unlink('file.out');
# Use variables for operator and filename
run_make_test(q!
define A
a
b
endef
OP = >
FN = file.out
$(file $(OP) $(FN),$(A))
x:;@cat file.out
!,
'', "a\nb");
unlink('file.out');
# Don't add newlines if one already exists
run_make_test(q!
define A
a
b
endef
$(file >file.out,$(A))
x:;@cat file.out
!,
'', "a\nb");
unlink('file.out');
# Empty text
run_make_test(q!
$(file >file.out,)
$(file >>file.out,)
x:;@cat file.out
!,
'', "\n\n");
unlink('file.out');
1;

View File

@ -89,6 +89,7 @@ sub toplevel
foreach (# UNIX-specific things foreach (# UNIX-specific things
'TZ', 'TMPDIR', 'HOME', 'USER', 'LOGNAME', 'PATH', 'TZ', 'TMPDIR', 'HOME', 'USER', 'LOGNAME', 'PATH',
'LD_LIBRARY_PATH',
# Purify things # Purify things
'PURIFYOPTIONS', 'PURIFYOPTIONS',
# Windows NT-specific stuff # Windows NT-specific stuff