build: scripts/config - update to kconfig-v6.6.16
[openwrt/staging/stintel.git] / scripts / config / confdata.c
index dad0b471c7df31ec1922a770c2222f84ea1ec762..e3f6fdfeb324e828fe2ded7924b1ca4fae1105ad 100644 (file)
@@ -11,6 +11,7 @@
 #include <fcntl.h>
 #include <limits.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -129,41 +130,22 @@ static size_t depfile_prefix_len;
 /* touch depfile for symbol 'name' */
 static int conf_touch_dep(const char *name)
 {
-       int fd, ret;
-       char *d;
+       int fd;
 
        /* check overflow: prefix + name + '\0' must fit in buffer. */
        if (depfile_prefix_len + strlen(name) + 1 > sizeof(depfile_path))
                return -1;
 
-       d = depfile_path + depfile_prefix_len;
-       strcpy(d, name);
+       strcpy(depfile_path + depfile_prefix_len, name);
 
-       /* Assume directory path already exists. */
        fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-       if (fd == -1) {
-               if (errno != ENOENT)
-                       return -1;
-
-               ret = make_parent_dir(depfile_path);
-               if (ret)
-                       return ret;
-
-               /* Try it again. */
-               fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-               if (fd == -1)
-                       return -1;
-       }
+       if (fd == -1)
+               return -1;
        close(fd);
 
        return 0;
 }
 
-struct conf_printer {
-       void (*print_symbol)(FILE *, struct symbol *, const char *, void *);
-       void (*print_comment)(FILE *, const char *, void *);
-};
-
 static void conf_warning(const char *fmt, ...)
        __attribute__ ((format (printf, 1, 2)));
 
@@ -227,6 +209,20 @@ static const char *conf_get_autoconfig_name(void)
        return name ? name : "include/config/auto.conf";
 }
 
+static const char *conf_get_autoheader_name(void)
+{
+       char *name = getenv("KCONFIG_AUTOHEADER");
+
+       return name ? name : "include/generated/autoconf.h";
+}
+
+static const char *conf_get_rustccfg_name(void)
+{
+       char *name = getenv("KCONFIG_RUSTCCFG");
+
+       return name ? name : "include/generated/rustc_cfg";
+}
+
 static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p)
 {
        char *p2;
@@ -255,19 +251,21 @@ static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p)
                                     p, sym->name);
                return 1;
        case S_STRING:
-               if (*p++ != '"')
-                       break;
-               for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
-                       if (*p2 == '"') {
-                               *p2 = 0;
+               /* No escaping for S_DEF_AUTO (include/config/auto.conf) */
+               if (def != S_DEF_AUTO) {
+                       if (*p++ != '"')
                                break;
+                       for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
+                               if (*p2 == '"') {
+                                       *p2 = 0;
+                                       break;
+                               }
+                               memmove(p2, p2 + 1, strlen(p2));
                        }
-                       memmove(p2, p2 + 1, strlen(p2));
-               }
-               if (!p2) {
-                       if (def != S_DEF_AUTO)
+                       if (!p2) {
                                conf_warning("invalid string found");
-                       return 1;
+                               return 1;
+                       }
                }
                /* fall through */
        case S_INT:
@@ -376,7 +374,11 @@ int conf_read_simple(const char *name, int def)
        char *p, *p2;
        struct symbol *sym;
        int def_flags;
+       const char *warn_unknown;
+       const char *werror;
 
