kconfig: allow loading multiple configurations
[powerpc.git] / scripts / kconfig / confdata.c
index 02f670c..ca693fc 100644 (file)
 #define LKC_DIRECT_LINK
 #include "lkc.h"
 
+static void conf_warning(const char *fmt, ...)
+       __attribute__ ((format (printf, 1, 2)));
+
+static const char *conf_filename;
+static int conf_lineno, conf_warnings, conf_unsaved;
+
 const char conf_def_filename[] = ".config";
 
 const char conf_defname[] = "arch/$ARCH/defconfig";
@@ -27,6 +33,17 @@ const char *conf_confnames[] = {
        NULL,
 };
 
+static void conf_warning(const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno);
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n");
+       va_end(ap);
+       conf_warnings++;
+}
+
 static char *conf_expand_value(const char *in)
 {
        struct symbol *sym;
@@ -69,53 +86,65 @@ char *conf_get_default_confname(void)
        return name;
 }
 
-int conf_read(const char *name)
+int conf_read_simple(const char *name, int def)
 {
        FILE *in = NULL;
        char line[1024];
        char *p, *p2;
-       int lineno = 0;
        struct symbol *sym;
-       struct property *prop;
-       struct expr *e;
-       int i;
+       int i, def_flags;
 
        if (name) {
                in = zconf_fopen(name);
        } else {
                const char **names = conf_confnames;
+               name = *names++;
+               if (!name)
+                       return 1;
+               in = zconf_fopen(name);
+               if (in)
+                       goto load;
+               sym_change_count++;
                while ((name = *names++)) {
                        name = conf_expand_value(name);
                        in = zconf_fopen(name);
                        if (in) {
                                printf(_("#\n"
-                                        "# using defaults found in %s\n"
-                                        "#\n"), name);
-                               break;
+                                        "# using defaults found in %s\n"
+                                        "#\n"), name);
+                               goto load;
                        }
                }
        }
-
        if (!in)
                return 1;
 
