Subject: new feature for src/usr.bin/make: read-only variables
To: None <tech-userlevel@NetBSD.org, tech-pkg@NetBSD.org>
From: Roland Illig <rillig@NetBSD.org>
List: tech-userlevel
Date: 09/02/2005 11:37:25
This is a multi-part message in MIME format.
--------------080503080400050703050705
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
This patch adds a new feature to make(1). Variables can be marked
read-only, so there is no chance of overwriting them accidentally. The
intended usage is:
.BEGIN_READONLY:
CFLAGS.NetBSD= -Wall
CFLAGS.SunOS= -O
CFLAGS.IRIX= -v
.END_READONLY:
PROG= foobar
.READONLY: PROG
This patch is mostly useful for pkgsrc, as pkgsrc contains really much
code in Makefiles with more than 1000 global variables.
Roland
--------------080503080400050703050705
Content-Type: text/plain;
name="make-readonly-variables.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="make-readonly-variables.patch"
Index: nonints.h
===================================================================
RCS file: /cvsroot/src/usr.bin/make/nonints.h,v
retrieving revision 1.34
diff -u -p -r1.34 nonints.h
--- nonints.h 8 May 2005 00:38:47 -0000 1.34
+++ nonints.h 2 Sep 2005 09:35:36 -0000
@@ -152,6 +152,8 @@ void Suff_AddSuffix(char *, GNode **);
Lst Suff_GetPath(char *);
void Suff_DoPaths(void);
void Suff_AddInclude(char *);
+void Var_Make_Readonly __P((char *, GNode *));
+void Var_Set_Readonly_Mode __P((int));
void Suff_AddLib(char *);
void Suff_FindDeps(GNode *);
Lst Suff_FindPath(GNode *);
Index: parse.c
===================================================================
RCS file: /cvsroot/src/usr.bin/make/parse.c,v
retrieving revision 1.106
diff -u -p -r1.106 parse.c
--- parse.c 9 Aug 2005 21:36:42 -0000 1.106
+++ parse.c 2 Sep 2005 09:35:36 -0000
@@ -186,8 +186,10 @@ Lst defIncPath; /* default dire
*/
typedef enum {
Begin, /* .BEGIN */
+ Begin_Readonly, /* .BEGIN_READONLY */
Default, /* .DEFAULT */
End, /* .END */
+ End_Readonly, /* .END_READONLY */
Ignore, /* .IGNORE */
Includes, /* .INCLUDES */
Interrupt, /* .INTERRUPT */
@@ -209,6 +211,7 @@ typedef enum {
Posix, /* .POSIX */
#endif
Precious, /* .PRECIOUS */
+ Readonly, /* .READONLY */
ExShell, /* .SHELL */
Silent, /* .SILENT */
SingleShell, /* .SINGLESHELL */
@@ -241,8 +244,10 @@ static struct {
int op; /* Operator when used as a source */
} parseKeywords[] = {
{ ".BEGIN", Begin, 0 },
+{ ".BEGIN_READONLY", Begin_Readonly, 0 },
{ ".DEFAULT", Default, 0 },
{ ".END", End, 0 },
+{ ".END_READONLY", End_Readonly, 0 },
{ ".EXEC", Attribute, OP_EXEC },
{ ".IGNORE", Ignore, OP_IGNORE },
{ ".INCLUDES", Includes, 0 },
@@ -270,6 +275,7 @@ static struct {
{ ".POSIX", Posix, 0 },
#endif
{ ".PRECIOUS", Precious, OP_PRECIOUS },
+{ ".READONLY", Readonly, 0 },
{ ".RECURSIVE", Attribute, OP_MAKE },
{ ".SHELL", ExShell, 0 },
{ ".SILENT", Silent, OP_SILENT },
@@ -1271,6 +1277,12 @@ ParseDoDependency(char *line)
*/
if (!*line) {
switch (specType) {
+ case Begin_Readonly:
+ Var_Set_Readonly_Mode(1);
+ break;
+ case End_Readonly:
+ Var_Set_Readonly_Mode(0);
+ break;
case Suffixes:
Suff_ClearSuffixes();
break;
@@ -1318,7 +1330,8 @@ ParseDoDependency(char *line)
*/
if ((specType == Suffixes) || (specType == ExPath) ||
(specType == Includes) || (specType == Libs) ||
- (specType == Null) || (specType == ExObjdir))
+ (specType == Null) || (specType == ExObjdir) ||
+ (specType == Readonly))
{
while (*line) {
/*
@@ -1372,6 +1385,9 @@ ParseDoDependency(char *line)
case ExObjdir:
Main_SetObjdir(line);
break;
+ case Readonly:
+ Var_Make_Readonly (line, VAR_GLOBAL);
+ break;
default:
break;
}
Index: var.c
===================================================================
RCS file: /cvsroot/src/usr.bin/make/var.c,v
retrieving revision 1.100
diff -u -p -r1.100 var.c
--- var.c 27 Aug 2005 08:04:26 -0000 1.100
+++ var.c 2 Sep 2005 09:35:38 -0000
@@ -167,6 +167,11 @@ static char varNoError[] = "";
GNode *VAR_GLOBAL; /* variables from the makefile */
GNode *VAR_CMD; /* variables defined on the command-line */
+/* When non-zero, all assignments mark the destination variable read-only,
+ * so it cannot be modified again.
+ */
+static int var_readonly_mode = 0;
+
#define FIND_CMD 0x1 /* look in VAR_CMD when searching */
#define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */
#define FIND_ENV 0x4 /* look in the environment also */
@@ -185,6 +190,7 @@ typedef struct Var {
#define VAR_KEEP 8 /* Variable is VAR_JUNK, but we found
* a use for it in some modifier and
* the value is therefore valid */
+#define VAR_READONLY 16
} Var;
@@ -448,8 +454,13 @@ VarAdd(const char *name, const char *val
h = Hash_CreateEntry(&ctxt->context, name, NULL);
Hash_SetValue(h, v);
v->name = h->name;
+
+ if (var_readonly_mode)
+ v->flags |= VAR_READONLY;
+
if (DEBUG(VAR)) {
- printf("%s:%s = %s\n", ctxt->name, name, val);
+ printf("%s:%s = %s%s\n", ctxt->name, name, val,
+ (v->flags & VAR_READONLY) ? " (read-only)" : "");
}
}
@@ -479,6 +490,9 @@ Var_Delete(const char *name, GNode *ctxt
Var *v;
v = (Var *)Hash_GetValue(ln);
+ if (v->flags & VAR_READONLY) {
+ Parse_Error(PARSE_FATAL, "%s: Readonly variables cannot be deleted.", v->name);
+ }
if (v->name != ln->name)
free(v->name);
Hash_DeleteEntry(&ctxt->context, ln);
@@ -487,6 +501,25 @@ Var_Delete(const char *name, GNode *ctxt
}
}
+void
+Var_Make_Readonly(char *name, GNode *ctxt)
+{
+ Hash_Entry *ln;
+ Var *v;
+
+ if (DEBUG(VAR)) {
+ printf("%s:make_readonly %s\n", ctxt->name, name);
+ }
+
+ ln = Hash_FindEntry(&ctxt->context, name);
+ if (ln == NULL) {
+ Parse_Error(PARSE_WARNING, ".readonly on undefined variables has no effect.");
+ } else {
+ v = (Var *)Hash_GetValue(ln);
+ v->flags |= VAR_READONLY;
+ }
+}
+
/*-
*-----------------------------------------------------------------------
* Var_Set --
@@ -531,12 +564,18 @@ Var_Set(const char *name, const char *va
v = VarFind(name, ctxt, 0);
if (v == (Var *)NIL) {
VarAdd(name, val, ctxt);
+ } else if (v->flags & VAR_READONLY) {
+ Parse_Error(PARSE_FATAL, "%s: Readonly variables cannot be modified.", v->name);
} else {
Buf_Discard(v->val, Buf_Size(v->val));
Buf_AddBytes(v->val, strlen(val), (const Byte *)val);
+ if (var_readonly_mode)
+ v->flags |= VAR_READONLY;
+
if (DEBUG(VAR)) {
- printf("%s:%s = %s\n", ctxt->name, name, val);
+ printf("%s:%s = %s%s\n", ctxt->name, name, val,
+ (v->flags & VAR_READONLY) ? " (read-only)" : "");
}
}
/*
@@ -603,13 +642,19 @@ Var_Append(const char *name, const char
if (v == (Var *)NIL) {
VarAdd(name, val, ctxt);
+ } else if (v->flags & VAR_READONLY) {
+ Parse_Error(PARSE_FATAL, "%s: Readonly variables cannot be modified.", v->name);
} else {
Buf_AddByte(v->val, (Byte)' ');
Buf_AddBytes(v->val, strlen(val), (const Byte *)val);
+ if (var_readonly_mode)
+ v->flags |= VAR_READONLY;
+
if (DEBUG(VAR)) {
- printf("%s:%s = %s\n", ctxt->name, name,
- (char *)Buf_GetAll(v->val, NULL));
+ printf("%s:%s = %s%s\n", ctxt->name, name,
+ (char *) Buf_GetAll(v->val, (int *)NULL),
+ (v->flags & VAR_READONLY) ? " (read-only)" : "");
}
if (v->flags & VAR_FROM_ENV) {
@@ -3522,3 +3567,9 @@ Var_Dump(GNode *ctxt)
VarPrintVar(Hash_GetValue(h));
}
}
+
+void
+Var_Set_Readonly_Mode(int onoff)
+{
+ var_readonly_mode = onoff;
+}
--------------080503080400050703050705--