+       warn_unknown = getenv("KCONFIG_WARN_UNKNOWN_SYMBOLS");
+       werror = getenv("KCONFIG_WERROR");
        if (name) {
                in = zconf_fopen(name);
        } else {
@@ -448,6 +450,10 @@ load:
                        if (def == S_DEF_USER) {
                                sym = sym_find(line + 2 + strlen(CONFIG_));
                                if (!sym) {
+                                       if (warn_unknown)
+                                               conf_warning("unknown symbol: %s",
+                                                            line + 2 + strlen(CONFIG_));
+
                                        conf_set_changed(true);
                                        continue;
                                }
@@ -479,7 +485,7 @@ load:
 
                        sym = sym_find(line + strlen(CONFIG_));
                        if (!sym) {
-                               if (def == S_DEF_AUTO)
+                               if (def == S_DEF_AUTO) {
                                        /*
                                         * Reading from include/config/auto.conf
                                         * If CONFIG_FOO previously existed in
@@ -487,8 +493,13 @@ load:
                                         * include/config/FOO must be touched.
                                         */
                                        conf_touch_dep(line + strlen(CONFIG_));
-                               else
+                               } else {
+                                       if (warn_unknown)
+                                               conf_warning("unknown symbol: %s",
+                                                            line + strlen(CONFIG_));
+
                                        conf_set_changed(true);
+                               }
                                continue;
                        }
 
@@ -524,6 +535,10 @@ load:
        }
        free(line);
        fclose(in);
+
+       if (conf_warnings && werror)
+               exit(1);
+
        return 0;
 }
 
@@ -597,169 +612,226 @@ int conf_read(const char *name)
        return 0;
 }
 
-/*
- * Kconfig configuration printer
- *
- * This printer is used when generating the resulting configuration after
- * kconfig invocation and `defconfig' files. Unset symbol might be omitted by
- * passing a non-NULL argument to the printer.
- *
- */
-static void
-kconfig_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
+struct comment_style {
+       const char *decoration;
+       const char *prefix;
+       const char *postfix;
+};
+
+static const struct comment_style comment_style_pound = {
+       .decoration = "#",
+       .prefix = "#",
+       .postfix = "#",
+};
+
+static const struct comment_style comment_style_c = {
+       .decoration = " *",
+       .prefix = "/*",
+       .postfix = " */",
+};
+
+static void conf_write_heading(FILE *fp, const struct comment_style *cs)
 {
+       if (!cs)
+               return;
 
-       switch (sym->type) {
-       case S_BOOLEAN:
-       case S_TRISTATE:
-               if (*value == 'n') {
-                       bool skip_unset = (arg != NULL);
+       fprintf(fp, "%s\n", cs->prefix);
 
-                       if (!skip_unset)
-                               fprintf(fp, "# %s%s is not set\n",
-                                   CONFIG_, sym->name);
-                       return;
-               }
-               break;
-       default:
-               break;
-       }
+       fprintf(fp, "%s Automatically generated file; DO NOT EDIT.\n",
+               cs->decoration);
+
+       fprintf(fp, "%s %s\n", cs->decoration, rootmenu.prompt->text);
 
-       fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, value);
+       fprintf(fp, "%s\n", cs->postfix);
 }
 
