From fca11f60390cf607f68b497c3909b1fb40251070 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Sun, 29 Jan 2012 18:12:22 +0000 Subject: Create a new function $(file ...) --- ChangeLog | 5 +++ NEWS | 14 ++++--- build_w32.bat | 6 --- configure.in | 3 +- doc/make.texi | 68 +++++++++++++++++++++++++++++++- function.c | 40 +++++++++++++++++++ tests/ChangeLog | 4 ++ tests/scripts/functions/file | 94 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_driver.pl | 1 + 9 files changed, 219 insertions(+), 16 deletions(-) create mode 100644 tests/scripts/functions/file diff --git a/ChangeLog b/ChangeLog index 3c52a4a..facb4cc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,13 @@ 2012-01-29 Paul Smith + * 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 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 @@ -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 @@ -2104,6 +2104,45 @@ func_realpath (char *o, char **argv, const char *funcname UNUSED) 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 * 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("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 + + * scripts/functions/file: Test the new $(file ...) function. + 2012-01-12 Paul Smith * 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 -- cgit v1.2.3