On Fri, Aug 23, 2024 at 12:45 PM Luis Chamberlain <mcgrof@xxxxxxxxxx> wrote: > > kconfig is used outside of Linux, and one of the uses of Kconfig is to > also allow kconfig to be used for automation on kdevops by leveraging > a smaller subset of variables for yaml run time for ansible runs. > There is no need to clutter a full yaml file with every single config > we have as we do in the kernel, and so this lets users decide if they > want all or just a few select key symbols as part of the yaml output. > > What this will do is save us the pain of doing the selective transformation > we currently do and let's us only annotate what we need for runtime with > ansible. > > You can test with the Linux kernel config (that's not what we use): > > export KCONFIG_YAMLCFG=".yaml" > export KCONFIG_YAMLCFG_ALL="y" > rm -f .config .yaml > make defconfig > head -10 .yaml > --- > cc_version_text: "gcc (Debian 13.3.0-1) 13.3.0" > cc_is_gcc: True > gcc_version: 130300 > clang_version: 0 > as_is_gnu: True > as_version: 24250 > ld_is_bfd: True > ld_version: 24250 > lld_version: 0 > > You can also use the selective mechanism "output yaml" on any symbol, > so that we only output those. This also paves the way to let us later > use kconfig for direct json transformations directly from the same > kconfig logic. > > Signed-off-by: Luis Chamberlain <mcgrof@xxxxxxxxxx> > --- > > Long ago, I envisioned we could do this so to simplify the addition of > new workflows and remove all the stupid Makefile transformations we have > in kdevops today to generate extra_vars.yaml. > > Feedback welcome. > > I completley understand if this is not desirable upstream. Agree. Not desirable upstream. > However > kdevops does aim to track kconfig upstream using a git sub tree already, > it follows linux-next, and so getting support upstream is easier rather > than going with a branch for our git subtree. > > The only puzzle I have is why when we use the selective method, we end > up with tons of empty lines.. Any ideas? Example of how one can use this > this on random symbols in case it is not clear, with the selective > method: > > If we use this for example: > > diff --git a/fs/efivarfs/Kconfig b/fs/efivarfs/Kconfig > index edec8a19c894..2faf651725dc 100644 > --- a/fs/efivarfs/Kconfig > +++ b/fs/efivarfs/Kconfig > @@ -3,6 +3,7 @@ config EFIVAR_FS > tristate "EFI Variable filesystem" > depends on EFI > default m > + output yaml > help > efivarfs is a replacement filesystem for the old EFI > variable support via sysfs, as it doesn't suffer from the > > In this case we'd end up with just: > > export KCONFIG_YAMLCFG=".yaml" > unset KCONFIG_YAMLCFG_ALL > rm -f .config .yaml > make defconfig > cat .yaml | cat -s > --- > > efivar_fs: m > > Thoughts? > > scripts/kconfig/confdata.c | 152 ++++++++++++++++++++++++++++++++++++- > scripts/kconfig/expr.h | 1 + > scripts/kconfig/lexer.l | 2 + > scripts/kconfig/parser.y | 11 +++ > 4 files changed, 163 insertions(+), 3 deletions(-) > > diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c > index 76193ce5a792..78d188320040 100644 > --- a/scripts/kconfig/confdata.c > +++ b/scripts/kconfig/confdata.c > @@ -233,6 +233,20 @@ static const char *conf_get_rustccfg_name(void) > return name ? name : "include/generated/rustc_cfg"; > } > > +static bool conf_yaml_enable_all(void) > +{ > + char *name = getenv("KCONFIG_YAMLCFG_ALL"); > + > + return name ? true: false; > +} > + > +static const char *conf_get_yaml_config_name(void) > +{ > + char *name = getenv("KCONFIG_YAMLCFG"); > + > + return name ? name : NULL; > +} > + > static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p) > { > char *p2; > @@ -623,9 +637,103 @@ static void __print_symbol(FILE *fp, struct symbol *sym, enum output_n output_n, > free(escaped); > } > > -static void print_symbol_for_dotconfig(FILE *fp, struct symbol *sym) > +static char *conf_name_to_yaml(struct symbol *sym) > +{ > + const char *name = sym->name; > + size_t len = strlen(name); > + size_t i, j = 0; > + char *yaml_name = (char *) malloc(len + 1); > + > + if (!yaml_name) > + return NULL; > + > + for (i = 0; i < len; i++) { > + if (name[i] == '_') > + yaml_name[j++] = '_'; > + else > + yaml_name[j++] = tolower(name[i]); > + } > + > + yaml_name[j] = '\0'; > + > + return yaml_name; > +} > + > +static char *conf_value_to_yaml(struct symbol *sym, const char *val) > +{ > + char *yaml_value = NULL; > + > + switch (sym->type) { > + case S_INT: > + yaml_value = strdup(val); > + break; > + case S_HEX: > + asprintf(&yaml_value, "0x%s", val); > + break; > + case S_STRING: > + /* Wrap strings in quotes */ > + asprintf(&yaml_value, "\"%s\"", val); > + break; > + case S_BOOLEAN: > + case S_TRISTATE: > + if (strcmp(val, "y") == 0) > + yaml_value = strdup("True"); > + else if (strcmp(val, "n") == 0) > + yaml_value = strdup("False"); > + else > + yaml_value = strdup(val); /* m in tristate */ > + break; > + default: > + /* In case type is unknown */ > + yaml_value = strdup(val); > + break; > + } > + > + return yaml_value; > +} > + > +static void __print_yaml_symbol(FILE *fp, struct symbol *sym, > + enum output_n output_n, > + bool escape_string) > +{ > + const char *val; > + char *yaml_config = NULL; > + char *yaml_config_value = NULL; > + > + if (!fp || sym->type == S_UNKNOWN) > + return; > + if (!conf_yaml_enable_all() && !(sym->flags & SYMBOL_YAML)) > + return; > + > + val = sym_get_string_value(sym); > + > + yaml_config = conf_name_to_yaml(sym); > + if (!yaml_config) > + return; > + > + yaml_config_value = conf_value_to_yaml(sym, val); > + if (!yaml_config_value) { > + free(yaml_config); > + return; > + } > + > + if ((sym->type == S_BOOLEAN || sym->type == S_TRISTATE) && > + output_n != OUTPUT_N && *val == 'n') { > + if (output_n == OUTPUT_N_AS_UNSET && conf_yaml_enable_all()) > + fprintf(fp, "# %s: False\n", yaml_config); > + return; > + } > + > + fprintf(fp, "%s: %s\n", yaml_config, yaml_config_value); > + > + free(yaml_config); > + free(yaml_config_value); > +} > + > +static void print_symbol_for_dotconfig(FILE *fp, FILE *yaml, struct symbol *sym) > { > __print_symbol(fp, sym, OUTPUT_N_AS_UNSET, true); > + __print_yaml_symbol(yaml, sym, OUTPUT_N_AS_UNSET, true); > } > > static void print_symbol_for_autoconf(FILE *fp, struct symbol *sym) > @@ -748,11 +856,24 @@ int conf_write_defconfig(const char *filename) > struct symbol *sym; > struct menu *menu; > FILE *out; > + FILE *yaml_out = NULL; > + const char *yaml_config = NULL; > + > + yaml_config = conf_get_yaml_config_name(); > > out = fopen(filename, "w"); > if (!out) > return 1; > > + if (yaml_config) { > + yaml_out = fopen(yaml_config, "w"); > + if (!yaml_out) { > + fclose(out); > + return 1; > + } > + fprintf(yaml_out, "---\n"); > + } > + > sym_clear_all_valid(); > > menu_for_each_entry(menu) { > @@ -783,21 +904,25 @@ int conf_write_defconfig(const char *filename) > if (sym == ds && sym_get_tristate_value(sym) == yes) > continue; > } > - print_symbol_for_dotconfig(out, sym); > + print_symbol_for_dotconfig(out, yaml_out, sym); > } > fclose(out); > + if (yaml_out) > + fclose(yaml_out); > return 0; > } > > int conf_write(const char *name) > { > FILE *out; > + FILE *yaml_out = NULL; > struct symbol *sym; > struct menu *menu; > const char *str; > char tmpname[PATH_MAX + 1], oldname[PATH_MAX + 1]; > char *env; > bool need_newline = false; > + const char *yaml_config; > > if (!name) > name = conf_get_configname(); > @@ -815,18 +940,33 @@ int conf_write(const char *name) > if (make_parent_dir(name)) > return -1; > > + yaml_config = conf_get_yaml_config_name(); > + > env = getenv("KCONFIG_OVERWRITECONFIG"); > if (env && *env) { > *tmpname = 0; > out = fopen(name, "w"); > + if (yaml_config) > + yaml_out = fopen(yaml_config, "w"); > } else { > snprintf(tmpname, sizeof(tmpname), "%s.%d.tmp", > name, (int)getpid()); > out = fopen(tmpname, "w"); > + if (yaml_config) > + yaml_out = fopen(yaml_config, "w"); > } > if (!out) > return 1; > > + if (yaml_config) { > + if (!yaml_out) { > + fclose(out); > + return 1; > + } > + fprintf(yaml_out, "---\n"); > + } > + > + > conf_write_heading(out, &comment_style_pound); > > if (!conf_get_changed()) > @@ -852,9 +992,11 @@ int conf_write(const char *name) > if (need_newline) { > fprintf(out, "\n"); > need_newline = false; > + if (yaml_config) > + fprintf(yaml_out, "\n"); > } > sym->flags |= SYMBOL_WRITTEN; > - print_symbol_for_dotconfig(out, sym); > + print_symbol_for_dotconfig(out, yaml_out, sym); > } > > next: > @@ -879,6 +1021,8 @@ int conf_write(const char *name) > } > } > fclose(out); > + if (yaml_out) > + fclose(yaml_out); > > for_all_symbols(sym) > sym->flags &= ~SYMBOL_WRITTEN; > @@ -898,6 +1042,8 @@ int conf_write(const char *name) > } > > conf_message("configuration written to %s", name); > + if (yaml_config) > + conf_message("yaml configuration written to %s", yaml_config); > > conf_set_changed(false); > > diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h > index 2bc96cd28253..88e8a2a06f67 100644 > --- a/scripts/kconfig/expr.h > +++ b/scripts/kconfig/expr.h > @@ -132,6 +132,7 @@ struct symbol { > #define SYMBOL_CHECK 0x0008 /* used during dependency checking */ > #define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */ > #define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */ > +#define SYMBOL_YAML 0x0400 /* write symbol to file (KCONFIG_YAMLCFG) */ > #define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .config */ > #define SYMBOL_CHECKED 0x2000 /* used during dependency checking */ > #define SYMBOL_WARNED 0x8000 /* warning has been issued */ > diff --git a/scripts/kconfig/lexer.l b/scripts/kconfig/lexer.l > index 8dd597c4710d..190937070fb1 100644 > --- a/scripts/kconfig/lexer.l > +++ b/scripts/kconfig/lexer.l > @@ -120,6 +120,7 @@ n [A-Za-z0-9_-] > "menuconfig" return T_MENUCONFIG; > "modules" return T_MODULES; > "on" return T_ON; > +"output" return T_OUTPUT; > "prompt" return T_PROMPT; > "range" return T_RANGE; > "select" return T_SELECT; > @@ -127,6 +128,7 @@ n [A-Za-z0-9_-] > "string" return T_STRING; > "tristate" return T_TRISTATE; > "visible" return T_VISIBLE; > +"yaml" return T_YAML; > "||" return T_OR; > "&&" return T_AND; > "=" return T_EQUAL; > diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y > index 61900feb4254..f298f052dddc 100644 > --- a/scripts/kconfig/parser.y > +++ b/scripts/kconfig/parser.y > @@ -69,6 +69,7 @@ struct menu *current_menu, *current_entry, *current_choice; > %token T_MODULES > %token T_ON > %token T_OPEN_PAREN > +%token T_OUTPUT > %token T_PLUS_EQUAL > %token T_PROMPT > %token T_RANGE > @@ -77,6 +78,7 @@ struct menu *current_menu, *current_entry, *current_choice; > %token T_STRING > %token T_TRISTATE > %token T_VISIBLE > +%token T_YAML > %token T_EOL > %token <string> T_ASSIGN_VAL > > @@ -234,6 +236,15 @@ config_option: T_MODULES T_EOL > modules_sym = current_entry->sym; > }; > > +/* When we want to output symbols as part of an additional output formats */ > + > +config_option: T_OUTPUT T_YAML T_EOL > +{ > + printd(DEBUG_PARSE, "%s will be part of the yaml output file %s:%d:\n", > + current_entry->sym->name, cur_filename, cur_lineno); > + current_entry->sym->flags |= SYMBOL_YAML; > +}; > + > /* choice entry */ > > choice: T_CHOICE T_EOL > -- > 2.43.0 > -- Best Regards Masahiro Yamada