+load:
+       conf_filename = name;
+       conf_lineno = 0;
+       conf_warnings = 0;
+       conf_unsaved = 0;
+
+       def_flags = SYMBOL_DEF << def;
        for_all_symbols(i, sym) {
-               sym->flags |= SYMBOL_NEW | SYMBOL_CHANGED;
-               sym->flags &= ~SYMBOL_VALID;
+               sym->flags |= SYMBOL_CHANGED;
+               sym->flags &= ~(def_flags|SYMBOL_VALID);
+               if (sym_is_choice(sym))
+                       sym->flags |= def_flags;
                switch (sym->type) {
                case S_INT:
                case S_HEX:
                case S_STRING:
-                       if (sym->user.val)
-                               free(sym->user.val);
+                       if (sym->def[def].val)
+                               free(sym->def[def].val);
                default:
-                       sym->user.val = NULL;
-                       sym->user.tri = no;
+                       sym->def[def].val = NULL;
+                       sym->def[def].tri = no;
                }
        }
 
        while (fgets(line, sizeof(line), in)) {
-               lineno++;
+               conf_lineno++;
                sym = NULL;
                switch (line[0]) {
                case '#':
@@ -127,24 +156,36 @@ int conf_read(const char *name)
                        *p++ = 0;
                        if (strncmp(p, "is not set", 10))
                                continue;
-                       sym = sym_find(line + 9);
-                       if (!sym) {
-                               fprintf(stderr, "%s:%d: trying to assign nonexistent symbol %s\n", name, lineno, line + 9);
+                       if (def == S_DEF_USER) {
+                               sym = sym_find(line + 9);
+                               if (!sym) {
+                                       conf_warning("trying to assign nonexistent symbol %s", line + 9);
+                                       break;
+                               }
+                       } else {
+                               sym = sym_lookup(line + 9, 0);
+                               if (sym->type == S_UNKNOWN)
+                                       sym->type = S_BOOLEAN;
+                       }
+                       if (sym->flags & def_flags) {
+                               conf_warning("trying to reassign symbol %s", sym->name);
                                break;
                        }
                        switch (sym->type) {
                        case S_BOOLEAN:
                        case S_TRISTATE:
-                               sym->user.tri = no;
-                               sym->flags &= ~SYMBOL_NEW;
+                               sym->def[def].tri = no;
+                               sym->flags |= def_flags;
                                break;
                        default:
                                ;
                        }
                        break;
                case 'C':
-                       if (memcmp(line, "CONFIG_", 7))
+                       if (memcmp(line, "CONFIG_", 7)) {
+                               conf_warning("unexpected data");
                                continue;
+                       }
                        p = strchr(line + 7, '=');
                        if (!p)
                                continue;
@@ -152,30 +193,48 @@ int conf_read(const char *name)
                        p2 = strchr(p, '\n');
                        if (p2)
                                *p2 = 0;
-                       sym = sym_find(line + 7);
-                       if (!sym) {
-                               fprintf(stderr, "%s:%d: trying to assign nonexistent symbol %s\n", name, lineno, line + 7);
+                       if (def == S_DEF_USER) {
+                               sym = sym_find(line + 7);
+                               if (!sym) {
+                                       conf_warning("trying to assign nonexistent symbol %s", line + 7);
+                                       break;
+                               }
+                       } else {
+                               sym = sym_lookup(line + 7, 0);
+                               if (sym->type == S_UNKNOWN)
+                                       sym->type = S_OTHER;
+                       }
+                       if (sym->flags & def_flags) {
+                               conf_warning("trying to reassign symbol %s", sym->name);
                                break;
                        }
                        switch (sym->type) {
                        case S_TRISTATE:
                                if (p[0] == 'm') {
-                                       sym->user.tri = mod;
-                                       sym->flags &= ~SYMBOL_NEW;
+                                       sym->def[def].tri = mod;
+                                       sym->flags |= def_flags;
                                        break;
                                }
                        case S_BOOLEAN:
                                if (p[0] == 'y') {
-                                       sym->user.tri = yes;
-                                       sym->flags &= ~SYMBOL_NEW;
+                                       sym->def[def].tri = yes;
+                                       sym->flags |= def_flags;
                                        break;
                                }
                                if (p[0] == 'n') {
-                                       sym->user.tri = no;
-                                       sym->flags &= ~SYMBOL_NEW;
+                                       sym->def[def].tri = no;
+                                       sym->flags |= def_flags;
                                        break;
                                }
+                               conf_warning("symbol value '%s' invalid for %s", p, sym->name);
                                break;
+                       case S_OTHER:
+                               if (*p != '"') {
+                                       for (p2 = p; *p2 && !isspace(*p2); p2++)
+                                               ;
+                                       sym->type = S_STRING;
+                                       goto done;
+                               }
                        case S_STRING:
                                if (*p++ != '"')
                                        break;
@@ -187,17 +246,18 @@ int conf_read(const char *name)
                                        memmove(p2, p2 + 1, strlen(p2));
                                }
                                if (!p2) {
-                                       fprintf(stderr, "%s:%d: invalid string found\n", name, lineno);
-                                       exit(1);
+                                       conf_warning("invalid string found");
+                                       continue;
                                }
                        case S_INT:
                        case S_HEX:
+                       done:
                                if (sym_string_valid(sym, p)) {
-                                       sym->user.val = strdup(p);
-                                       sym->flags &= ~SYMBOL_NEW;
+                                       sym->def[def].val = strdup(p);
+                                       sym->flags |= def_flags;
                                } else {
-                                       fprintf(stderr, "%s:%d: symbol value '%s' invalid for %s\n", name, lineno, p, sym->name);
-                                       exit(1);
+                                       conf_warning("symbol value '%s' invalid for %s", p, sym->name);
+                                       continue;
                                }
                                break;
                        default:
@@ -207,42 +267,83 @@ int conf_read(const char *name)
                case '\n':
                        break;
                default:
+                       conf_warning("unexpected data");
                        continue;
                }
                if (sym && sym_is_choice_value(sym)) {
                        struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
-                       switch (sym->user.tri) {
+                       switch (sym->def[def].tri) {
                        case no:
                                break;
                        case mod:
-                               if (cs->user.tri == yes)
-                                       /* warn? */;
+                               if (cs->def[def].tri == yes) {
+                                       conf_warning("%s creates inconsistent choice state", sym->name);
+                                       cs->flags &= ~def_flags;
+                               }
                                break;
                        case yes:
-                               if (cs->user.tri != no)
-                                       /* warn? */;
-                               cs->user.val = sym;
+                               if (cs->def[def].tri != no) {
+                                       conf_warning("%s creates inconsistent choice state", sym->name);
+                                       cs->flags &= ~def_flags;
+                               } else
+                                       cs->def[def].val = sym;
                                break;
                        }
-                       cs->user.tri = E_OR(cs->user.tri, sym->user.tri);
-                       cs->flags &= ~SYMBOL_NEW;
+                       cs->def[def].tri = E_OR(cs->def[def].tri, sym->def[def].tri);
                }
        }
        fclose(in);
 
        if (modules_sym)
                sym_calc_value(modules_sym);
+       return 0;
+}
+
+int conf_read(const char *name)
+{
+       struct symbol *sym;
+       struct property *prop;
+       struct expr *e;
+       int i, flags;
+
+       sym_change_count = 0;
+
+       if (conf_read_simple(name, S_DEF_USER))
+               return 1;
+
        for_all_symbols(i, sym) {
                sym_calc_value(sym);
+               if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO))
+                       goto sym_ok;
+               if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) {
+                       /* check that calculated value agrees with saved value */
+                       switch (sym->type) {
+                       case S_BOOLEAN:
+                       case S_TRISTATE:
+                               if (sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym))
+                                       break;
+                               if (!sym_is_choice(sym))
+                                       goto sym_ok;
+                       default:
+                               if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val))
+                                       goto sym_ok;
+                               break;
+                       }
+               } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE))
+                       /* no previous value and not saved */
+                       goto sym_ok;
+               conf_unsaved++;
+               /* maybe print value in verbose mode... */
+       sym_ok:
                if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
                        if (sym->visible == no)
