diff --git a/dep.h b/dep.h index 10bf947e..1cd6cb0b 100644 --- a/dep.h +++ b/dep.h @@ -14,9 +14,21 @@ A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ + +/* Structure used in chains of names, for parsing and globbing. */ + +#define NAMESEQ(_t) \ + _t *next; \ + const char *name + +struct nameseq + { + NAMESEQ (struct nameseq); + }; + /* Flag bits for the second argument to 'read_makefile'. - These flags are saved in the 'changed' field of each - 'struct dep' in the chain returned by 'read_all_makefiles'. */ + These flags are saved in the 'flags' field of each + 'struct goaldep' in the chain returned by 'read_all_makefiles'. */ #define RM_NO_DEFAULT_GOAL (1 << 0) /* Do not set default goal. */ #define RM_INCLUDED (1 << 1) /* Search makefile search path. */ @@ -25,34 +37,37 @@ this program. If not, see . */ #define RM_NOFLAG 0 /* Structure representing one dependency of a file. - Each struct file's 'deps' points to a chain of these, - chained through the 'next'. 'stem' is the stem for this - dep line of static pattern rule or NULL. + Each struct file's 'deps' points to a chain of these, through 'next'. + 'stem' is the stem for this dep line of static pattern rule or NULL. */ - Note that the first two words of this match a struct nameseq. */ +#define DEP(_t) \ + NAMESEQ (_t); \ + struct file *file; \ + const char *stem; \ + unsigned short flags : 8; \ + unsigned short changed : 1; \ + unsigned short ignore_mtime : 1; \ + unsigned short staticpattern : 1; \ + unsigned short need_2nd_expansion : 1 struct dep { - struct dep *next; - const char *name; - const char *stem; - struct file *file; - unsigned int changed : 8; - unsigned int ignore_mtime : 1; - unsigned int staticpattern : 1; - unsigned int need_2nd_expansion : 1; - unsigned int dontcare : 1; + DEP (struct dep); }; +/* Structure representing one goal. + The goals to be built constitute a chain of these, chained through 'next'. + 'stem' is not used, but it's simpler to include and ignore it. */ -/* Structure used in chains of names, for parsing and globbing. */ - -struct nameseq +struct goaldep { - struct nameseq *next; - const char *name; + DEP (struct goaldep); + unsigned short error; + gmk_floc floc; }; +/* Options for parsing lists of filenames. */ + #define PARSEFS_NONE 0x0000 #define PARSEFS_NOSTRIP 0x0001 #define PARSEFS_NOAR 0x0002 @@ -78,15 +93,40 @@ char *tilde_expand (const char *name); struct nameseq *ar_glob (const char *arname, const char *member_pattern, unsigned int size); #endif -#define dep_name(d) ((d)->name == 0 ? (d)->file->name : (d)->name) +#define dep_name(d) ((d)->name ? (d)->name : (d)->file->name) -#define alloc_dep() (xcalloc (sizeof (struct dep))) -#define free_ns(_n) free (_n) -#define free_dep(_d) free_ns (_d) +#define alloc_seq_elt(_t) xcalloc (sizeof (_t)) +void free_ns_chain (struct nameseq *n); + +#if defined(MAKE_MAINTAINER_MODE) && defined(__GNUC__) +/* Use inline to get real type-checking. */ +#define SI static inline +SI struct nameseq *alloc_ns() { return alloc_seq_elt (struct nameseq); } +SI struct dep *alloc_dep() { return alloc_seq_elt (struct dep); } +SI struct goaldep *alloc_goaldep() { return alloc_seq_elt (struct goaldep); } + +SI void free_ns(struct nameseq *n) { free (n); } +SI void free_dep(struct dep *d) { free_ns ((struct nameseq *)d); } +SI void free_goaldep(struct goaldep *g) { free_dep ((struct dep *)g); } + +SI void free_dep_chain(struct dep *d) { free_ns_chain((struct nameseq *)d); } +SI void free_goal_chain(struct goaldep *g) { free_dep_chain((struct dep *)g); } +#else +# define alloc_ns() alloc_seq_elt (struct nameseq) +# define alloc_dep() alloc_seq_elt (struct dep) +# define alloc_goaldep() alloc_seq_elt (struct goaldep) + +# define free_ns(_n) free (_n) +# define free_dep(_d) free_ns (_d) +# define free_goaldep(_g) free_dep (_g) + +# define free_dep_chain(_d) free_ns_chain ((struct nameseq *)(_d)) +# define free_goal_chain(_g) free_ns_chain ((struct nameseq *)(_g)) +#endif struct dep *copy_dep_chain (const struct dep *d); -void free_dep_chain (struct dep *d); -void free_ns_chain (struct nameseq *n); -struct dep *read_all_makefiles (const char **makefiles); + +struct goaldep *read_all_makefiles (const char **makefiles); void eval_buffer (char *buffer, const gmk_floc *floc); -enum update_status update_goal_chain (struct dep *goals); +enum update_status update_goal_chain (struct goaldep *goals); +void show_goal_error (); diff --git a/job.c b/job.c index 66dde71c..59f51cd2 100644 --- a/job.c +++ b/job.c @@ -508,6 +508,8 @@ child_error (struct child *child, OUTPUT_SET (&child->output); + show_goal_error (); + if (exit_sig == 0) error (NILF, l + INTSTR_LENGTH, _("%s[%s: %s] Error %d%s"), pre, nm, f->name, exit_code, post); diff --git a/main.c b/main.c index ee08720f..73eaf181 100644 --- a/main.c +++ b/main.c @@ -503,7 +503,7 @@ static struct option long_option_aliases[] = /* List of goal targets. */ -static struct dep *goals, *lastgoal; +static struct goaldep *goals, *lastgoal; /* List of variables which were defined on the command line (or, equivalently, in MAKEFLAGS). */ @@ -1079,7 +1079,7 @@ main (int argc, char **argv, char **envp) { static char *stdin_nm = 0; int makefile_status = MAKE_SUCCESS; - struct dep *read_files; + struct goaldep *read_files; PATH_VAR (current_directory); unsigned int restarts = 0; unsigned int syncing = 0; @@ -2106,7 +2106,7 @@ main (int argc, char **argv, char **envp) define_makeflags (1, 0); - /* Make each 'struct dep' point at the 'struct file' for the file + /* Make each 'struct goaldep' point at the 'struct file' for the file depended on. Also do magic for special targets. */ snap_deps (); @@ -2184,7 +2184,7 @@ main (int argc, char **argv, char **envp) /* Remove any makefiles we don't want to try to update. Also record the current modtimes so we can compare them later. */ { - register struct dep *d, *last; + register struct goaldep *d, *last; last = 0; d = read_files; while (d != 0) @@ -2213,7 +2213,7 @@ main (int argc, char **argv, char **envp) last->next = d->next; /* Free the storage. */ - free_dep (d); + free_goaldep (d); d = last == 0 ? read_files : last->next; @@ -2270,7 +2270,7 @@ main (int argc, char **argv, char **envp) in updating or could not be found at all. */ int any_failed = 0; unsigned int i; - struct dep *d; + struct goaldep *d; for (i = 0, d = read_files; d != 0; ++i, d = d->next) { @@ -2287,7 +2287,7 @@ main (int argc, char **argv, char **envp) any_remade |= (file_mtime_no_search (d->file) != makefile_mtimes[i]); } - else if (! (d->changed & RM_DONTCARE)) + else if (! (d->flags & RM_DONTCARE)) { FILE_TIMESTAMP mtime; /* The update failed and this makefile was not @@ -2302,13 +2302,13 @@ main (int argc, char **argv, char **envp) } else /* This makefile was not found at all. */ - if (! (d->changed & RM_DONTCARE)) + if (! (d->flags & RM_DONTCARE)) { const char *dnm = dep_name (d); size_t l = strlen (dnm); /* This is a makefile we care about. See how much. */ - if (d->changed & RM_INCLUDED) + if (d->flags & RM_INCLUDED) /* An included makefile. We don't need to die, but we do want to complain. */ error (NILF, l, @@ -2544,7 +2544,7 @@ main (int argc, char **argv, char **envp) if (f) { - goals = alloc_dep (); + goals = alloc_goaldep (); goals->file = f; } } @@ -2730,12 +2730,12 @@ handle_non_switch_argument (const char *arg, int env) if (goals == 0) { - goals = alloc_dep (); + goals = alloc_goaldep (); lastgoal = goals; } else { - lastgoal->next = alloc_dep (); + lastgoal->next = alloc_goaldep (); lastgoal = lastgoal->next; } diff --git a/misc.c b/misc.c index f2fa24f1..e7ab8092 100644 --- a/misc.c +++ b/misc.c @@ -376,19 +376,6 @@ copy_dep_chain (const struct dep *d) return firstnew; } -/* Free a chain of 'struct dep'. */ - -void -free_dep_chain (struct dep *d) -{ - while (d != 0) - { - struct dep *df = d; - d = d->next; - free_dep (df); - } -} - /* Free a chain of struct nameseq. For struct dep chains use free_dep_chain. */ @@ -399,7 +386,7 @@ free_ns_chain (struct nameseq *ns) { struct nameseq *t = ns; ns = ns->next; - free (t); + free_ns (t); } } diff --git a/read.c b/read.c index 08739a54..10b250e7 100644 --- a/read.c +++ b/read.c @@ -132,9 +132,9 @@ const gmk_floc *reading_file = 0; /* The chain of files read by read_all_makefiles. */ -static struct dep *read_files = 0; +static struct goaldep *read_files = 0; -static int eval_makefile (const char *filename, int flags); +static struct goaldep *eval_makefile (const char *filename, int flags); static void eval (struct ebuffer *buffer, int flags); static long readline (struct ebuffer *ebuf); @@ -167,7 +167,7 @@ static char *unescape_char (char *string, int c); /* Read in all the makefiles and return a chain of targets to rebuild. */ -struct dep * +struct goaldep * read_all_makefiles (const char **makefiles) { unsigned int num_makefiles = 0; @@ -217,17 +217,11 @@ read_all_makefiles (const char **makefiles) if (makefiles != 0) while (*makefiles != 0) { - struct dep *tail = read_files; - struct dep *d; + struct goaldep *d = eval_makefile (*makefiles, 0); - if (! eval_makefile (*makefiles, 0)) + if (errno) perror_with_name ("", *makefiles); - /* Find the first element eval_makefile() added to read_files. */ - d = read_files; - while (d->next != tail) - d = d->next; - /* Reuse the storage allocated for the read_file. */ *makefiles = dep_name (d); ++num_makefiles; @@ -260,25 +254,25 @@ read_all_makefiles (const char **makefiles) if (*p != 0) { - if (! eval_makefile (*p, 0)) + eval_makefile (*p, 0); + if (errno) perror_with_name ("", *p); } else { /* No default makefile was found. Add the default makefiles to the 'read_files' chain so they will be updated if possible. */ - struct dep *tail = read_files; + struct goaldep *tail = read_files; /* Add them to the tail, after any MAKEFILES variable makefiles. */ while (tail != 0 && tail->next != 0) tail = tail->next; for (p = default_makefiles; *p != 0; ++p) { - struct dep *d = alloc_dep (); + struct goaldep *d = alloc_goaldep (); d->file = enter_file (strcache_add (*p)); - d->dontcare = 1; /* Tell update_goal_chain to bail out as soon as this file is made, and main not to die if we can't make this file. */ - d->changed = RM_DONTCARE; + d->flags = RM_DONTCARE; if (tail == 0) read_files = d; else @@ -319,10 +313,10 @@ restore_conditionals (struct conditionals *saved) conditionals = saved; } -static int +static struct goaldep * eval_makefile (const char *filename, int flags) { - struct dep *deps; + struct goaldep *deps; struct ebuffer ebuf; const gmk_floc *curfile; char *expanded = 0; @@ -401,16 +395,14 @@ eval_makefile (const char *filename, int flags) filename = strcache_add (filename); /* Add FILENAME to the chain of read makefiles. */ - deps = alloc_dep (); + deps = alloc_goaldep (); deps->next = read_files; read_files = deps; deps->file = lookup_file (filename); if (deps->file == 0) deps->file = enter_file (filename); filename = deps->file->name; - deps->changed = flags; - if (flags & RM_DONTCARE) - deps->dontcare = 1; + deps->flags = flags; free (expanded); @@ -419,10 +411,10 @@ eval_makefile (const char *filename, int flags) if (ebuf.fp == 0) { /* If we did some searching, errno has the error from the last - attempt, rather from FILENAME itself. Restore it in case the + attempt, rather from FILENAME itself. Store it in case the caller wants to use it in a message. */ errno = makefile_errno; - return 0; + return deps; } /* Set close-on-exec to avoid leaking the makefile to children, such as @@ -452,7 +444,8 @@ eval_makefile (const char *filename, int flags) free (ebuf.bufstart); alloca (0); - return 1; + errno = 0; + return deps; } void @@ -903,21 +896,20 @@ eval (struct ebuffer *ebuf, int set_default) while (files != 0) { struct nameseq *next = files->next; - const char *name = files->name; - int r; + int flags = (RM_INCLUDED | RM_NO_TILDE + | (noerror ? RM_DONTCARE : 0) + | (set_default ? 0 : RM_NO_DEFAULT_GOAL)); + + struct goaldep *d = eval_makefile (files->name, flags); + + if (errno) + { + d->error = (unsigned short)errno; + d->floc = *fstart; + } free_ns (files); files = next; - - r = eval_makefile (name, - (RM_INCLUDED | RM_NO_TILDE - | (noerror ? RM_DONTCARE : 0) - | (set_default ? 0 : RM_NO_DEFAULT_GOAL))); - if (!r && !noerror) - { - const char *err = strerror (errno); - OSS (error, fstart, "%s: %s", name, err); - } } /* Restore conditional state. */ @@ -957,7 +949,7 @@ eval (struct ebuffer *ebuf, int set_default) { struct nameseq *next = files->next; const char *name = files->name; - struct dep *deps; + struct goaldep *deps; int r; /* Load the file. 0 means failure. */ @@ -973,7 +965,7 @@ eval (struct ebuffer *ebuf, int set_default) continue; /* It succeeded, so add it to the list "to be rebuilt". */ - deps = alloc_dep (); + deps = alloc_goaldep (); deps->next = read_files; read_files = deps; deps->file = lookup_file (name); diff --git a/remake.c b/remake.c index e7a7d495..1722474a 100644 --- a/remake.c +++ b/remake.c @@ -55,6 +55,10 @@ extern int try_implicit_rule (struct file *file, unsigned int depth); /* Incremented when a command is started (under -n, when one would be). */ unsigned int commands_started = 0; +/* Set to the goal dependency. Mostly needed for remaking makefiles. */ +static struct goaldep *goal_list; +static struct dep *goal_dep; + /* Current value for pruning the scan of the goal chain (toggle 0/1). */ static unsigned int considered; @@ -77,32 +81,23 @@ static const char *library_search (const char *lib, FILE_TIMESTAMP *mtime_ptr); one goal whose 'changed' member is nonzero is successfully made. */ enum update_status -update_goal_chain (struct dep *goals) +update_goal_chain (struct goaldep *goaldeps) { int t = touch_flag, q = question_flag, n = just_print_flag; enum update_status status = us_none; -#define MTIME(file) (rebuilding_makefiles ? file_mtime_no_search (file) \ - : file_mtime (file)) - /* Duplicate the chain so we can remove things from it. */ - goals = copy_dep_chain (goals); + struct dep *goals = copy_dep_chain ((struct dep *)goaldeps); - { - /* Clear the 'changed' flag of each goal in the chain. - We will use the flag below to notice when any commands - have actually been run for a target. When no commands - have been run, we give an "up to date" diagnostic. */ - - struct dep *g; - for (g = goals; g != 0; g = g->next) - g->changed = 0; - } + goal_list = rebuilding_makefiles ? goaldeps : NULL; /* All files start with the considered bit 0, so the global value is 1. */ considered = 1; +#define MTIME(file) (rebuilding_makefiles ? file_mtime_no_search (file) \ + : file_mtime (file)) + /* Update all the goals until they are all finished. */ while (goals != 0) @@ -125,6 +120,8 @@ update_goal_chain (struct dep *goals) struct file *file; int stop = 0, any_not_updated = 0; + goal_dep = g; + for (file = g->file->double_colon ? g->file->double_colon : g->file; file != NULL; file = file->prev) @@ -132,7 +129,7 @@ update_goal_chain (struct dep *goals) unsigned int ocommands_started; enum update_status fail; - file->dontcare = g->dontcare; + file->dontcare = ANY_SET (g->flags, RM_DONTCARE); check_renamed (file); if (rebuilding_makefiles) @@ -268,6 +265,28 @@ update_goal_chain (struct dep *goals) return status; } +/* If we're rebuilding an included makefile that failed, and we care + about errors, show an error message the first time. */ + +void +show_goal_error () +{ + if ((goal_dep->flags & (RM_INCLUDED|RM_DONTCARE)) != RM_INCLUDED) + return; + + for (struct goaldep *goal = goal_list; goal; goal = goal->next) + if (goal_dep->file == goal->file) + { + if (goal->error) + { + OSS (error, &goal->floc, "%s: %s", + goal->file->name, strerror ((int)goal->error)); + goal->error = 0; + } + return; + } +} + /* If FILE is not up to date, execute the commands for it. Return 0 if successful, non-0 if unsuccessful; but with some flag settings, just call 'exit' if unsuccessful. @@ -380,29 +399,28 @@ complain (struct file *file) if (d == 0) { + show_goal_error (); + /* Didn't find any dependencies to complain about. */ if (file->parent) { size_t l = strlen (file->name) + strlen (file->parent->name) + 4; + const char *m = _("%sNo rule to make target '%s', needed by '%s'%s"); if (!keep_going_flag) - fatal (NILF, l, - _("%sNo rule to make target '%s', needed by '%s'%s"), - "", file->name, file->parent->name, ""); + fatal (NILF, l, m, "", file->name, file->parent->name, ""); - error (NILF, l, _("%sNo rule to make target '%s', needed by '%s'%s"), - "*** ", file->name, file->parent->name, "."); + error (NILF, l, m, "*** ", file->name, file->parent->name, "."); } else { size_t l = strlen (file->name) + 4; + const char *m = _("%sNo rule to make target '%s'%s"); if (!keep_going_flag) - fatal (NILF, l, - _("%sNo rule to make target '%s'%s"), "", file->name, ""); + fatal (NILF, l, m, "", file->name, ""); - error (NILF, l, - _("%sNo rule to make target '%s'%s"), "*** ", file->name, "."); + error (NILF, l, m, "*** ", file->name, "."); } file->no_diag = 0; diff --git a/tests/scripts/features/include b/tests/scripts/features/include index ee014bd8..f78563f9 100644 --- a/tests/scripts/features/include +++ b/tests/scripts/features/include @@ -165,6 +165,70 @@ baz: end #MAKE#: *** No rule to make target 'end', needed by 'baz'. Stop.\n", 512); +# Test include of make-able file doesn't show an error (Savannah #102) +run_make_test(q! +.PHONY: default +default:; @echo DONE + +inc1:; echo > $@ +include inc1 +include inc2 +inc2:; echo > $@ +!, + '', "echo > inc2\necho > inc1\nDONE\n"); + +rmfiles('inc1', 'inc2'); + +# Test include of non-make-able file does show an error (Savannah #102) +run_make_test(q! +.PHONY: default +default:; @echo DONE + +inc1:; echo > $@ +include inc1 +include inc2 +!, + '', "#MAKEFILE#:7: inc2: No such file or directory\n#MAKE#: *** No rule to make target 'inc2'. Stop.\n", 512); + +rmfiles('inc1'); + +# Include same file multiple times + +run_make_test(q! +default:; @echo DEFAULT +include inc1 +inc1:; echo > $@ +include inc1 +!, + '', "echo > inc1\nDEFAULT\n"); + +rmfiles('inc1'); + +# Included file has a prerequisite that fails to build + +run_make_test(q! +default:; @echo DEFAULT +include inc1 +inc1: foo; echo > $@ +foo:; exit 1 +!, + '', "exit 1\n#MAKEFILE#:3: inc1: No such file or directory\n#MAKE#: *** [#MAKEFILE#:5: foo] Error 1\n", 512); + +rmfiles('inc1'); + +# Included file has a prerequisite we don't know how to build + +run_make_test(q! +default:; @echo DEFAULT +include inc1 +inc1: foo; echo > $@ +!, + '', "#MAKEFILE#:3: inc1: No such file or directory\n#MAKE#: *** No rule to make target 'foo', needed by 'inc1'. Stop.\n", 512); + +rmfiles('inc1'); + +# include a directory + if ($all_tests) { # Test that include of a rebuild-able file doesn't show a warning # Savannah bug #102 diff --git a/tests/scripts/options/dash-B b/tests/scripts/options/dash-B index 9c708b77..4c4c4cfb 100644 --- a/tests/scripts/options/dash-B +++ b/tests/scripts/options/dash-B @@ -45,7 +45,6 @@ include foo.x foo.x: ; @touch $@ ', '-B', 'MAKE_RESTARTS= -#MAKEFILE#:4: foo.x: No such file or directory MAKE_RESTARTS=1'); rmfiles('foo.x'); @@ -63,7 +62,6 @@ foo.x: ; @touch $@ blah.x: ; @echo $@ ', '-B', 'MAKE_RESTARTS= -#MAKEFILE#:4: foo.x: No such file or directory MAKE_RESTARTS=1 blah.x all'); @@ -83,3 +81,7 @@ x.a: x.b ; @echo $? unlink(qw(x.a x.b)); 1; + +### Local Variables: +### eval: (setq whitespace-action (delq 'auto-cleanup whitespace-action)) +### End: diff --git a/tests/scripts/options/dash-W b/tests/scripts/options/dash-W index 20b9f745..857b1cce 100644 --- a/tests/scripts/options/dash-W +++ b/tests/scripts/options/dash-W @@ -42,8 +42,7 @@ foo.x: bar.x bar.x: ; echo >> $@ baz.x: bar.x ; @echo "touch $@" ', - '', '#MAKEFILE#:3: foo.x: No such file or directory -echo >> bar.x + '', 'echo >> bar.x touch foo.x restarts=1 touch baz.x'); @@ -86,3 +85,7 @@ unlink(qw(y x-dir/x)); rmdir('x-dir'); 1; + +### Local Variables: +### eval: (setq whitespace-action (delq 'auto-cleanup whitespace-action)) +### End: diff --git a/tests/scripts/options/print-directory b/tests/scripts/options/print-directory index a05bbee7..db762b2c 100644 --- a/tests/scripts/options/print-directory +++ b/tests/scripts/options/print-directory @@ -18,7 +18,7 @@ include foo all: ;@: foo: ; touch foo !, - "", "#MAKEFILE#:2: foo: No such file or directory\ntouch foo\n"); + "", "touch foo\n"); unlink('foo'); # Test makefile rebuild with -w @@ -27,7 +27,7 @@ include foo all: ;@: foo: ; touch foo !, - "-w", "#MAKE#: Entering directory '#PWD#'\n#MAKEFILE#:2: foo: No such file or directory\ntouch foo\n#MAKE#: Leaving directory '#PWD#'\n"); + "-w", "#MAKE#: Entering directory '#PWD#'\ntouch foo\n#MAKE#: Leaving directory '#PWD#'\n"); unlink('foo'); 1; diff --git a/tests/scripts/variables/MAKE_RESTARTS b/tests/scripts/variables/MAKE_RESTARTS index ef8e368f..01bf55ec 100644 --- a/tests/scripts/variables/MAKE_RESTARTS +++ b/tests/scripts/variables/MAKE_RESTARTS @@ -11,7 +11,6 @@ include foo.x foo.x: ; @touch $@ ', '', 'MAKE_RESTARTS= -#MAKEFILE#:4: foo.x: No such file or directory MAKE_RESTARTS=1'); rmfiles('foo.x'); @@ -26,9 +25,7 @@ foo.x: ; @echo "include bar.x" > $@ bar.x: ; @touch $@ ', '', 'MAKE_RESTARTS= -#MAKEFILE#:4: foo.x: No such file or directory MAKE_RESTARTS=1 -foo.x:1: bar.x: No such file or directory MAKE_RESTARTS=2'); rmfiles('foo.x', 'bar.x'); @@ -47,9 +44,7 @@ foo.x: ; @echo "include bar.x" > $@ bar.x: ; @touch $@ ', '', "MAKE_RESTARTS= -#MAKEFILE#:8: foo.x: No such file or directory MAKE_RESTARTS=1 -foo.x:1: bar.x: No such file or directory MAKE_RESTARTS=2 recurse MAKE_RESTARTS= #MAKE#[1]: Entering directory '#PWD#' @@ -60,3 +55,7 @@ all MAKE_RESTARTS= rmfiles('foo.x', 'bar.x'); 1; + +### Local Variables: +### eval: (setq whitespace-action (delq 'auto-cleanup whitespace-action)) +### End: