This is a new extension to make. I don't think it will hinder portability
all that much, since != is already an expansion.
VAR != cmd
executes cmd on the spot, and replaces the output into VAR. This is a
convenient feature, but it is rather expensive, since each such VAR
will require the execution of an external shell.
In many cases, it is superior to evaluating stuff through the shell
using `` (or $() ), because make will echo the variable value, so you
actually see the result. e.g.,
SYSDIR != cd ${.CURDIR}/../../../..; pwd
CONFDIR != cd ${.CURDIR}/../../conf; pwd
config:
config -b ${.OBJDIR} -s ${SYSDIR} ${CONFDIR}/${.CURDIR:T}
will echo a line with everything fully expanded.
I propose that
VAR !!= cmd
had almost the same semantics:
- still expand the line on-the-spot (no way to determine where to expand
it otherwise)
- only run it through the shell the first time the variable value is
needed.
Making things "lazy" all the time would also be possible, but it would
change the semantics of things (e.g., you may do tricks like
VAR != some cmd with side effects
because you want the side effects, and then that would break.
I'm now looking for okays...
Index: make.1
===================================================================
RCS file: /cvs/src/usr.bin/make/make.1,v
retrieving revision 1.120
diff -u -p -r1.120 make.1
--- make.1 13 Mar 2015 19:58:41 -0000 1.120
+++ make.1 20 Oct 2016 13:16:05 -0000
@@ -532,11 +532,23 @@ Normally, expansion is not done until th
.It Ic \&!=
Expand the value and pass it to the shell for execution and assign
the result to the variable.
-Any newlines in the result are replaced with spaces
+newlines in the result are also replaced with spaces
.Po
.Bx
extension
.Pc .
+.It Ic \&!!=
+Expand the value on the spot, pass it to the shell for execution only when
+the value is needed, and assign the result to the variable.
+.Pp
+This is almost identical to
+.Ic \&!=
+except that a shell is only run when the variable value is needed.
+Any newlines in the result are replaced with spaces
+.Po
+.Ox
+extension
+.Pc .
.El
.Pp
Any whitespace before the assigned
@@ -557,6 +569,12 @@ and put its output into
if
.Va A
is not yet defined.
+.Pp
+Combinations that do not make sense, such as
+.Bd -literal -offset indent
+A +!!= cmd
+.Ed
+will not work.
.Pp
Variables are expanded by surrounding the variable name with either
curly braces
Index: parse.c
===================================================================
RCS file: /cvs/src/usr.bin/make/parse.c,v
retrieving revision 1.116
diff -u -p -r1.116 parse.c
--- parse.c 13 May 2016 12:18:11 -0000 1.116
+++ parse.c 20 Oct 2016 13:16:05 -0000
@@ -1322,7 +1322,7 @@ handle_poison(const char *line)
line);
return false;
} else {
- Var_MarkPoisoned(name, ename, type);
+ Var_Mark(name, ename, type);
return true;
}
}
Index: parsevar.c
===================================================================
RCS file: /cvs/src/usr.bin/make/parsevar.c,v
retrieving revision 1.15
diff -u -p -r1.15 parsevar.c
--- parsevar.c 22 Nov 2013 15:47:35 -0000 1.15
+++ parsevar.c 20 Oct 2016 13:16:05 -0000
@@ -79,6 +79,8 @@ parse_variable_assignment(const char *li
#define VAR_APPEND 2
#define VAR_SHELL 4
#define VAR_OPT 8
+#define VAR_LAZYSHELL 16
+#define VAR_SUNSHELL 32
int type;
struct Name name;
@@ -90,11 +92,14 @@ parse_variable_assignment(const char *li
type = VAR_NORMAL;
+ /* double operators (except for :) are forbidden */
+ /* OPT and APPEND don't match */
+ /* APPEND and LAZYSHELL can't really work */
while (*arg != '=') {
/* Check operator type. */
switch (*arg++) {
case '+':
- if (type & (VAR_OPT|VAR_APPEND))
+ if (type & (VAR_OPT|VAR_LAZYSHELL|VAR_APPEND))
type = VAR_INVALID;
else
type |= VAR_APPEND;
@@ -110,7 +115,7 @@ parse_variable_assignment(const char *li
case ':':
if (FEATURES(FEATURE_SUNSHCMD) &&
strncmp(arg, "sh", 2) == 0) {
- type = VAR_SHELL;
+ type = VAR_SUNSHELL;
arg += 2;
while (*arg != '=' && *arg != '\0')
arg++;
@@ -124,6 +129,11 @@ parse_variable_assignment(const char *li
case '!':
if (type & VAR_SHELL)
+ if (type & (VAR_APPEND))
+ type = VAR_INVALID;
+ else
+ type = VAR_LAZYSHELL;
+ else if (type & (VAR_LAZYSHELL|VAR_SUNSHELL))
type = VAR_INVALID;
else
type |= VAR_SHELL;
@@ -147,7 +157,7 @@ parse_variable_assignment(const char *li
VarName_Free(&name);
return true;
}
- if (type & VAR_SHELL) {
+ if (type & (VAR_SHELL|VAR_SUNSHELL)) {
char *err;
if (strchr(arg, '$') != NULL) {
@@ -164,6 +174,13 @@ parse_variable_assignment(const char *li
Parse_Error(PARSE_WARNING, err, arg);
arg = res1;
}
+ if (type & VAR_LAZYSHELL) {
+ if (strchr(arg, '$') != NULL) {
+ /* There's a dollar sign in the command, so perform
+ * variable expansion on the whole thing. */
+ arg = Var_Subst(arg, NULL, true);
+ }
+ }
if (type & VAR_SUBST) {
/*
* Allow variables in the old value to be undefined, but leave
@@ -195,6 +212,8 @@ parse_variable_assignment(const char *li
Var_Appendi_with_ctxt(name.s, name.e, arg, ctxt);
else
Var_Seti_with_ctxt(name.s, name.e, arg, ctxt);
+ if (type & VAR_LAZYSHELL)
+ Var_Mark(name.s, name.e, VAR_EXEC_LATER);
VarName_Free(&name);
free(res2);
Index: var.c
===================================================================
RCS file: /cvs/src/usr.bin/make/var.c,v
retrieving revision 1.99
diff -u -p -r1.99 var.c
--- var.c 27 Sep 2015 16:58:16 -0000 1.99
+++ var.c 20 Oct 2016 13:16:05 -0000
@@ -72,6 +72,7 @@
#include "config.h"
#include "defines.h"
#include "buf.h"
+#include "cmd_exec.h"
#include "stats.h"
#include "pathnames.h"
#include "varmodifiers.h"
@@ -235,7 +236,10 @@ static void fill_from_env(Var *);
static Var *create_var(const char *, const char *);
static void var_set_initial_value(Var *, const char *);
static void var_set_value(Var *, const char *);
-#define var_get_value(v) Buf_Retrieve(&((v)->val))
+#define var_get_value(v) ((v)->flags & VAR_EXEC_LATER ? \
+ var_exec_cmd(v) : \
+ Buf_Retrieve(&((v)->val)))
+static char *var_exec_cmd(Var *);
static void var_append_value(Var *, const char *);
static void poison_check(Var *);
static void var_set_append(const char *, const char *, const char *, int,
bool);
@@ -524,10 +528,10 @@ find_global_var(const char *name, const
return v;
}
-/* mark variable as poisoned, in a given setup.
+/* mark variable with special flags, in a given setup.
*/
void
-Var_MarkPoisoned(const char *name, const char *ename, unsigned int type)
+Var_Mark(const char *name, const char *ename, unsigned int type)
{
Var *v;
uint32_t k;
@@ -664,6 +668,21 @@ Var_Appendi_with_ctxt(const char *name,
int ctxt)
{
var_set_append(name, ename, val, ctxt, true);
+}
+
+static char *
+var_exec_cmd(Var *v)
+{
+ char *arg = Buf_Retrieve(&(v->val));
+ char *err;
+ char *res1;
+ res1 = Cmd_Exec(arg, &err);
+ if (err)
+ Parse_Error(PARSE_WARNING, err, arg);
+ var_set_value(v, res1);
+ free(res1);
+ v->flags &= ~VAR_EXEC_LATER;
+ return Buf_Retrieve(&(v->val));
}
/* XXX different semantics for Var_Valuei() and Var_Definedi():
Index: var.h
===================================================================
RCS file: /cvs/src/usr.bin/make/var.h,v
retrieving revision 1.17
diff -u -p -r1.17 var.h
--- var.h 6 Jan 2014 12:08:18 -0000 1.17
+++ var.h 20 Oct 2016 13:16:05 -0000
@@ -161,6 +161,7 @@ extern bool errorIsOkay;
#define POISON_NORMAL 64
#define POISON_EMPTY 128
#define POISON_NOT_DEFINED 256
+#define VAR_EXEC_LATER 512
-extern void Var_MarkPoisoned(const char *, const char *, unsigned int);
+extern void Var_Mark(const char *, const char *, unsigned int);
#endif