-                               sym->flags |= SYMBOL_NEW;
+                               sym->flags &= ~SYMBOL_DEF_USER;
                        switch (sym->type) {
                        case S_STRING:
                        case S_INT:
                        case S_HEX:
-                               if (!sym_string_within_range(sym, sym->user.val))
-                                       sym->flags |= SYMBOL_NEW;
+                               if (!sym_string_within_range(sym, sym->def[S_DEF_USER].val))
+                                       sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER);
                        default:
                                break;
                        }
@@ -250,19 +351,21 @@ int conf_read(const char *name)
                if (!sym_is_choice(sym))
                        continue;
                prop = sym_get_choice_prop(sym);
+               flags = sym->flags;
                for (e = prop->expr; e; e = e->left.expr)
                        if (e->right.sym->visible != no)
-                               sym->flags |= e->right.sym->flags & SYMBOL_NEW;
+                               flags &= e->right.sym->flags;
+               sym->flags |= flags & SYMBOL_DEF_USER;
        }
 
-       sym_change_count = 1;
+       sym_change_count += conf_warnings || conf_unsaved;
 
        return 0;
 }
 
 int conf_write(const char *name)
 {
-       FILE *out, *out_h;
+       FILE *out;
        struct symbol *sym;
        struct menu *menu;
        const char *basename;
@@ -299,13 +402,7 @@ int conf_write(const char *name)
        out = fopen(newname, "w");
        if (!out)
                return 1;
-       out_h = NULL;
-       if (!name) {
-               out_h = fopen(".tmpconfig.h", "w");
-               if (!out_h)
-                       return 1;
-       }
-       sym = sym_lookup("KERNELRELEASE", 0);
+       sym = sym_lookup("KERNELVERSION", 0);
        sym_calc_value(sym);
        time(&now);
        env = getenv("KCONFIG_NOTIMESTAMP");
@@ -320,16 +417,6 @@ int conf_write(const char *name)
                     sym_get_string_value(sym),
                     use_timestamp ? "# " : "",
                     use_timestamp ? ctime(&now) : "");
