summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Smith <psmith@gnu.org>2012-01-29 18:12:22 +0000
committerPaul Smith <psmith@gnu.org>2012-01-29 18:12:22 +0000
commitfca11f60390cf607f68b497c3909b1fb40251070 (patch)
treee8541f1110fab2d1ffc0c4b226405d85ad43c0ad
parentd6e1c6e6c5961b8e7dee7ff1fc7d1792ab0fb4f8 (diff)
downloadgunmake-fca11f60390cf607f68b497c3909b1fb40251070.tar.gz
Create a new function $(file ...)
-rw-r--r--ChangeLog5
-rw-r--r--NEWS14
-rw-r--r--build_w32.bat6
-rw-r--r--configure.in3
-rw-r--r--doc/make.texi68
-rw-r--r--function.c40
-rw-r--r--tests/ChangeLog4
-rw-r--r--tests/scripts/functions/file94
-rw-r--r--tests/test_driver.pl1
9 files changed, 219 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 3c52a4a..facb4cc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,13 @@
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
test for unprintable characters.
* configure.in [GUILE]: Guile 1.6 doesn't have pkg-config
+ * build_w32.bat: Ditto.
2012-01-28 Eli Zaretskii <eliz@gnu.org>
diff --git a/NEWS b/NEWS
index 4c50086..20b8704 100644
--- a/NEWS
+++ b/NEWS
@@ -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.
* 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.
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
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.
GNU Guile serves as an embedded extension language for make.
See the "Guile Function" section in the GNU Make manual for details.
-* 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 function: $(file ...) writes to a file.
* On failure, the makefile name and linenumber of the recipe that failed are
shown.
diff --git a/build_w32.bat b/build_w32.bat
index 57aa037..6fbc67a 100644
--- a/build_w32.bat
+++ b/build_w32.bat
@@ -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
if not ERRORLEVEL 1 set /P GUILELIBS= < guile.tmp
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"
GoTo NoGuile
:NoPkgCfg
diff --git a/configure.in b/configure.in
index a2050c7..90b55ea 100644
--- a/configure.in
+++ b/configure.in
@@ -180,8 +180,7 @@ AC_ARG_WITH([guile], [AS_HELP_STRING([--with-guile],
AS_IF([test "x$with_guile" != xno],
[ 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.6], [have_guile=yes],
- [have_guile=no])])])
+ [have_guile=no])])
])
AS_IF([test "$have_guile" = yes],
diff --git a/doc/make.texi b/doc/make.texi
index 63390c8..9b8faee 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -265,6 +265,7 @@ Functions for Transforming Text
* File Name Functions:: Functions for manipulating file names.
* Conditional Functions:: Functions that implement conditions.
* Foreach Function:: Repeat some text with controlled variation.
+* File Function:: Write text to a file.
* Call Function:: Expand a user-defined function.
* Value Function:: Return the un-expanded value of a variable.
* Eval Function:: Evaluate the arguments as makefile syntax.
@@ -6563,6 +6564,7 @@ be substituted.
* File Name Functions:: Functions for manipulating file names.
* Conditional Functions:: Functions that implement conditions.
* Foreach Function:: Repeat some text with controlled variation.
+* File Function:: Write text to a file.
* Call Function:: Expand a user-defined function.
* Value Function:: Return the un-expanded value of a variable.
* 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
-@node Foreach Function, Call Function, Conditional Functions, Functions
+@node Foreach Function, File Function, Conditional Functions, Functions
@section The @code{foreach} Function
@findex foreach
@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,
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
@findex call
@cindex functions, user defined
diff --git a/function.c b/function.c
index 29b106f..5acbb76 100644
--- a/function.c
+++ b/function.c
@@ -2105,6 +2105,45 @@ func_realpath (char *o, char **argv, const char *funcname UNUSED)
}
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 *
func_abspath (char *o, char **argv, const char *funcname UNUSED)
{
/* Expand the argument. */
@@ -2191,6 +2230,7 @@ static struct function_table_entry function_table_init[] =
{ STRING_SIZE_TUPLE("and"), 1, 0, 0, func_and},
{ STRING_SIZE_TUPLE("value"), 0, 1, 1, func_value},
{ STRING_SIZE_TUPLE("eval"), 0, 1, 1, func_eval},
+ { STRING_SIZE_TUPLE("file"), 1, 2, 1, func_file},
#ifdef EXPERIMENTAL
{ STRING_SIZE_TUPLE("eq"), 2, 2, 1, func_eq},
{ STRING_SIZE_TUPLE("not"), 0, 1, 1, func_not},
diff --git a/tests/ChangeLog b/tests/ChangeLog
index f603fc8..3e0643c 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -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>
* scripts/functions/guile: New regression tests for Guile support.
diff --git a/tests/scripts/functions/file b/tests/scripts/functions/file
new file mode 100644
index 0000000..b994af8
--- /dev/null
+++ b/tests/scripts/functions/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;
diff --git a/tests/test_driver.pl b/tests/test_driver.pl
index b57d936..4ec3a11 100644
--- a/tests/test_driver.pl
+++ b/tests/test_driver.pl
@@ -89,6 +89,7 @@ sub toplevel
foreach (# UNIX-specific things
'TZ', 'TMPDIR', 'HOME', 'USER', 'LOGNAME', 'PATH',
+ 'LD_LIBRARY_PATH',
# Purify things
'PURIFYOPTIONS',
# Windows NT-specific stuff