-static void
-kconfig_print_comment(FILE *fp, const char *value, void *arg)
+/* The returned pointer must be freed on the caller side */
+static char *escape_string_value(const char *in)
 {
-       const char *p = value;
-       size_t l;
+       const char *p;
+       char *out;
+       size_t len;
 
-       for (;;) {
-               l = strcspn(p, "\n");
-               fprintf(fp, "#");
-               if (l) {
-                       fprintf(fp, " ");
-                       xfwrite(p, l, 1, fp);
-                       p += l;
-               }
-               fprintf(fp, "\n");
-               if (*p++ == '\0')
+       len = strlen(in) + strlen("\"\"") + 1;
+
+       p = in;
+       while (1) {
+               p += strcspn(p, "\"\\");
+
+               if (p[0] == '\0')
                        break;
+
+               len++;
+               p++;
+       }
+
+       out = xmalloc(len);
+       out[0] = '\0';
+
+       strcat(out, "\"");
+
+       p = in;
+       while (1) {
+               len = strcspn(p, "\"\\");
+               strncat(out, p, len);
+               p += len;
+
+               if (p[0] == '\0')
+                       break;
+
+               strcat(out, "\\");
+               strncat(out, p++, 1);
        }
+
+       strcat(out, "\"");
+
+       return out;
 }
 
-static struct conf_printer kconfig_printer_cb =
+enum output_n { OUTPUT_N, OUTPUT_N_AS_UNSET, OUTPUT_N_NONE };
+
+static void __print_symbol(FILE *fp, struct symbol *sym, enum output_n output_n,
+                          bool escape_string)
 {
-       .print_symbol = kconfig_print_symbol,
-       .print_comment = kconfig_print_comment,
-};
+       const char *val;
+       char *escaped = NULL;
 
-/*
- * Header printer
- *
- * This printer is used when generating the `include/generated/autoconf.h' file.
- */
-static void
-header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
+       if (sym->type == S_UNKNOWN)
+               return;
+
+       val = sym_get_string_value(sym);
+
+       if ((sym->type == S_BOOLEAN || sym->type == S_TRISTATE) &&
+           output_n != OUTPUT_N && *val == 'n') {
+               if (output_n == OUTPUT_N_AS_UNSET)
+                       fprintf(fp, "# %s%s is not set\n", CONFIG_, sym->name);
+               return;
+       }
+
+       if (sym->type == S_STRING && escape_string) {
+               escaped = escape_string_value(val);
+               val = escaped;
+       }
+
+       fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, val);
+
+       free(escaped);
+}
+
+static void print_symbol_for_dotconfig(FILE *fp, struct symbol *sym)
 {
+       __print_symbol(fp, sym, OUTPUT_N_AS_UNSET, true);
+}
+
+static void print_symbol_for_autoconf(FILE *fp, struct symbol *sym)
+{
+       __print_symbol(fp, sym, OUTPUT_N_NONE, false);
+}
+
+void print_symbol_for_listconfig(struct symbol *sym)
+{
+       __print_symbol(stdout, sym, OUTPUT_N, true);
+}
+
+static void print_symbol_for_c(FILE *fp, struct symbol *sym)
+{
+       const char *val;
+       const char *sym_suffix = "";
+       const char *val_prefix = "";
+       char *escaped = NULL;
+
+       if (sym->type == S_UNKNOWN)
+               return;
+
+       val = sym_get_string_value(sym);
 
        switch (sym->type) {
        case S_BOOLEAN:
-       case S_TRISTATE: {
-               const char *suffix = "";
-
-               switch (*value) {
+       case S_TRISTATE:
+               switch (*val) {
                case 'n':
-                       break;
+                       return;
                case 'm':
-                       suffix = "_MODULE";
+                       sym_suffix = "_MODULE";
                        /* fall through */
                default:
-                       fprintf(fp, "#define %s%s%s 1\n",
-                           CONFIG_, sym->name, suffix);
+                       val = "1";
                }
                break;
-       }
-       case S_HEX: {
-               const char *prefix = "";
-
-               if (value[0] != '0' || (value[1] != 'x' && value[1] != 'X'))
-                       prefix = "0x";
-               fprintf(fp, "#define %s%s %s%s\n",
-                   CONFIG_, sym->name, prefix, value);
+       case S_HEX:
+               if (val[0] != '0' || (val[1] != 'x' && val[1] != 'X'))
+                       val_prefix = "0x";
                break;
-       }
        case S_STRING:
-       case S_INT:
-               fprintf(fp, "#define %s%s %s\n",
-                   CONFIG_, sym->name, value);
-               break;
+               escaped = escape_string_value(val);
+               val = escaped;
        default:
                break;
        }
 
-}
-
-static void
-header_print_comment(FILE *fp, const char *value, void *arg)
-{
-       const char *p = value;
-       size_t l;
+       fprintf(fp, "#define %s%s%s %s%s\n", CONFIG_, sym->name, sym_suffix,
+               val_prefix, val);
 
-       fprintf(fp, "/*\n");
-       for (;;) {
-               l = strcspn(p, "\n");
-               fprintf(fp, " *");
-               if (l) {
-                       fprintf(fp, " ");
-                       xfwrite(p, l, 1, fp);
-                       p += l;
-               }
-               fprintf(fp, "\n");
-               if (*p++ == '\0')
-                       break;
-       }
-       fprintf(fp, " */\n");
+       free(escaped);
 }
 
-static struct conf_printer header_printer_cb =
+static void print_symbol_for_rustccfg(FILE *fp, struct symbol *sym)
 {
-       .print_symbol = header_print_symbol,
-       .print_comment = header_print_comment,
-};
+       const char *val;
+       const char *val_prefix = "";
+       char *val_prefixed = NULL;
+       size_t val_prefixed_len;
+       char *escaped = NULL;
 
-static void conf_write_symbol(FILE *fp, struct symbol *sym,
-                             struct conf_printer *printer, void *printer_arg)
-{
-       const char *str;
+       if (sym->type == S_UNKNOWN)
+               return;
+
+       val = sym_get_string_value(sym);
 
        switch (sym->type) {
-       case S_UNKNOWN:
+       case S_BOOLEAN:
+       case S_TRISTATE:
+               /*
+                * We do not care about disabled ones, i.e. no need for
+                * what otherwise are "comments" in other printers.
+                */
+               if (*val == 'n')
+                       return;
+
+               /*
+                * To have similar functionality to the C macro `IS_ENABLED()`
+                * we provide an empty `--cfg CONFIG_X` here in both `y`
+                * and `m` cases.
+                *
+                * Then, the common `fprintf()` below will also give us
+                * a `--cfg CONFIG_X="y"` or `--cfg CONFIG_X="m"`, which can
+                * be used as the equivalent of `IS_BUILTIN()`/`IS_MODULE()`.
+                */
+               fprintf(fp, "--cfg=%s%s\n", CONFIG_, sym->name);
                break;
-       case S_STRING:
-               str = sym_get_string_value(sym);
-               str = sym_escape_string_value(str);
-               printer->print_symbol(fp, sym, str, printer_arg);
-               free((void *)str);
+       case S_HEX:
+               if (val[0] != '0' || (val[1] != 'x' && val[1] != 'X'))
+                       val_prefix = "0x";
                break;
        default:
-               str = sym_get_string_value(sym);
-               printer->print_symbol(fp, sym, str, printer_arg);
+               break;
        }
-}
 
-static void
-conf_write_heading(FILE *fp, struct conf_printer *printer, void *printer_arg)
-{
-       char buf[256];
+       if (strlen(val_prefix) > 0) {
+               val_prefixed_len = strlen(val) + strlen(val_prefix) + 1;
+               val_prefixed = xmalloc(val_prefixed_len);
+               snprintf(val_prefixed, val_prefixed_len, "%s%s", val_prefix, val);
+               val = val_prefixed;
+       }
+
+       /* All values get escaped: the `--cfg` option only takes strings */
+       escaped = escape_string_value(val);
+       val = escaped;
 
-       snprintf(buf, sizeof(buf),
-           "\n"
-           "Automatically generated file; DO NOT EDIT.\n"
-           "%s\n",
-           rootmenu.prompt->text);
+       fprintf(fp, "--cfg=%s%s=%s\n", CONFIG_, sym->name, val);
 
-       printer->print_comment(fp, buf, printer_arg);
+       free(escaped);
+       free(val_prefixed);
 }
 
 /*
@@ -818,7 +890,7 @@ int conf_write_defconfig(const char *filename)
                                                goto next_menu;
                                }
                        }
-                       conf_write_symbol(out, sym, &kconfig_printer_cb, NULL);
+                       print_symbol_for_dotconfig(out, sym);
                }
 next_menu:
                if (menu->list != NULL) {
@@ -878,7 +950,7 @@ int conf_write(const char *name)
        if (!out)
                return 1;
 
-       conf_write_heading(out, &kconfig_printer_cb, NULL);
+       conf_write_heading(out, &comment_style_pound);
 
        if (!conf_get_changed())
                sym_clear_all_valid();
@@ -905,7 +977,7 @@ int conf_write(const char *name)
                                need_newline = false;
                        }
                        sym->flags |= SYMBOL_WRITTEN;
-                       conf_write_symbol(out, sym, &kconfig_printer_cb, NULL);
+                       print_symbol_for_dotconfig(out, sym);
                }
 
 next:
@@ -913,19 +985,20 @@ next:
                        menu = menu->list;
                        continue;
                }
-               if (menu->next)
+
+end_check:
+               if (!menu->sym && menu_is_visible(menu) && menu != &rootmenu &&
+                   menu->prompt->type == P_MENU) {
+                       fprintf(out, "# end of %s\n", menu_get_prompt(menu));
+                       need_newline = true;
+               }
+
+               if (menu->next) {
                        menu = menu->next;
-               else while ((menu = menu->parent)) {
-                       if (!menu->sym && menu_is_visible(menu) &&
-                           menu != &rootmenu) {
-                               str = menu_get_prompt(menu);
-                               fprintf(out, "# end of %s\n", str);
-                               need_newline = true;
-                       }
-                       if (menu->next) {
-                               menu = menu->next;
-                               break;
-                       }
+               } else {
+                       menu = menu->parent;
+                       if (menu)
+                               goto end_check;
                }
        }
        fclose(out);
@@ -955,45 +1028,69 @@ next:
 }
 
 /* write a dependency file as used by kbuild to track dependencies */
-static int conf_write_dep(const char *name)
+static int conf_write_autoconf_cmd(const char *autoconf_name)
 {
+       char name[PATH_MAX], tmp[PATH_MAX];
        struct file *file;
        FILE *out;
+       int ret;
 
-       out = fopen("..config.tmp", "w");
-       if (!out)
-               return 1;
-       fprintf(out, "deps_config := \\\n");
-       for (file = file_list; file; file = file->next) {
-               if (file->next)
-                       fprintf(out, "\t%s \\\n", file->name);
-               else
-                       fprintf(out, "\t%s\n", file->name);
+       ret = snprintf(name, sizeof(name), "%s.cmd", autoconf_name);
+       if (ret >= sizeof(name)) /* check truncation */
+               return -1;
+
+       if (make_parent_dir(name))
+               return -1;
+
+       ret = snprintf(tmp, sizeof(tmp), "%s.cmd.tmp", autoconf_name);
+       if (ret >= sizeof(tmp)) /* check truncation */
+               return -1;
+
+       out = fopen(tmp, "w");
+       if (!out) {
+               perror("fopen");
+               return -1;
        }
-       fprintf(out, "\n%s: \\\n"
-                    "\t$(deps_config)\n\n", conf_get_autoconfig_name());
 
-       env_write_dep(out, conf_get_autoconfig_name());
+       fprintf(out, "deps_config := \\\n");
+       for (file = file_list; file; file = file->next)
+               fprintf(out, "\t%s \\\n", file->name);
+
+       fprintf(out, "\n%s: $(deps_config)\n\n", autoconf_name);
+
+       env_write_dep(out, autoconf_name);
 
        fprintf(out, "\n$(deps_config): ;\n");
+
+       fflush(out);
+       ret = ferror(out); /* error check for all fprintf() calls */
        fclose(out);
+       if (ret)
+               return -1;
+
+       if (rename(tmp, name)) {
+               perror("rename");
+               return -1;
+       }
 
-       if (make_parent_dir(name))
-               return 1;
-       rename("..config.tmp", name);
        return 0;
 }
 
 static int conf_touch_deps(void)
 {
-       const char *name;
+       const char *name, *tmp;
        struct symbol *sym;
        int res, i;
 
-       strcpy(depfile_path, "include/config/");
-       depfile_prefix_len = strlen(depfile_path);
-
        name = conf_get_autoconfig_name();
+       tmp = strrchr(name, '/');
+       depfile_prefix_len = tmp ? tmp - name + 1 : 0;
+       if (depfile_prefix_len + 1 > sizeof(depfile_path))
+               return -1;
+
+       strncpy(depfile_path, name, depfile_prefix_len);
+       depfile_path[depfile_prefix_len] = 0;
+
        conf_read_simple(name, S_DEF_AUTO);
        sym_calc_value(modules_sym);
 
@@ -1056,13 +1153,54 @@ static int conf_touch_deps(void)
        return 0;
 }
 
+static int __conf_write_autoconf(const char *filename,
+                                void (*print_symbol)(FILE *, struct symbol *),
+                                const struct comment_style *comment_style)
+{
+       char tmp[PATH_MAX];
+       FILE *file;
+       struct symbol *sym;
+       int ret, i;
+
+       if (make_parent_dir(filename))
+               return -1;
+
+       ret = snprintf(tmp, sizeof(tmp), "%s.tmp", filename);
+       if (ret >= sizeof(tmp)) /* check truncation */
+               return -1;
+
+       file = fopen(tmp, "w");
+       if (!file) {
+               perror("fopen");
+               return -1;
+       }
+
+       conf_write_heading(file, comment_style);
+
+       for_all_symbols(i, sym)
+               if ((sym->flags & SYMBOL_WRITE) && sym->name)
+                       print_symbol(file, sym);
+
+       fflush(file);
+       /* check possible errors in conf_write_heading() and print_symbol() */
+       ret = ferror(file);
+       fclose(file);
+       if (ret)
+               return -1;
+
+       if (rename(tmp, filename)) {
+               perror("rename");
+               return -1;
+       }
+
+       return 0;
+}
+
 int conf_write_autoconf(int overwrite)
 {
        struct symbol *sym;
-       const char *name;
        const char *autoconf_name = conf_get_autoconfig_name();
-       FILE *out, *out_h;
-       int i;
+       int ret, i;
 
 #ifndef OPENWRT_DOES_NOT_WANT_THIS
        return 0;
@@ -1070,52 +1208,38 @@ int conf_write_autoconf(int overwrite)
        if (!overwrite && is_present(autoconf_name))
                return 0;
 
-       conf_write_dep("include/config/auto.conf.cmd");
+       ret = conf_write_autoconf_cmd(autoconf_name);
+       if (ret)
+               return -1;
 
        if (conf_touch_deps())
                return 1;
 
-       out = fopen(".tmpconfig", "w");
-       if (!out)
-               return 1;
-
-       out_h = fopen(".tmpconfig.h", "w");
-       if (!out_h) {
-               fclose(out);
-               return 1;
-       }
-
-       conf_write_heading(out, &kconfig_printer_cb, NULL);
-       conf_write_heading(out_h, &header_printer_cb, NULL);
-
-       for_all_symbols(i, sym) {
+       for_all_symbols(i, sym)
                sym_calc_value(sym);
-               if (!(sym->flags & SYMBOL_WRITE) || !sym->name)
-                       continue;
 
-               /* write symbols to auto.conf and autoconf.h */
-               conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1);
-               conf_write_symbol(out_h, sym, &header_printer_cb, NULL);
-       }
-       fclose(out);
-       fclose(out_h);
+       ret = __conf_write_autoconf(conf_get_autoheader_name(),
+                                   print_symbol_for_c,
+                                   &comment_style_c);
+       if (ret)
+               return ret;
 
-       name = getenv("KCONFIG_AUTOHEADER");
-       if (!name)
-               name = "include/generated/autoconf.h";
-       if (make_parent_dir(name))
-               return 1;
-       if (rename(".tmpconfig.h", name))
-               return 1;
+       ret = __conf_write_autoconf(conf_get_rustccfg_name(),
+                                   print_symbol_for_rustccfg,
+                                   NULL);
+       if (ret)
+               return ret;
 
-       if (make_parent_dir(autoconf_name))
-               return 1;
        /*
-        * This must be the last step, kbuild has a dependency on auto.conf
-        * and this marks the successful completion of the previous steps.
+        * Create include/config/auto.conf. This must be the last step because
+        * Kbuild has a dependency on auto.conf and this marks the successful
+        * completion of the previous steps.
         */
-       if (rename(".tmpconfig", autoconf_name))
-               return 1;
+       ret = __conf_write_autoconf(conf_get_autoconfig_name(),
+                                   print_symbol_for_autoconf,
+                                   &comment_style_pound);
+       if (ret)
+               return ret;
 
        return 0;
 }
@@ -1125,10 +1249,12 @@ static void (*conf_changed_callback)(void);
 
 void conf_set_changed(bool val)
 {
-       if (conf_changed_callback && conf_changed != val)
-               conf_changed_callback();
+       bool changed = conf_changed != val;
 
        conf_changed = val;
+
+       if (conf_changed_callback && changed)
+               conf_changed_callback();
 }
 
 bool conf_get_changed(void)