-       if (out_h)
-               fprintf(out_h, "/*\n"
-                              " * Automatically generated C config: don't edit\n"
-                              " * Linux kernel version: %s\n"
-                              "%s%s"
-                              " */\n"
-                              "#define AUTOCONF_INCLUDED\n",
-                              sym_get_string_value(sym),
-                              use_timestamp ? " * " : "",
-                              use_timestamp ? ctime(&now) : "");
 
        if (!sym_change_count)
                sym_clear_all_valid();
@@ -345,11 +432,6 @@ int conf_write(const char *name)
                                     "#\n"
                                     "# %s\n"
                                     "#\n", str);
-                       if (out_h)
-                               fprintf(out_h, "\n"
-                                              "/*\n"
-                                              " * %s\n"
-                                              " */\n", str);
                } else if (!(sym->flags & SYMBOL_CHOICE)) {
                        sym_calc_value(sym);
                        if (!(sym->flags & SYMBOL_WRITE))
@@ -367,59 +449,39 @@ int conf_write(const char *name)
                                switch (sym_get_tristate_value(sym)) {
                                case no:
                                        fprintf(out, "# CONFIG_%s is not set\n", sym->name);
-                                       if (out_h)
-                                               fprintf(out_h, "#undef CONFIG_%s\n", sym->name);
                                        break;
                                case mod:
                                        fprintf(out, "CONFIG_%s=m\n", sym->name);
-                                       if (out_h)
-                                               fprintf(out_h, "#define CONFIG_%s_MODULE 1\n", sym->name);
                                        break;
                                case yes:
                                        fprintf(out, "CONFIG_%s=y\n", sym->name);
-                                       if (out_h)
-                                               fprintf(out_h, "#define CONFIG_%s 1\n", sym->name);
                                        break;
                                }
                                break;
                        case S_STRING:
-                               // fix me
                                str = sym_get_string_value(sym);
                                fprintf(out, "CONFIG_%s=\"", sym->name);
-                               if (out_h)
-                                       fprintf(out_h, "#define CONFIG_%s \"", sym->name);
-                               do {
+                               while (1) {
                                        l = strcspn(str, "\"\\");
                                        if (l) {
                                                fwrite(str, l, 1, out);
-                                               if (out_h)
-                                                       fwrite(str, l, 1, out_h);
+                                               str += l;
                                        }
-                                       str += l;
-                                       while (*str == '\\' || *str == '"') {
-                                               fprintf(out, "\\%c", *str);
-                                               if (out_h)
-                                                       fprintf(out_h, "\\%c", *str);
-                                               str++;
-                                       }
-                               } while (*str);
+                                       if (!*str)
+                                               break;
+                                       fprintf(out, "\\%c", *str++);
+                               }
                                fputs("\"\n", out);
-                               if (out_h)
-                                       fputs("\"\n", out_h);
                                break;
                        case S_HEX:
                                str = sym_get_string_value(sym);
                                if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
                                        fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
-                                       if (out_h)
-                                               fprintf(out_h, "#define CONFIG_%s 0x%s\n", sym->name, str);
                                        break;
                                }
                        case S_INT:
                                str = sym_get_string_value(sym);
                                fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
-                               if (out_h)
-                                       fprintf(out_h, "#define CONFIG_%s %s\n", sym->name, str);
                                break;
                        }
                }
@@ -439,11 +501,6 @@ int conf_write(const char *name)
                }
        }
        fclose(out);
-       if (out_h) {
-               fclose(out_h);
-               rename(".tmpconfig.h", "include/linux/autoconf.h");
-               file_write_dep(NULL);
-       }
        if (!name || basename != conf_def_filename) {
                if (!name)
                        name = conf_def_filename;
@@ -454,7 +511,128 @@ int conf_write(const char *name)
        if (rename(newname, tmpname))
                return 1;
 
+       printf(_("#\n"
+                "# configuration written to %s\n"
+                "#\n"), tmpname);
+
        sym_change_count = 0;
 
        return 0;
 }
+
+int conf_write_autoconf(void)
+{
+       struct symbol *sym;
+       const char *str;
+       char *name;
+       FILE *out, *out_h;
+       time_t now;
+       int i, l;
+
+       file_write_dep("include/config/auto.conf.cmd");
+
+       out = fopen(".tmpconfig", "w");
+       if (!out)
+               return 1;
+
+       out_h = fopen(".tmpconfig.h", "w");
+       if (!out_h) {
+               fclose(out);
+               return 1;
+       }
+
+       sym = sym_lookup("KERNELVERSION", 0);
+       sym_calc_value(sym);
+       time(&now);
+       fprintf(out, "#\n"
+                    "# Automatically generated make config: don't edit\n"
+                    "# Linux kernel version: %s\n"
+                    "# %s"
+                    "#\n",
+                    sym_get_string_value(sym), ctime(&now));
+       fprintf(out_h, "/*\n"
+                      " * Automatically generated C config: don't edit\n"
+                      " * Linux kernel version: %s\n"
+                      " * %s"
+                      " */\n"
+                      "#define AUTOCONF_INCLUDED\n",
+                      sym_get_string_value(sym), ctime(&now));
+
+       sym_clear_all_valid();
+
+       for_all_symbols(i, sym) {
+               sym_calc_value(sym);
+               if (!(sym->flags & SYMBOL_WRITE) || !sym->name)
+                       continue;
+               switch (sym->type) {
+               case S_BOOLEAN:
+               case S_TRISTATE:
+                       switch (sym_get_tristate_value(sym)) {
+                       case no:
+                               break;
+                       case mod:
+                               fprintf(out, "CONFIG_%s=m\n", sym->name);
+                               fprintf(out_h, "#define CONFIG_%s_MODULE 1\n", sym->name);
+                               break;
+                       case yes:
+                               fprintf(out, "CONFIG_%s=y\n", sym->name);
+                               fprintf(out_h, "#define CONFIG_%s 1\n", sym->name);
+                               break;
+                       }
+                       break;
+               case S_STRING:
+                       str = sym_get_string_value(sym);
+                       fprintf(out, "CONFIG_%s=\"", sym->name);
+                       fprintf(out_h, "#define CONFIG_%s \"", sym->name);
+                       while (1) {
+                               l = strcspn(str, "\"\\");
+                               if (l) {
+                                       fwrite(str, l, 1, out);
+                                       fwrite(str, l, 1, out_h);
+                                       str += l;
+                               }
+                               if (!*str)
+                                       break;
+                               fprintf(out, "\\%c", *str);
+                               fprintf(out_h, "\\%c", *str);
+                               str++;
+                       }
+                       fputs("\"\n", out);
+                       fputs("\"\n", out_h);
+                       break;
+               case S_HEX:
+                       str = sym_get_string_value(sym);
+                       if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
+                               fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
+                               fprintf(out_h, "#define CONFIG_%s 0x%s\n", sym->name, str);
+                               break;
+                       }
+               case S_INT:
+                       str = sym_get_string_value(sym);
+                       fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
+                       fprintf(out_h, "#define CONFIG_%s %s\n", sym->name, str);
+                       break;
+               default:
+                       break;
+               }
+       }
+       fclose(out);
+       fclose(out_h);
+
+       name = getenv("KCONFIG_AUTOHEADER");
+       if (!name)
+               name = "include/linux/autoconf.h";
+       if (rename(".tmpconfig.h", name))
+               return 1;
+       name = getenv("KCONFIG_AUTOCONFIG");
+       if (!name)
+               name = "include/config/auto.conf";
+       /*
+        * This must be the last step, kbuild has a dependency on auto.conf
+        * and this marks the successful completion of the previous steps.
+        */
+       if (rename(".tmpconfig", name))
+               return 1;
+
+       return 0;
+}