From 0a1eab129fedb4281e65c845b04be02b53c99f9c Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Tue, 24 Sep 2024 20:47:22 +0200 Subject: kconfig: use memcmp instead of deprecated bcmp bcmp() was removed in POSIX.1-2008. This commit replaces bcmp() with memcmp(). This allows Kconfig to link with C libraries that do not provide bcmp(), such as Android bionic libc. Signed-off-by: Thomas Meyer Reviewed-by: Miguel Ojeda Reported-by: Abhigyan Ghosh [masahiro: update commit description] Signed-off-by: Masahiro Yamada --- scripts/kconfig/confdata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index ac95661a1c9d..9599a0408862 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -77,7 +77,7 @@ static bool is_same(const char *file1, const char *file2) if (map2 == MAP_FAILED) goto close2; - if (bcmp(map1, map2, st1.st_size)) + if (memcmp(map1, map2, st1.st_size)) goto close2; ret = true; -- cgit v1.2.3 From 694174f94ebeeb5ec5cc0e9de9b40c82057e1d95 Mon Sep 17 00:00:00 2001 From: "Yann E. MORIN" Date: Thu, 14 Nov 2013 00:53:32 +0100 Subject: kconfig: lxdialog: fix 'space' to (de)select options In case a menu has comment without letters/numbers (eg. characters matching the regexp '^[^[:alpha:][:digit:]]+$', for example - or *), hitting space will cycle through those comments, rather than selecting/deselecting the currently-highlighted option. This is the behaviour of hitting any letter/digit: jump to the next option which prompt starts with that letter. The only letters that do not behave as such are 'y' 'm' and 'n'. Prompts that start with one of those three letters are instead matched on the first letter that is not 'y', 'm' or 'n'. Fix that by treating 'space' as we treat y/m/n, ie. as an action key, not as shortcut to jump to prompt. Signed-off-by: Yann E. MORIN Signed-off-by: Peter Korsgaard Signed-off-by: Cherniaev Andrei [masahiro: took from Buildroot, adjusted the commit subject] Signed-off-by: Masahiro Yamada --- scripts/kconfig/lxdialog/menubox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/lxdialog/menubox.c b/scripts/kconfig/lxdialog/menubox.c index 6e6244df0c56..d4c19b7beebb 100644 --- a/scripts/kconfig/lxdialog/menubox.c +++ b/scripts/kconfig/lxdialog/menubox.c @@ -264,7 +264,7 @@ do_resize: if (key < 256 && isalpha(key)) key = tolower(key); - if (strchr("ynmh", key)) + if (strchr("ynmh ", key)) i = max_choice; else { for (i = choice + 1; i < max_choice; i++) { -- cgit v1.2.3 From b04c7e88bcf5ddcd15e2c620b802c28848f437bb Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:49 +0900 Subject: kconfig: set MENU_CHANGED to choice when the selected member is changed In gconf, choice entries display the selected symbol in the 'Value' column, but it is not updated when the selected symbol is changed. Set the MENU_CHANGED flag, so it is updated. Signed-off-by: Masahiro Yamada --- scripts/kconfig/symbol.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'scripts') diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index d57f8cbba291..26ab10c0fd76 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -195,6 +195,10 @@ static void sym_set_changed(struct symbol *sym) list_for_each_entry(menu, &sym->menus, link) menu->flags |= MENU_CHANGED; + + menu = sym_get_choice_menu(sym); + if (menu) + menu->flags |= MENU_CHANGED; } static void sym_set_all_changed(void) -- cgit v1.2.3 From cd5f1534a37e0b05733a8714195ec90474c20e82 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:50 +0900 Subject: kconfig: qconf: do not show checkbox icon for choice When you select "Show All Options" or "Show Prompt Options", choice entries display a check box icon, but this has no point because choice is always y since commit fde192511bdb ("kconfig: remove tristate choice support"). Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/qconf.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index eaa465b0ccf9..546738a5c3b1 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -92,7 +92,6 @@ void ConfigItem::updateMenu(void) { ConfigList* list; struct symbol* sym; - struct property *prop; QString prompt; int type; tristate expr; @@ -105,11 +104,10 @@ void ConfigItem::updateMenu(void) } sym = menu->sym; - prop = menu->prompt; prompt = menu_get_prompt(menu); - if (prop) switch (prop->type) { - case P_MENU: + switch (menu->type) { + case M_MENU: if (list->mode == singleMode) { /* a menuconfig entry is displayed differently * depending whether it's at the view root or a child. @@ -123,10 +121,13 @@ void ConfigItem::updateMenu(void) setIcon(promptColIdx, QIcon()); } goto set_prompt; - case P_COMMENT: + case M_COMMENT: setIcon(promptColIdx, QIcon()); prompt = "*** " + prompt + " ***"; goto set_prompt; + case M_CHOICE: + setIcon(promptColIdx, QIcon()); + goto set_prompt; default: ; } -- cgit v1.2.3 From 604f5b2127fb76e15dcc6dabbd73b541817a2fba Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:51 +0900 Subject: kconfig: qconf: show selected choice in the Value column It is useful to display the selected choice's value in the Value column. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/qconf.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index 546738a5c3b1..68640e507ec4 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -127,6 +127,9 @@ void ConfigItem::updateMenu(void) goto set_prompt; case M_CHOICE: setIcon(promptColIdx, QIcon()); + sym = sym_calc_choice(menu); + if (sym) + setText(dataColIdx, sym->name); goto set_prompt; default: ; @@ -189,7 +192,11 @@ void ConfigItem::testUpdateMenu(void) if (!menu) return; - sym_calc_value(menu->sym); + if (menu->type == M_CHOICE) + sym_calc_choice(menu); + else + sym_calc_value(menu->sym); + if (menu->flags & MENU_CHANGED) { /* the menu entry changed, so update all list items */ menu->flags &= ~MENU_CHANGED; -- cgit v1.2.3 From 3c292cd0047c8758a2db7a44e441314e78b4db00 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:52 +0900 Subject: kconfig: rename menu_get_parent_menu() to menu_get_menu_or_parent_menu() The current menu_get_parent_menu() does not always return the parent menu; if the given argument is itself a menu, it returns that menu. Rename this function to better reflect this behavior. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/conf.c | 2 +- scripts/kconfig/lkc.h | 2 +- scripts/kconfig/menu.c | 8 +++++++- scripts/kconfig/qconf.cc | 6 +++--- 4 files changed, 12 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 8abe57041955..a7b44cd8ae14 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -594,7 +594,7 @@ static void check_conf(struct menu *menu) default: if (!conf_cnt++) printf("*\n* Restart config...\n*\n"); - rootEntry = menu_get_parent_menu(menu); + rootEntry = menu_get_menu_or_parent_menu(menu); conf(rootEntry); break; } diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index fbc907f75eac..5cc85c3d4aaa 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -97,7 +97,7 @@ bool menu_is_empty(struct menu *menu); bool menu_is_visible(struct menu *menu); bool menu_has_prompt(const struct menu *menu); const char *menu_get_prompt(const struct menu *menu); -struct menu *menu_get_parent_menu(struct menu *menu); +struct menu *menu_get_menu_or_parent_menu(struct menu *menu); int get_jump_key_char(void); struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); void menu_get_ext_help(struct menu *menu, struct gstr *help); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 7d48a692bd27..ccb690bbf05d 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -575,7 +575,13 @@ const char *menu_get_prompt(const struct menu *menu) return NULL; } -struct menu *menu_get_parent_menu(struct menu *menu) +/** + * menu_get_menu_or_parent_menu - return the parent menu or the menu itself + * @menu: pointer to the menu + * return: the parent menu. If the given argument is already a menu, return + * itself. + */ +struct menu *menu_get_menu_or_parent_menu(struct menu *menu) { enum prop_type type; diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index 68640e507ec4..dc056b0a8fde 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -577,7 +577,7 @@ void ConfigList::setParentMenu(void) oldroot = rootEntry; if (rootEntry == &rootmenu) return; - setRootMenu(menu_get_parent_menu(rootEntry->parent)); + setRootMenu(menu_get_menu_or_parent_menu(rootEntry->parent)); QTreeWidgetItemIterator it(this); while (*it) { @@ -1540,7 +1540,7 @@ void ConfigMainWindow::setMenuLink(struct menu *menu) switch (configList->mode) { case singleMode: list = configList; - parent = menu_get_parent_menu(menu); + parent = menu_get_menu_or_parent_menu(menu); if (!parent) return; list->setRootMenu(parent); @@ -1551,7 +1551,7 @@ void ConfigMainWindow::setMenuLink(struct menu *menu) configList->clearSelection(); list = configList; } else { - parent = menu_get_parent_menu(menu->parent); + parent = menu_get_menu_or_parent_menu(menu->parent); if (!parent) return; -- cgit v1.2.3 From 7d1bfaa457686b1e791de03450a3d49f28bdd022 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:53 +0900 Subject: kconfig: re-add menu_get_parent_menu() that returns parent menu This helper returns the parent menu, or NULL if there is no parent. The main difference from the previous version is that it always returns the parent menu even when the given argument is itself a menu. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/lkc.h | 1 + scripts/kconfig/menu.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) (limited to 'scripts') diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 5cc85c3d4aaa..37b606c74bff 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -97,6 +97,7 @@ bool menu_is_empty(struct menu *menu); bool menu_is_visible(struct menu *menu); bool menu_has_prompt(const struct menu *menu); const char *menu_get_prompt(const struct menu *menu); +struct menu *menu_get_parent_menu(struct menu *menu); struct menu *menu_get_menu_or_parent_menu(struct menu *menu); int get_jump_key_char(void); struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index ccb690bbf05d..a5e5b4fdcd93 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -575,6 +575,20 @@ const char *menu_get_prompt(const struct menu *menu) return NULL; } +/** + * menu_get_parent_menu - return the parent menu or NULL + * @menu: pointer to the menu + * return: the parent menu, or NULL if there is no parent. + */ +struct menu *menu_get_parent_menu(struct menu *menu) +{ + for (menu = menu->parent; menu; menu = menu->parent) + if (menu->type == M_MENU) + return menu; + + return NULL; +} + /** * menu_get_menu_or_parent_menu - return the parent menu or the menu itself * @menu: pointer to the menu -- cgit v1.2.3 From 2f2d60f489f0b2410f33103fa42296f7466673e0 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:54 +0900 Subject: kconfig: gconf: make columns resizable The variable "resizeable" is a typo and always set to FALSE, resulting in dead code in init_right_tree(). It is unclear column resizing should be disabled. Enable it. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index c0f46f189060..a3978d3420d1 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -30,7 +30,6 @@ static gint view_mode = FULL_VIEW; static gboolean show_name = TRUE; static gboolean show_range = TRUE; static gboolean show_value = TRUE; -static gboolean resizeable = FALSE; static int opt_mode = OPT_NORMAL; GtkWidget *main_wnd = NULL; @@ -312,11 +311,9 @@ static void init_right_tree(void) column = gtk_tree_view_get_column(view, COL_VALUE); gtk_tree_view_column_set_visible(column, show_value); - if (resizeable) { - for (i = 0; i < COL_VALUE; i++) { - column = gtk_tree_view_get_column(view, i); - gtk_tree_view_column_set_resizable(column, TRUE); - } + for (i = 0; i < COL_VALUE; i++) { + column = gtk_tree_view_get_column(view, i); + gtk_tree_view_column_set_resizable(column, TRUE); } sel = gtk_tree_view_get_selection(view); -- cgit v1.2.3 From f72ed4c6a375e52a3f4b75615e4a89d29d8acea7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:55 +0900 Subject: kconfig: gconf: fix potential memory leak in renderer_edited() If gtk_tree_model_get_iter() fails, gtk_tree_path_free() is not called. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index a3978d3420d1..769f38307f34 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -745,7 +745,7 @@ static void renderer_edited(GtkCellRendererText * cell, struct symbol *sym; if (!gtk_tree_model_get_iter(model2, &iter, path)) - return; + goto free; gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); sym = menu->sym; @@ -757,6 +757,7 @@ static void renderer_edited(GtkCellRendererText * cell, update_tree(&rootmenu, NULL); +free: gtk_tree_path_free(path); } -- cgit v1.2.3 From 02bb13bd6c55bffb53de8da1eae87533d332235d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:56 +0900 Subject: kconfig: gconf: always destroy dialog in on_window1_delete_event() When gtk_dialog_run() returns GTK_RESPONSE_YES or GTK_RESPONSE_NO, gtk_widget_destroy() is not called, resulting in a memory leak. It is better to always destroy the dialog, even if the application is about to exit. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 769f38307f34..52d439a5119b 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -378,6 +378,7 @@ gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event, { GtkWidget *dialog, *label; gint result; + gint ret = FALSE; if (!conf_get_changed()) return FALSE; @@ -404,17 +405,19 @@ gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event, switch (result) { case GTK_RESPONSE_YES: on_save_activate(NULL, NULL); - return FALSE; + break; case GTK_RESPONSE_NO: - return FALSE; + break; case GTK_RESPONSE_CANCEL: case GTK_RESPONSE_DELETE_EVENT: default: - gtk_widget_destroy(dialog); - return TRUE; + ret = TRUE; + break; } - return FALSE; + gtk_widget_destroy(dialog); + + return ret; } -- cgit v1.2.3 From bff576a2a90954c6b242bf02d915c49f52b1e3cb Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:57 +0900 Subject: kconfig: gconf: remove old #ifdef GTK_CHECK_VERSION Remove old code. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 52d439a5119b..b2a0208b0a5f 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -849,16 +849,12 @@ on_treeview2_button_press_event(GtkWidget * widget, struct menu *menu; gint col; -#if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK gint tx = (gint) event->x; gint ty = (gint) event->y; gint cx, cy; gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, &cy); -#else - gtk_tree_view_get_cursor(view, &path, &column); -#endif if (path == NULL) return FALSE; -- cgit v1.2.3 From ab2924ab5e75380b007fad1fded809b5ba650b76 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:58 +0900 Subject: kconfig: gconf: remove empty if-block This if-block is empty. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index b2a0208b0a5f..7960c456e3b9 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -913,8 +913,6 @@ on_treeview2_key_press_event(GtkWidget * widget, gtk_tree_view_expand_row(view, path, FALSE); return TRUE; } - if (event->keyval == GDK_KP_Enter) { - } if (widget == tree1_w) return FALSE; -- cgit v1.2.3 From 5575df3d3a216860db720ed5d3d1dcef33ab4d6d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:59 +0900 Subject: kconfig: gconf: remove meaningless code in init_main_window() The 'widget' variable is set, but not used in later code. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 7960c456e3b9..4b5befa4f685 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -129,7 +129,6 @@ static void init_main_window(const gchar *glade_file) conf_set_changed_callback(conf_changed); style = gtk_widget_get_style(main_wnd); - widget = glade_xml_get_widget(xml, "toolbar1"); replace_button_icon(xml, main_wnd->window, style, "button4", (gchar **) xpm_single_view); -- cgit v1.2.3 From 08726436886e05c46efcb0655018a74c534ddead Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:00 +0900 Subject: kconfig: gconf: remove unneeded gtk_tree_view_set_headers_visible() calls The headers are visible by default. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 4b5befa4f685..32e5e9054846 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -189,7 +189,6 @@ static void init_left_tree(void) GtkTreeViewColumn *column; gtk_tree_view_set_model(view, model1); - gtk_tree_view_set_headers_visible(view, TRUE); gtk_tree_view_set_rules_hint(view, TRUE); column = gtk_tree_view_column_new(); @@ -232,7 +231,6 @@ static void init_right_tree(void) gint i; gtk_tree_view_set_model(view, model2); - gtk_tree_view_set_headers_visible(view, TRUE); gtk_tree_view_set_rules_hint(view, TRUE); column = gtk_tree_view_column_new(); -- cgit v1.2.3 From ede0a43249d47660ca977c90a279b6cfc9da314a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:01 +0900 Subject: kconfig: gconf: remove gtk_tree_view_column_set_visible() calls The columns are visible by default. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 32e5e9054846..a027f0f10af9 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -297,17 +297,6 @@ static void init_right_tree(void) g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(renderer_edited), NULL); - column = gtk_tree_view_get_column(view, COL_NAME); - gtk_tree_view_column_set_visible(column, show_name); - column = gtk_tree_view_get_column(view, COL_NO); - gtk_tree_view_column_set_visible(column, show_range); - column = gtk_tree_view_get_column(view, COL_MOD); - gtk_tree_view_column_set_visible(column, show_range); - column = gtk_tree_view_get_column(view, COL_YES); - gtk_tree_view_column_set_visible(column, show_range); - column = gtk_tree_view_get_column(view, COL_VALUE); - gtk_tree_view_column_set_visible(column, show_value); - for (i = 0; i < COL_VALUE; i++) { column = gtk_tree_view_get_column(view, i); gtk_tree_view_column_set_resizable(column, TRUE); -- cgit v1.2.3 From dc1de6c03bc67ff918d904c7f239eaebea34b99b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:02 +0900 Subject: kconfig: gconf: remove gtk_widget_realize() calls This function is primarily used in widget implementations, and isn't very useful otherwise. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index a027f0f10af9..3f9b9957f089 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -215,7 +215,6 @@ static void init_left_tree(void) sel = gtk_tree_view_get_selection(view); gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); - gtk_widget_realize(tree1_w); } static void renderer_edited(GtkCellRendererText * cell, @@ -967,7 +966,6 @@ on_treeview1_button_press_event(GtkWidget * widget, display_tree_part(); } - gtk_widget_realize(tree2_w); gtk_tree_view_set_cursor(view, path, NULL, FALSE); gtk_widget_grab_focus(tree2_w); -- cgit v1.2.3 From ace8bee8369c209b647600c3dc28b529e4f44966 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:03 +0900 Subject: kconfig: gconf: remove gtk_tree_view_set_rules_hint() calls The use of the this function is not recommended, and it has been deprecated since GTK 3.14. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.14.0/gtk/gtktreeview.c#L11891 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 3f9b9957f089..4bbc8f87deb6 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -189,7 +189,6 @@ static void init_left_tree(void) GtkTreeViewColumn *column; gtk_tree_view_set_model(view, model1); - gtk_tree_view_set_rules_hint(view, TRUE); column = gtk_tree_view_column_new(); gtk_tree_view_append_column(view, column); @@ -230,7 +229,6 @@ static void init_right_tree(void) gint i; gtk_tree_view_set_model(view, model2); - gtk_tree_view_set_rules_hint(view, TRUE); column = gtk_tree_view_column_new(); gtk_tree_view_append_column(view, column); -- cgit v1.2.3 From f931a5d37a17f941492392b13f0ad67b1bac8bef Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:04 +0900 Subject: kconfig: gconf: remove unnecessary gtk_set_locale() call gtk_set_locale() has been deprecated since version 2.24, and setlocale() should be used directly. [1] However, gtk_init() automatically does this, so there is typically no point in calling this function. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gtk/gtkmain.c#L1152 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 4bbc8f87deb6..3e632a325c10 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1367,7 +1367,6 @@ int main(int ac, char *av[]) gchar *glade_file; /* GTK stuffs */ - gtk_set_locale(); gtk_init(&ac, &av); glade_init(); -- cgit v1.2.3 From a54b0397d36706ce6f60e0e56709ad94791eda45 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:05 +0900 Subject: kconfig: gconf: remove internal-child="image" nodes from glade These nodes do not appear to serve anything useful. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.glade | 84 --------------------------------------------- 1 file changed, 84 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index aa483cb32755..19b80f2ec1ff 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -48,18 +48,6 @@ True - - - - True - gtk-open - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -71,18 +59,6 @@ True - - - - True - gtk-save - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -93,18 +69,6 @@ Save _as True - - - - True - gtk-save-as - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -121,18 +85,6 @@ True - - - - True - gtk-quit - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -244,18 +196,6 @@ True - - - - True - gtk-dialog-question - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -266,18 +206,6 @@ True - - - - True - gtk-properties - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -287,18 +215,6 @@ _License True - - - - True - gtk-justify-fill - 1 - 0.5 - 0.5 - 0 - 0 - - -- cgit v1.2.3 From b3841b501c4a58ba20f190afd25aa4b93cd664f9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:06 +0900 Subject: kconfig: gconf: remove parents[] array and indent variable The parents[] array is used to store the GtkTreeIter of parent nodes, but this can be simplified: we can pass a GtkTreeIter pointer down when _display_tree() recurses. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 49 ++++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 35 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 3e632a325c10..432a467e3250 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -47,8 +47,6 @@ GdkColor color; GtkTreeStore *tree1, *tree2, *tree; GtkTreeModel *model1, *model2; -static GtkTreeIter *parents[256]; -static gint indent; static struct menu *current; // current node for SINGLE view static struct menu *browsed; // browsed node for SPLIT view @@ -153,8 +151,6 @@ static void init_main_window(const gchar *glade_file) static void init_tree_model(void) { - gint i; - tree = tree2 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, @@ -166,9 +162,6 @@ static void init_tree_model(void) G_TYPE_BOOLEAN); model2 = GTK_TREE_MODEL(tree2); - for (parents[0] = NULL, i = 1; i < 256; i++) - parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter)); - tree1 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, @@ -1131,18 +1124,6 @@ static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row) g_object_unref(pix); } - -/* Add a node to the tree */ -static void place_node(struct menu *menu, char **row) -{ - GtkTreeIter *parent = parents[indent - 1]; - GtkTreeIter *node = parents[indent]; - - gtk_tree_store_append(tree, node, parent); - set_node(node, menu, row); -} - - /* Find a node in the GTK+ tree */ static GtkTreeIter found; @@ -1193,9 +1174,6 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) struct symbol *sym; struct menu *menu1, *menu2; - if (src == &rootmenu) - indent = 1; - valid = gtk_tree_model_iter_children(model2, child2, dst); for (child1 = src->list; child1; child1 = child1->next) { @@ -1253,9 +1231,7 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) set_node(child2, menu1, fill_row(menu1)); } - indent++; update_tree(child1, child2); - indent--; valid = gtk_tree_model_iter_next(model2, child2); } @@ -1263,16 +1239,15 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) /* Display the whole tree (single/split/full view) */ -static void display_tree(struct menu *menu) +static void _display_tree(struct menu *menu, GtkTreeIter *parent) { struct property *prop; struct menu *child; enum prop_type ptype; + GtkTreeIter iter; - if (menu == &rootmenu) { - indent = 1; + if (menu == &rootmenu) current = &rootmenu; - } for (child = menu->list; child; child = child->next) { prop = child->prompt; @@ -1290,8 +1265,10 @@ static void display_tree(struct menu *menu) if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) || (opt_mode == OPT_PROMPT && menu_has_prompt(child)) || - (opt_mode == OPT_ALL && menu_get_prompt(child))) - place_node(child, fill_row(child)); + (opt_mode == OPT_ALL && menu_get_prompt(child))) { + gtk_tree_store_append(tree, &iter, parent); + set_node(&iter, child, fill_row(child)); + } if ((view_mode != FULL_VIEW) && (ptype == P_MENU) && (tree == tree2)) @@ -1308,14 +1285,16 @@ static void display_tree(struct menu *menu) if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) || (view_mode == FULL_VIEW) - || (view_mode == SPLIT_VIEW)) { - indent++; - display_tree(child); - indent--; - } + || (view_mode == SPLIT_VIEW)) + _display_tree(child, &iter); } } +static void display_tree(struct menu *menu) +{ + _display_tree(menu, NULL); +} + /* Display a part of the tree starting at current node (single/split view) */ static void display_tree_part(void) { -- cgit v1.2.3 From 9b8338fd45fc10961130bc2477aa72e4484e2732 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:07 +0900 Subject: kconfig: gconf: remove unnecessary NULL checks for tree1 and tree2 The tree1 and tree2 variables are initialized earlier in init_tree_model(), so the NULL checks are redundant. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 432a467e3250..2ab000adcced 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -678,8 +678,7 @@ void on_split_clicked(GtkButton * button, gpointer user_data) gtk_widget_show(tree1_w); gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); gtk_paned_set_position(GTK_PANED(hpaned), w / 2); - if (tree2) - gtk_tree_store_clear(tree2); + gtk_tree_store_clear(tree2); display_list(); /* Disable back btn, like in full mode. */ @@ -691,8 +690,7 @@ void on_full_clicked(GtkButton * button, gpointer user_data) { view_mode = FULL_VIEW; gtk_widget_hide(tree1_w); - if (tree2) - gtk_tree_store_clear(tree2); + gtk_tree_store_clear(tree2); display_tree(&rootmenu); gtk_widget_set_sensitive(back_btn, FALSE); } @@ -1298,8 +1296,7 @@ static void display_tree(struct menu *menu) /* Display a part of the tree starting at current node (single/split view) */ static void display_tree_part(void) { - if (tree2) - gtk_tree_store_clear(tree2); + gtk_tree_store_clear(tree2); if (view_mode == SINGLE_VIEW) display_tree(current); else if (view_mode == SPLIT_VIEW) @@ -1312,8 +1309,7 @@ static void display_tree_part(void) /* Display the list in the left frame (split view) */ static void display_list(void) { - if (tree1) - gtk_tree_store_clear(tree1); + gtk_tree_store_clear(tree1); tree = tree1; display_tree(&rootmenu); -- cgit v1.2.3 From 8e3136eb27211eaf0560543dd0ee4698c6eb751f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:08 +0900 Subject: kconfig: gconf: remove unneeded variable in on_split_clicked() The height of the window is not used here. Passing NULL to gtk_window_get_default_size() is allowed. [1] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gtk/gtkwindow.c#L3974 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 2ab000adcced..c78eded5c01b 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -673,10 +673,10 @@ void on_single_clicked(GtkButton * button, gpointer user_data) void on_split_clicked(GtkButton * button, gpointer user_data) { - gint w, h; + gint w; view_mode = SPLIT_VIEW; gtk_widget_show(tree1_w); - gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, NULL); gtk_paned_set_position(GTK_PANED(hpaned), w / 2); gtk_tree_store_clear(tree2); display_list(); -- cgit v1.2.3 From 57b63d17f73e2d5576e57521fb10307b91439b72 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:09 +0900 Subject: kconfig: gconf: remove unneeded variables in on_treeview*_button_press_event() Not all position parameters are used here. Passing NULL to gtk_tree_view_get_cursor() or gtk_tree_view_get_path_at_pos() is allowed. [1] [2] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gtk/gtktreeview.c#L12638 [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gtk/gtktreeview.c#L12795 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index c78eded5c01b..ab2e0df21037 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -822,13 +822,10 @@ on_treeview2_button_press_event(GtkWidget * widget, GtkTreeIter iter; struct menu *menu; gint col; - gint tx = (gint) event->x; gint ty = (gint) event->y; - gint cx, cy; - gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, - &cy); + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, NULL, NULL); if (path == NULL) return FALSE; @@ -871,12 +868,11 @@ on_treeview2_key_press_event(GtkWidget * widget, { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; - GtkTreeViewColumn *column; GtkTreeIter iter; struct menu *menu; gint col; - gtk_tree_view_get_cursor(view, &path, &column); + gtk_tree_view_get_cursor(view, &path, NULL); if (path == NULL) return FALSE; @@ -930,16 +926,12 @@ on_treeview1_button_press_event(GtkWidget * widget, { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; - GtkTreeViewColumn *column; GtkTreeIter iter; struct menu *menu; - gint tx = (gint) event->x; gint ty = (gint) event->y; - gint cx, cy; - gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, - &cy); + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, NULL, NULL, NULL); if (path == NULL) return FALSE; -- cgit v1.2.3 From b4809e25e2bf05de6398830f0990abe5dff49ac5 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:10 +0900 Subject: kconfig: gconf: remove unused 'color' variable This is not used at all. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index ab2e0df21037..4fff931f34fc 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -43,7 +43,6 @@ GtkWidget *save_btn = NULL; GtkWidget *save_menu_item = NULL; GtkTextTag *tag1, *tag2; -GdkColor color; GtkTreeStore *tree1, *tree2, *tree; GtkTreeModel *model1, *model2; -- cgit v1.2.3 From 290fc035dfeb07fcec57b09d888f837531333af7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:11 +0900 Subject: kconfig: gconf: add static qualifiers to variables I also removed unnecessary initializers. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 4fff931f34fc..0d5a02706bcb 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -32,20 +32,20 @@ static gboolean show_range = TRUE; static gboolean show_value = TRUE; static int opt_mode = OPT_NORMAL; -GtkWidget *main_wnd = NULL; -GtkWidget *tree1_w = NULL; // left frame -GtkWidget *tree2_w = NULL; // right frame -GtkWidget *text_w = NULL; -GtkWidget *hpaned = NULL; -GtkWidget *vpaned = NULL; -GtkWidget *back_btn = NULL; -GtkWidget *save_btn = NULL; -GtkWidget *save_menu_item = NULL; - -GtkTextTag *tag1, *tag2; - -GtkTreeStore *tree1, *tree2, *tree; -GtkTreeModel *model1, *model2; +static GtkWidget *main_wnd; +static GtkWidget *tree1_w; // left frame +static GtkWidget *tree2_w; // right frame +static GtkWidget *text_w; +static GtkWidget *hpaned; +static GtkWidget *vpaned; +static GtkWidget *back_btn; +static GtkWidget *save_btn; +static GtkWidget *save_menu_item; + +static GtkTextTag *tag1, *tag2; + +static GtkTreeStore *tree1, *tree2, *tree; +static GtkTreeModel *model1, *model2; static struct menu *current; // current node for SINGLE view static struct menu *browsed; // browsed node for SPLIT view -- cgit v1.2.3 From 7ef533938e6cd7d0e33e1c24389c34a21221979b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:12 +0900 Subject: kconfig: gconf: move init_*() functions below This allows removal of the forward declaration of renderer_edited(). Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 450 ++++++++++++++++++++++++------------------------ 1 file changed, 222 insertions(+), 228 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 0d5a02706bcb..9d06c050b270 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -62,240 +62,12 @@ static void display_tree(struct menu *menu); static void display_tree_part(void); static void update_tree(struct menu *src, GtkTreeIter * dst); -static void replace_button_icon(GladeXML *xml, GdkDrawable *window, - GtkStyle *style, gchar *btn_name, gchar **xpm) -{ - GdkPixmap *pixmap; - GdkBitmap *mask; - GtkToolButton *button; - GtkWidget *image; - - pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, - &style->bg[GTK_STATE_NORMAL], - xpm); - - button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); - image = gtk_image_new_from_pixmap(pixmap, mask); - gtk_widget_show(image); - gtk_tool_button_set_icon_widget(button, image); -} - static void conf_changed(bool dirty) { gtk_widget_set_sensitive(save_btn, dirty); gtk_widget_set_sensitive(save_menu_item, dirty); } -/* Main Window Initialization */ -static void init_main_window(const gchar *glade_file) -{ - GladeXML *xml; - GtkWidget *widget; - GtkTextBuffer *txtbuf; - GtkStyle *style; - - xml = glade_xml_new(glade_file, "window1", NULL); - if (!xml) - g_error("GUI loading failed !\n"); - glade_xml_signal_autoconnect(xml); - - main_wnd = glade_xml_get_widget(xml, "window1"); - hpaned = glade_xml_get_widget(xml, "hpaned1"); - vpaned = glade_xml_get_widget(xml, "vpaned1"); - tree1_w = glade_xml_get_widget(xml, "treeview1"); - tree2_w = glade_xml_get_widget(xml, "treeview2"); - text_w = glade_xml_get_widget(xml, "textview3"); - - back_btn = glade_xml_get_widget(xml, "button1"); - gtk_widget_set_sensitive(back_btn, FALSE); - - widget = glade_xml_get_widget(xml, "show_name1"); - gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, - show_name); - - widget = glade_xml_get_widget(xml, "show_range1"); - gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, - show_range); - - widget = glade_xml_get_widget(xml, "show_data1"); - gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, - show_value); - - save_btn = glade_xml_get_widget(xml, "button3"); - save_menu_item = glade_xml_get_widget(xml, "save1"); - conf_set_changed_callback(conf_changed); - - style = gtk_widget_get_style(main_wnd); - - replace_button_icon(xml, main_wnd->window, style, - "button4", (gchar **) xpm_single_view); - replace_button_icon(xml, main_wnd->window, style, - "button5", (gchar **) xpm_split_view); - replace_button_icon(xml, main_wnd->window, style, - "button6", (gchar **) xpm_tree_view); - - txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); - tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", - "foreground", "red", - "weight", PANGO_WEIGHT_BOLD, - NULL); - tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2", - /*"style", PANGO_STYLE_OBLIQUE, */ - NULL); - - gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); - - gtk_widget_show(main_wnd); -} - -static void init_tree_model(void) -{ - tree = tree2 = gtk_tree_store_new(COL_NUMBER, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, - G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); - model2 = GTK_TREE_MODEL(tree2); - - tree1 = gtk_tree_store_new(COL_NUMBER, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, - G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); - model1 = GTK_TREE_MODEL(tree1); -} - -static void init_left_tree(void) -{ - GtkTreeView *view = GTK_TREE_VIEW(tree1_w); - GtkCellRenderer *renderer; - GtkTreeSelection *sel; - GtkTreeViewColumn *column; - - gtk_tree_view_set_model(view, model1); - - column = gtk_tree_view_column_new(); - gtk_tree_view_append_column(view, column); - gtk_tree_view_column_set_title(column, "Options"); - - renderer = gtk_cell_renderer_toggle_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "active", COL_BTNACT, - "inconsistent", COL_BTNINC, - "visible", COL_BTNVIS, - "radio", COL_BTNRAD, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "text", COL_OPTION, - "foreground-gdk", - COL_COLOR, NULL); - - sel = gtk_tree_view_get_selection(view); - gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); -} - -static void renderer_edited(GtkCellRendererText * cell, - const gchar * path_string, - const gchar * new_text, gpointer user_data); - -static void init_right_tree(void) -{ - GtkTreeView *view = GTK_TREE_VIEW(tree2_w); - GtkCellRenderer *renderer; - GtkTreeSelection *sel; - GtkTreeViewColumn *column; - gint i; - - gtk_tree_view_set_model(view, model2); - - column = gtk_tree_view_column_new(); - gtk_tree_view_append_column(view, column); - gtk_tree_view_column_set_title(column, "Options"); - - renderer = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "pixbuf", COL_PIXBUF, - "visible", COL_PIXVIS, NULL); - renderer = gtk_cell_renderer_toggle_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "active", COL_BTNACT, - "inconsistent", COL_BTNINC, - "visible", COL_BTNVIS, - "radio", COL_BTNRAD, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "text", COL_OPTION, - "foreground-gdk", - COL_COLOR, NULL); - - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "Name", renderer, - "text", COL_NAME, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "N", renderer, - "text", COL_NO, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "M", renderer, - "text", COL_MOD, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "Y", renderer, - "text", COL_YES, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "Value", renderer, - "text", COL_VALUE, - "editable", - COL_EDIT, - "foreground-gdk", - COL_COLOR, NULL); - g_signal_connect(G_OBJECT(renderer), "edited", - G_CALLBACK(renderer_edited), NULL); - - for (i = 0; i < COL_VALUE; i++) { - column = gtk_tree_view_get_column(view, i); - gtk_tree_view_column_set_resizable(column, TRUE); - } - - sel = gtk_tree_view_get_selection(view); - gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); -} - - /* Utility Functions */ @@ -1324,6 +1096,228 @@ static void fixup_rootmenu(struct menu *menu) } } +/* Main Window Initialization */ +static void replace_button_icon(GladeXML *xml, GdkDrawable *window, + GtkStyle *style, gchar *btn_name, gchar **xpm) +{ + GdkPixmap *pixmap; + GdkBitmap *mask; + GtkToolButton *button; + GtkWidget *image; + + pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, + &style->bg[GTK_STATE_NORMAL], + xpm); + + button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); + image = gtk_image_new_from_pixmap(pixmap, mask); + gtk_widget_show(image); + gtk_tool_button_set_icon_widget(button, image); +} + +static void init_main_window(const gchar *glade_file) +{ + GladeXML *xml; + GtkWidget *widget; + GtkTextBuffer *txtbuf; + GtkStyle *style; + + xml = glade_xml_new(glade_file, "window1", NULL); + if (!xml) + g_error("GUI loading failed !\n"); + glade_xml_signal_autoconnect(xml); + + main_wnd = glade_xml_get_widget(xml, "window1"); + hpaned = glade_xml_get_widget(xml, "hpaned1"); + vpaned = glade_xml_get_widget(xml, "vpaned1"); + tree1_w = glade_xml_get_widget(xml, "treeview1"); + tree2_w = glade_xml_get_widget(xml, "treeview2"); + text_w = glade_xml_get_widget(xml, "textview3"); + + back_btn = glade_xml_get_widget(xml, "button1"); + gtk_widget_set_sensitive(back_btn, FALSE); + + widget = glade_xml_get_widget(xml, "show_name1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_name); + + widget = glade_xml_get_widget(xml, "show_range1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_range); + + widget = glade_xml_get_widget(xml, "show_data1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_value); + + save_btn = glade_xml_get_widget(xml, "button3"); + save_menu_item = glade_xml_get_widget(xml, "save1"); + conf_set_changed_callback(conf_changed); + + style = gtk_widget_get_style(main_wnd); + + replace_button_icon(xml, main_wnd->window, style, + "button4", (gchar **) xpm_single_view); + replace_button_icon(xml, main_wnd->window, style, + "button5", (gchar **) xpm_split_view); + replace_button_icon(xml, main_wnd->window, style, + "button6", (gchar **) xpm_tree_view); + + txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", + "foreground", "red", + "weight", PANGO_WEIGHT_BOLD, + NULL); + tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2", + /*"style", PANGO_STYLE_OBLIQUE, */ + NULL); + + gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); + + gtk_widget_show(main_wnd); +} + +static void init_tree_model(void) +{ + tree = tree2 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + model2 = GTK_TREE_MODEL(tree2); + + tree1 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + model1 = GTK_TREE_MODEL(tree1); +} + +static void init_left_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree1_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + + gtk_tree_view_set_model(view, model1); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, "Options"); + + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-gdk", + COL_COLOR, NULL); + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); +} + +static void init_right_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree2_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + gint i; + + gtk_tree_view_set_model(view, model2); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, "Options"); + + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "pixbuf", COL_PIXBUF, + "visible", COL_PIXVIS, NULL); + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-gdk", + COL_COLOR, NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Name", renderer, + "text", COL_NAME, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "N", renderer, + "text", COL_NO, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "M", renderer, + "text", COL_MOD, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Y", renderer, + "text", COL_YES, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Value", renderer, + "text", COL_VALUE, + "editable", + COL_EDIT, + "foreground-gdk", + COL_COLOR, NULL); + g_signal_connect(G_OBJECT(renderer), "edited", + G_CALLBACK(renderer_edited), NULL); + + for (i = 0; i < COL_VALUE; i++) { + column = gtk_tree_view_get_column(view, i); + gtk_tree_view_column_set_resizable(column, TRUE); + } + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); +} /* Main */ int main(int ac, char *av[]) -- cgit v1.2.3 From 8d6b5c14ab0bdca5b8a7583ccbec4a092e3458bf Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:13 +0900 Subject: kconfig: gconf: refactor view setting code Factor out common code for setting the view into a new function, set_view_mode(). Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 74 +++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 36 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 9d06c050b270..185084fccc23 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -18,7 +18,7 @@ #include #include -enum { +enum view_mode { SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW }; @@ -116,6 +116,39 @@ static void text_insert_msg(const char *title, const char *message) NULL); } +static void set_view_mode(enum view_mode mode) +{ + view_mode = mode; + + if (mode == SPLIT_VIEW) { // two panes + gint w; + + gtk_widget_show(tree1_w); + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, NULL); + gtk_paned_set_position(GTK_PANED(hpaned), w / 2); + } else { + gtk_widget_hide(tree1_w); + gtk_paned_set_position(GTK_PANED(hpaned), 0); + } + + switch (mode) { + case SINGLE_VIEW: + current = &rootmenu; + display_tree_part(); + break; + case SPLIT_VIEW: + gtk_tree_store_clear(tree2); + display_list(); + break; + case FULL_VIEW: + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); + break; + } + + if (mode != SINGLE_VIEW) + gtk_widget_set_sensitive(back_btn, FALSE); +} /* Main Windows Callbacks */ @@ -435,35 +468,19 @@ void on_load_clicked(GtkButton * button, gpointer user_data) void on_single_clicked(GtkButton * button, gpointer user_data) { - view_mode = SINGLE_VIEW; - gtk_widget_hide(tree1_w); - current = &rootmenu; - display_tree_part(); + set_view_mode(SINGLE_VIEW); } void on_split_clicked(GtkButton * button, gpointer user_data) { - gint w; - view_mode = SPLIT_VIEW; - gtk_widget_show(tree1_w); - gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, NULL); - gtk_paned_set_position(GTK_PANED(hpaned), w / 2); - gtk_tree_store_clear(tree2); - display_list(); - - /* Disable back btn, like in full mode. */ - gtk_widget_set_sensitive(back_btn, FALSE); + set_view_mode(SPLIT_VIEW); } void on_full_clicked(GtkButton * button, gpointer user_data) { - view_mode = FULL_VIEW; - gtk_widget_hide(tree1_w); - gtk_tree_store_clear(tree2); - display_tree(&rootmenu); - gtk_widget_set_sensitive(back_btn, FALSE); + set_view_mode(FULL_VIEW); } @@ -1039,11 +1056,6 @@ static void _display_tree(struct menu *menu, GtkTreeIter *parent) || (view_mode == FULL_VIEW) || (view_mode == SPLIT_VIEW))*/ - /* Change paned position if the view is not in 'split mode' */ - if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) { - gtk_paned_set_position(GTK_PANED(hpaned), 0); - } - if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) || (view_mode == FULL_VIEW) || (view_mode == SPLIT_VIEW)) @@ -1368,17 +1380,7 @@ int main(int ac, char *av[]) conf_read(NULL); - switch (view_mode) { - case SINGLE_VIEW: - display_tree_part(); - break; - case SPLIT_VIEW: - display_list(); - break; - case FULL_VIEW: - display_tree(&rootmenu); - break; - } + set_view_mode(view_mode); gtk_main(); -- cgit v1.2.3 From b22bbaea7f59ea1fa609462bed2eb075eea97586 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:14 +0900 Subject: kconfig: gconf: grey out button for current view This clarifies which view is currently selected. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 185084fccc23..77e742eebf24 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -38,8 +38,7 @@ static GtkWidget *tree2_w; // right frame static GtkWidget *text_w; static GtkWidget *hpaned; static GtkWidget *vpaned; -static GtkWidget *back_btn; -static GtkWidget *save_btn; +static GtkWidget *back_btn, *save_btn, *single_btn, *split_btn, *full_btn; static GtkWidget *save_menu_item; static GtkTextTag *tag1, *tag2; @@ -131,18 +130,25 @@ static void set_view_mode(enum view_mode mode) gtk_paned_set_position(GTK_PANED(hpaned), 0); } + gtk_widget_set_sensitive(single_btn, TRUE); + gtk_widget_set_sensitive(split_btn, TRUE); + gtk_widget_set_sensitive(full_btn, TRUE); + switch (mode) { case SINGLE_VIEW: current = &rootmenu; display_tree_part(); + gtk_widget_set_sensitive(single_btn, FALSE); break; case SPLIT_VIEW: gtk_tree_store_clear(tree2); display_list(); + gtk_widget_set_sensitive(split_btn, FALSE); break; case FULL_VIEW: gtk_tree_store_clear(tree2); display_tree(&rootmenu); + gtk_widget_set_sensitive(full_btn, FALSE); break; } @@ -1167,10 +1173,15 @@ static void init_main_window(const gchar *glade_file) style = gtk_widget_get_style(main_wnd); + single_btn = glade_xml_get_widget(xml, "button4"); replace_button_icon(xml, main_wnd->window, style, "button4", (gchar **) xpm_single_view); + + split_btn = glade_xml_get_widget(xml, "button5"); replace_button_icon(xml, main_wnd->window, style, "button5", (gchar **) xpm_split_view); + + full_btn = glade_xml_get_widget(xml, "button6"); replace_button_icon(xml, main_wnd->window, style, "button6", (gchar **) xpm_tree_view); -- cgit v1.2.3 From 3e0fb3ef01584bcace87c42a4f96abacad624386 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:15 +0900 Subject: kconfig: gconf: move the main window event handlers below This allows removal of the forward delcaration of on_save_activate(). Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 163 +++++++++++++++++++++++------------------------- 1 file changed, 78 insertions(+), 85 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 77e742eebf24..d265d25f72cb 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -156,83 +156,6 @@ static void set_view_mode(enum view_mode mode) gtk_widget_set_sensitive(back_btn, FALSE); } -/* Main Windows Callbacks */ - -void on_save_activate(GtkMenuItem * menuitem, gpointer user_data); -gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event, - gpointer user_data) -{ - GtkWidget *dialog, *label; - gint result; - gint ret = FALSE; - - if (!conf_get_changed()) - return FALSE; - - dialog = gtk_dialog_new_with_buttons("Warning !", - GTK_WINDOW(main_wnd), - (GtkDialogFlags) - (GTK_DIALOG_MODAL | - GTK_DIALOG_DESTROY_WITH_PARENT), - GTK_STOCK_OK, - GTK_RESPONSE_YES, - GTK_STOCK_NO, - GTK_RESPONSE_NO, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, NULL); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), - GTK_RESPONSE_CANCEL); - - label = gtk_label_new("\nSave configuration ?\n"); - gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); - gtk_widget_show(label); - - result = gtk_dialog_run(GTK_DIALOG(dialog)); - switch (result) { - case GTK_RESPONSE_YES: - on_save_activate(NULL, NULL); - break; - case GTK_RESPONSE_NO: - break; - case GTK_RESPONSE_CANCEL: - case GTK_RESPONSE_DELETE_EVENT: - default: - ret = TRUE; - break; - } - - gtk_widget_destroy(dialog); - - return ret; -} - - -void on_window1_destroy(GtkObject * object, gpointer user_data) -{ - gtk_main_quit(); -} - - -void -on_window1_size_request(GtkWidget * widget, - GtkRequisition * requisition, gpointer user_data) -{ - static gint old_h; - gint w, h; - - if (widget->window == NULL) - gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); - else - gdk_window_get_size(widget->window, &w, &h); - - if (h == old_h) - return; - old_h = h; - - gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); -} - - /* Menu & Toolbar Callbacks */ @@ -311,14 +234,6 @@ void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show(fs); } - -void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data) -{ - if (!on_window1_delete_event(NULL, NULL, NULL)) - gtk_widget_destroy(GTK_WIDGET(main_wnd)); -} - - void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data) { GtkTreeViewColumn *col; @@ -501,6 +416,84 @@ void on_expand_clicked(GtkButton * button, gpointer user_data) gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } +/* Main Windows Callbacks */ + +void on_window1_destroy(GtkObject *object, gpointer user_data) +{ + gtk_main_quit(); +} + +void on_window1_size_request(GtkWidget *widget, + GtkRequisition *requisition, + gpointer user_data) +{ + static gint old_h; + gint w, h; + + if (widget->window == NULL) + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); + else + gdk_window_get_size(widget->window, &w, &h); + + if (h == old_h) + return; + old_h = h; + + gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); +} + +gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, + gpointer user_data) +{ + GtkWidget *dialog, *label; + gint result; + gint ret = FALSE; + + if (!conf_get_changed()) + return FALSE; + + dialog = gtk_dialog_new_with_buttons("Warning !", + GTK_WINDOW(main_wnd), + (GtkDialogFlags) + (GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_STOCK_OK, + GTK_RESPONSE_YES, + GTK_STOCK_NO, + GTK_RESPONSE_NO, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), + GTK_RESPONSE_CANCEL); + + label = gtk_label_new("\nSave configuration ?\n"); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); + gtk_widget_show(label); + + result = gtk_dialog_run(GTK_DIALOG(dialog)); + switch (result) { + case GTK_RESPONSE_YES: + on_save_activate(NULL, NULL); + break; + case GTK_RESPONSE_NO: + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_DELETE_EVENT: + default: + ret = TRUE; + break; + } + + gtk_widget_destroy(dialog); + + return ret; +} + +void on_quit1_activate(GtkMenuItem *menuitem, gpointer user_data) +{ + if (!on_window1_delete_event(NULL, NULL, NULL)) + gtk_widget_destroy(GTK_WIDGET(main_wnd)); +} /* CTree Callbacks */ -- cgit v1.2.3 From f0049c937d2f38ae208c06aa5ef114dac226d01c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:16 +0900 Subject: kconfig: gconf: move button1 and save1 initialization code Move the relevant initialization code closer together. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index d265d25f72cb..3a7bb27b4871 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1145,8 +1145,8 @@ static void init_main_window(const gchar *glade_file) tree2_w = glade_xml_get_widget(xml, "treeview2"); text_w = glade_xml_get_widget(xml, "textview3"); - back_btn = glade_xml_get_widget(xml, "button1"); - gtk_widget_set_sensitive(back_btn, FALSE); + /* menubar */ + save_menu_item = glade_xml_get_widget(xml, "save1"); widget = glade_xml_get_widget(xml, "show_name1"); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, @@ -1160,9 +1160,11 @@ static void init_main_window(const gchar *glade_file) gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_value); + /* toolbar */ + back_btn = glade_xml_get_widget(xml, "button1"); + gtk_widget_set_sensitive(back_btn, FALSE); + save_btn = glade_xml_get_widget(xml, "button3"); - save_menu_item = glade_xml_get_widget(xml, "save1"); - conf_set_changed_callback(conf_changed); style = gtk_widget_get_style(main_wnd); @@ -1190,6 +1192,8 @@ static void init_main_window(const gchar *glade_file) gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); gtk_widget_show(main_wnd); + + conf_set_changed_callback(conf_changed); } static void init_tree_model(void) -- cgit v1.2.3 From 77e8ff988918de554e0176c4ce0064944935efb7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:17 +0900 Subject: kconfig: gconf: add static qualifiers to event handlers This fixes several -Wmissing-prototypes warnings. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 178 +++++++++++++++++++++++++++++++------------- scripts/kconfig/gconf.glade | 30 -------- 2 files changed, 125 insertions(+), 83 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 3a7bb27b4871..3b4bd897856c 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -173,7 +173,7 @@ load_filename(GtkFileSelection * file_selector, gpointer user_data) display_tree_part(); } -void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_load1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *fs; @@ -192,8 +192,7 @@ void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show(fs); } - -void on_save_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_save_activate(GtkMenuItem *menuitem, gpointer user_data) { if (conf_write(NULL)) text_insert_msg("Error", "Unable to save configuration !"); @@ -215,7 +214,7 @@ store_filename(GtkFileSelection * file_selector, gpointer user_data) gtk_widget_destroy(GTK_WIDGET(user_data)); } -void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_save_as1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *fs; @@ -234,7 +233,7 @@ void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show(fs); } -void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_show_name1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; @@ -244,8 +243,7 @@ void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_tree_view_column_set_visible(col, show_name); } - -void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_show_range1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; @@ -262,8 +260,7 @@ void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data) } - -void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_show_data1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; @@ -273,35 +270,31 @@ void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_tree_view_column_set_visible(col, show_value); } - -void -on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data) +static void on_set_option_mode1_activate(GtkMenuItem *menuitem, + gpointer user_data) { opt_mode = OPT_NORMAL; gtk_tree_store_clear(tree2); display_tree(&rootmenu); /* instead of update_tree to speed-up */ } - -void -on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data) +static void on_set_option_mode2_activate(GtkMenuItem *menuitem, + gpointer user_data) { opt_mode = OPT_ALL; gtk_tree_store_clear(tree2); display_tree(&rootmenu); /* instead of update_tree to speed-up */ } - -void -on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data) +static void on_set_option_mode3_activate(GtkMenuItem *menuitem, + gpointer user_data) { opt_mode = OPT_PROMPT; gtk_tree_store_clear(tree2); display_tree(&rootmenu); /* instead of update_tree to speed-up */ } - -void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_introduction1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *intro_text = @@ -328,8 +321,7 @@ void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show_all(dialog); } - -void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_about1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *about_text = @@ -346,8 +338,7 @@ void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show_all(dialog); } - -void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_license1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *license_text = @@ -365,8 +356,8 @@ void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show_all(dialog); } - -void on_back_clicked(GtkButton * button, gpointer user_data) +/* toolbar handlers */ +static void on_back_clicked(GtkButton *button, gpointer user_data) { enum prop_type ptype; @@ -380,50 +371,44 @@ void on_back_clicked(GtkButton * button, gpointer user_data) gtk_widget_set_sensitive(back_btn, FALSE); } - -void on_load_clicked(GtkButton * button, gpointer user_data) +static void on_load_clicked(GtkButton *button, gpointer user_data) { on_load1_activate(NULL, user_data); } - -void on_single_clicked(GtkButton * button, gpointer user_data) +static void on_single_clicked(GtkButton *button, gpointer user_data) { set_view_mode(SINGLE_VIEW); } - -void on_split_clicked(GtkButton * button, gpointer user_data) +static void on_split_clicked(GtkButton *button, gpointer user_data) { set_view_mode(SPLIT_VIEW); } - -void on_full_clicked(GtkButton * button, gpointer user_data) +static void on_full_clicked(GtkButton *button, gpointer user_data) { set_view_mode(FULL_VIEW); } - -void on_collapse_clicked(GtkButton * button, gpointer user_data) +static void on_collapse_clicked(GtkButton *button, gpointer user_data) { gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w)); } - -void on_expand_clicked(GtkButton * button, gpointer user_data) +static void on_expand_clicked(GtkButton *button, gpointer user_data) { gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } /* Main Windows Callbacks */ -void on_window1_destroy(GtkObject *object, gpointer user_data) +static void on_window1_destroy(GtkObject *object, gpointer user_data) { gtk_main_quit(); } -void on_window1_size_request(GtkWidget *widget, +static void on_window1_size_request(GtkWidget *widget, GtkRequisition *requisition, gpointer user_data) { @@ -442,7 +427,7 @@ void on_window1_size_request(GtkWidget *widget, gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); } -gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, +static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) { GtkWidget *dialog, *label; @@ -489,7 +474,7 @@ gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, return ret; } -void on_quit1_activate(GtkMenuItem *menuitem, gpointer user_data) +static void on_quit1_activate(GtkMenuItem *menuitem, gpointer user_data) { if (!on_window1_delete_event(NULL, NULL, NULL)) gtk_widget_destroy(GTK_WIDGET(main_wnd)); @@ -599,9 +584,9 @@ static gint column2index(GtkTreeViewColumn * column) /* User click: update choice (full) or goes down (single) */ -gboolean -on_treeview2_button_press_event(GtkWidget * widget, - GdkEventButton * event, gpointer user_data) +static gboolean on_treeview2_button_press_event(GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; @@ -649,9 +634,9 @@ on_treeview2_button_press_event(GtkWidget * widget, } /* Key pressed: update choice */ -gboolean -on_treeview2_key_press_event(GtkWidget * widget, - GdkEventKey * event, gpointer user_data) +static gboolean on_treeview2_key_press_event(GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; @@ -691,8 +676,8 @@ on_treeview2_key_press_event(GtkWidget * widget, /* Row selection changed: update help */ -void -on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data) +static void on_treeview2_cursor_changed(GtkTreeView *treeview, + gpointer user_data) { GtkTreeSelection *selection; GtkTreeIter iter; @@ -707,9 +692,9 @@ on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data) /* User click: display sub-tree in the right frame. */ -gboolean -on_treeview1_button_press_event(GtkWidget * widget, - GdkEventButton * event, gpointer user_data) +static gboolean on_treeview1_button_press_event(GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; @@ -1139,47 +1124,134 @@ static void init_main_window(const gchar *glade_file) glade_xml_signal_autoconnect(xml); main_wnd = glade_xml_get_widget(xml, "window1"); + g_signal_connect(main_wnd, "destroy", + G_CALLBACK(on_window1_destroy), NULL); + g_signal_connect(main_wnd, "size_request", + G_CALLBACK(on_window1_size_request), NULL); + g_signal_connect(main_wnd, "delete_event", + G_CALLBACK(on_window1_delete_event), NULL); + hpaned = glade_xml_get_widget(xml, "hpaned1"); vpaned = glade_xml_get_widget(xml, "vpaned1"); tree1_w = glade_xml_get_widget(xml, "treeview1"); + g_signal_connect(tree1_w, "cursor_changed", + G_CALLBACK(on_treeview2_cursor_changed), NULL); + g_signal_connect(tree1_w, "button_press_event", + G_CALLBACK(on_treeview1_button_press_event), NULL); + g_signal_connect(tree1_w, "key_press_event", + G_CALLBACK(on_treeview2_key_press_event), NULL); + tree2_w = glade_xml_get_widget(xml, "treeview2"); + g_signal_connect(tree2_w, "cursor_changed", + G_CALLBACK(on_treeview2_cursor_changed), NULL); + g_signal_connect(tree2_w, "button_press_event", + G_CALLBACK(on_treeview2_button_press_event), NULL); + g_signal_connect(tree2_w, "key_press_event", + G_CALLBACK(on_treeview2_key_press_event), NULL); + text_w = glade_xml_get_widget(xml, "textview3"); /* menubar */ + widget = glade_xml_get_widget(xml, "load1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_load1_activate), NULL); + save_menu_item = glade_xml_get_widget(xml, "save1"); + g_signal_connect(save_menu_item, "activate", + G_CALLBACK(on_save_activate), NULL); + + widget = glade_xml_get_widget(xml, "save_as1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_save_as1_activate), NULL); + + widget = glade_xml_get_widget(xml, "quit1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_quit1_activate), NULL); widget = glade_xml_get_widget(xml, "show_name1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_show_name1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_name); widget = glade_xml_get_widget(xml, "show_range1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_show_range1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_range); widget = glade_xml_get_widget(xml, "show_data1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_show_data1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_value); + widget = glade_xml_get_widget(xml, "set_option_mode1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_set_option_mode1_activate), NULL); + + widget = glade_xml_get_widget(xml, "set_option_mode2"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_set_option_mode2_activate), NULL); + + widget = glade_xml_get_widget(xml, "set_option_mode3"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_set_option_mode3_activate), NULL); + + widget = glade_xml_get_widget(xml, "introduction1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_introduction1_activate), NULL); + + widget = glade_xml_get_widget(xml, "about1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_about1_activate), NULL); + + widget = glade_xml_get_widget(xml, "license1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_license1_activate), NULL); + /* toolbar */ back_btn = glade_xml_get_widget(xml, "button1"); + g_signal_connect(back_btn, "clicked", + G_CALLBACK(on_back_clicked), NULL); gtk_widget_set_sensitive(back_btn, FALSE); + widget = glade_xml_get_widget(xml, "button2"); + g_signal_connect(widget, "clicked", + G_CALLBACK(on_load_clicked), NULL); + save_btn = glade_xml_get_widget(xml, "button3"); + g_signal_connect(save_btn, "clicked", + G_CALLBACK(on_save_activate), NULL); style = gtk_widget_get_style(main_wnd); single_btn = glade_xml_get_widget(xml, "button4"); + g_signal_connect(single_btn, "clicked", + G_CALLBACK(on_single_clicked), NULL); replace_button_icon(xml, main_wnd->window, style, "button4", (gchar **) xpm_single_view); split_btn = glade_xml_get_widget(xml, "button5"); + g_signal_connect(split_btn, "clicked", + G_CALLBACK(on_split_clicked), NULL); replace_button_icon(xml, main_wnd->window, style, "button5", (gchar **) xpm_split_view); full_btn = glade_xml_get_widget(xml, "button6"); + g_signal_connect(full_btn, "clicked", + G_CALLBACK(on_full_clicked), NULL); replace_button_icon(xml, main_wnd->window, style, "button6", (gchar **) xpm_tree_view); + widget = glade_xml_get_widget(xml, "button7"); + g_signal_connect(widget, "clicked", + G_CALLBACK(on_collapse_clicked), NULL); + + widget = glade_xml_get_widget(xml, "button8"); + g_signal_connect(widget, "clicked", + G_CALLBACK(on_expand_clicked), NULL); + txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", "foreground", "red", diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index 19b80f2ec1ff..8519104a3c2b 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -17,9 +17,6 @@ False GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_NORTH_WEST - - - @@ -46,7 +43,6 @@ Load a config file _Load True - @@ -57,7 +53,6 @@ Save the config in .config _Save True - @@ -68,7 +63,6 @@ Save the config in a file Save _as True - @@ -83,7 +77,6 @@ True _Quit True - @@ -108,7 +101,6 @@ Show _name True False - @@ -119,7 +111,6 @@ Show _range True False - @@ -130,7 +121,6 @@ Show _data True False - @@ -147,7 +137,6 @@ Show normal options True True - @@ -159,7 +148,6 @@ True False set_option_mode1 - @@ -171,7 +159,6 @@ True False set_option_mode1 - @@ -194,7 +181,6 @@ True _Introduction True - @@ -204,7 +190,6 @@ True _About True - @@ -214,7 +199,6 @@ True _License True - @@ -254,7 +238,6 @@ True True False - False @@ -291,7 +274,6 @@ True True False - False @@ -309,7 +291,6 @@ True True False - False @@ -346,7 +327,6 @@ True True False - False @@ -364,7 +344,6 @@ True True False - False @@ -382,7 +361,6 @@ True True False - False @@ -419,7 +397,6 @@ True True False - False @@ -437,7 +414,6 @@ True True False - False @@ -477,9 +453,6 @@ False False False - - - @@ -512,9 +485,6 @@ False False False - - - -- cgit v1.2.3 From e06b176bf1b4a779f76b686ab5230dce45a8360e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:18 +0900 Subject: kconfig: gconf: remove glade_xml_signal_autoconnect() call Now that all signals are connected manually, this is no longer necessary. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 3b4bd897856c..1c2fd71369f0 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1121,7 +1121,6 @@ static void init_main_window(const gchar *glade_file) xml = glade_xml_new(glade_file, "window1", NULL); if (!xml) g_error("GUI loading failed !\n"); - glade_xml_signal_autoconnect(xml); main_wnd = glade_xml_get_widget(xml, "window1"); g_signal_connect(main_wnd, "destroy", -- cgit v1.2.3 From 3beae8659513550b6b82a4ccdc4d25be9497f208 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:19 +0900 Subject: kconfig: gconf: make key_press_event work in left pane too Currently, on_treeview2_key_press_event() returns early for the tree1 widget. We can make it work on the left pane as well by avoiding the hardcoded use of model2. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 1c2fd71369f0..4acbcf912c6e 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -639,6 +639,7 @@ static gboolean on_treeview2_key_press_event(GtkWidget *widget, gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path; GtkTreeIter iter; struct menu *menu; @@ -655,11 +656,9 @@ static gboolean on_treeview2_key_press_event(GtkWidget *widget, gtk_tree_view_expand_row(view, path, FALSE); return TRUE; } - if (widget == tree1_w) - return FALSE; - gtk_tree_model_get_iter(model2, &iter, path); - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); if (!strcasecmp(event->string, "n")) col = COL_NO; -- cgit v1.2.3 From cae9cdbcd9af044810bcceeb43a87accca47c71d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:20 +0900 Subject: kconfig: gconf: avoid hardcoding model2 in on_treeview2_cursor_changed() The on_treeview2_cursor_changed() handler is connected to both the left and right tree views, but it hardcodes model2 (the GtkTreeModel of the right tree view). This is incorrect. Get the associated model from the view. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 4acbcf912c6e..d7aa7bad965f 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -678,13 +678,14 @@ static gboolean on_treeview2_key_press_event(GtkWidget *widget, static void on_treeview2_cursor_changed(GtkTreeView *treeview, gpointer user_data) { + GtkTreeModel *model = gtk_tree_view_get_model(treeview); GtkTreeSelection *selection; GtkTreeIter iter; struct menu *menu; selection = gtk_tree_view_get_selection(treeview); - if (gtk_tree_selection_get_selected(selection, &model2, &iter)) { - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + if (gtk_tree_selection_get_selected(selection, &model, &iter)) { + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); text_insert_help(menu); } } -- cgit v1.2.3 From e6991e8004bf1ff8fc31b14833c4995672f18b04 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:21 +0900 Subject: kconfig: gconf: avoid hardcoding model2 in renderer_edited() Although this is only used in the right tree view, it is better not to hardcode model2 for consistency. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index d7aa7bad965f..df822f4e13c5 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -487,19 +487,21 @@ static void renderer_edited(GtkCellRendererText * cell, const gchar * path_string, const gchar * new_text, gpointer user_data) { + GtkTreeView *view = GTK_TREE_VIEW(user_data); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path = gtk_tree_path_new_from_string(path_string); GtkTreeIter iter; const char *old_def, *new_def; struct menu *menu; struct symbol *sym; - if (!gtk_tree_model_get_iter(model2, &iter, path)) + if (!gtk_tree_model_get_iter(model, &iter, path)) goto free; - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); sym = menu->sym; - gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1); + gtk_tree_model_get(model, &iter, COL_VALUE, &old_def, -1); new_def = new_text; sym_set_string_value(sym, new_def); @@ -1399,7 +1401,7 @@ static void init_right_tree(void) "foreground-gdk", COL_COLOR, NULL); g_signal_connect(G_OBJECT(renderer), "edited", - G_CALLBACK(renderer_edited), NULL); + G_CALLBACK(renderer_edited), tree2_w); for (i = 0; i < COL_VALUE; i++) { column = gtk_tree_view_get_column(view, i); -- cgit v1.2.3 From 59adbcd8051a222023f52cfac0143d927735b194 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:22 +0900 Subject: kconfig: gconf: avoid hardcoding model* in on_treeview*_button_press_event() It is better not to hardcode model1 or model2 for consistency. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index df822f4e13c5..f03e94cd5fa3 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -591,6 +591,7 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path; GtkTreeViewColumn *column; GtkTreeIter iter; @@ -603,9 +604,9 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, if (path == NULL) return FALSE; - if (!gtk_tree_model_get_iter(model2, &iter, path)) + if (!gtk_tree_model_get_iter(model, &iter, path)) return FALSE; - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); col = column2index(column); if (event->type == GDK_2BUTTON_PRESS) { @@ -699,6 +700,7 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path; GtkTreeIter iter; struct menu *menu; @@ -709,8 +711,8 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, if (path == NULL) return FALSE; - gtk_tree_model_get_iter(model1, &iter, path); - gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); if (event->type == GDK_2BUTTON_PRESS) { toggle_sym_value(menu); -- cgit v1.2.3 From 4d89059a722d9a562bfe6ee1e3941ccc3c6c70b6 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:23 +0900 Subject: kconfig: gconf: add on_save_clicked() event handler The "clicked" event handler for GtkToolButton takes the GtkToolButton* as the first parameter. This is different from the existing on_save_activate() handler. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index f03e94cd5fa3..4e21cf46ff01 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -376,6 +376,11 @@ static void on_load_clicked(GtkButton *button, gpointer user_data) on_load1_activate(NULL, user_data); } +static void on_save_clicked(GtkButton *button, gpointer user_data) +{ + on_save_activate(NULL, user_data); +} + static void on_single_clicked(GtkButton *button, gpointer user_data) { set_view_mode(SINGLE_VIEW); @@ -1225,7 +1230,7 @@ static void init_main_window(const gchar *glade_file) save_btn = glade_xml_get_widget(xml, "button3"); g_signal_connect(save_btn, "clicked", - G_CALLBACK(on_save_activate), NULL); + G_CALLBACK(on_save_clicked), NULL); style = gtk_widget_get_style(main_wnd); -- cgit v1.2.3 From 30dda0fdf7a6655bf180b61063087aed28812007 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:24 +0900 Subject: kconfig: gconf: use GtkFileChooser in on_load1_activate() gtk_file_selection_new() is deprecated, and gtk_file_chooser_dialog_new() should be used instead. [1] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/docs/reference/gtk/tmpl/gtkfilesel.sgml?ref_type=tags#L156 Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 54 ++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 4e21cf46ff01..20a20a5888b9 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -158,38 +158,38 @@ static void set_view_mode(enum view_mode mode) /* Menu & Toolbar Callbacks */ - -static void -load_filename(GtkFileSelection * file_selector, gpointer user_data) +static void on_load1_activate(GtkMenuItem *menuitem, gpointer user_data) { - const gchar *fn; + GtkWidget *dialog; + GtkFileChooser *chooser; + gint res; - fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION - (user_data)); + dialog = gtk_file_chooser_dialog_new("Load file...", + GTK_WINDOW(user_data), + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL); - if (conf_read(fn)) - text_insert_msg("Error", "Unable to load configuration !"); - else - display_tree_part(); -} + chooser = GTK_FILE_CHOOSER(dialog); + gtk_file_chooser_set_filename(chooser, conf_get_configname()); -static void on_load1_activate(GtkMenuItem *menuitem, gpointer user_data) -{ - GtkWidget *fs; + res = gtk_dialog_run(GTK_DIALOG(dialog)); + if (res == GTK_RESPONSE_ACCEPT) { + char *filename; - fs = gtk_file_selection_new("Load file..."); - g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), - "clicked", - G_CALLBACK(load_filename), (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->ok_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->cancel_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - gtk_widget_show(fs); + filename = gtk_file_chooser_get_filename(chooser); + + if (conf_read(filename)) + text_insert_msg("Error", + "Unable to load configuration!"); + else + display_tree_part(); + + g_free(filename); + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); } static void on_save_activate(GtkMenuItem *menuitem, gpointer user_data) -- cgit v1.2.3 From fd7fd8024c32ae2037f98d53198e42d5c597ca0b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:25 +0900 Subject: kconfig: gconf: use GtkFileChooser in on_save_as1_activate() gtk_file_selection_new() is deprecated, and gtk_file_chooser_dialog_new() should be used instead. [1] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/docs/reference/gtk/tmpl/gtkfilesel.sgml?ref_type=tags#L156 Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 52 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 20a20a5888b9..82e8edb8a82c 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -199,38 +199,36 @@ static void on_save_activate(GtkMenuItem *menuitem, gpointer user_data) conf_write_autoconf(0); } - -static void -store_filename(GtkFileSelection * file_selector, gpointer user_data) +static void on_save_as1_activate(GtkMenuItem *menuitem, gpointer user_data) { - const gchar *fn; + GtkWidget *dialog; + GtkFileChooser *chooser; + gint res; - fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION - (user_data)); + dialog = gtk_file_chooser_dialog_new("Save file as...", + GTK_WINDOW(user_data), + GTK_FILE_CHOOSER_ACTION_SAVE, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Save", GTK_RESPONSE_ACCEPT, + NULL); - if (conf_write(fn)) - text_insert_msg("Error", "Unable to save configuration !"); + chooser = GTK_FILE_CHOOSER(dialog); + gtk_file_chooser_set_filename(chooser, conf_get_configname()); - gtk_widget_destroy(GTK_WIDGET(user_data)); -} + res = gtk_dialog_run(GTK_DIALOG(dialog)); + if (res == GTK_RESPONSE_ACCEPT) { + char *filename; -static void on_save_as1_activate(GtkMenuItem *menuitem, gpointer user_data) -{ - GtkWidget *fs; - - fs = gtk_file_selection_new("Save file as..."); - g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), - "clicked", - G_CALLBACK(store_filename), (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->ok_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->cancel_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - gtk_widget_show(fs); + filename = gtk_file_chooser_get_filename(chooser); + + if (conf_write(filename)) + text_insert_msg("Error", + "Unable to save configuration !"); + + g_free(filename); + } + + gtk_widget_destroy(dialog); } static void on_show_name1_activate(GtkMenuItem *menuitem, gpointer user_data) -- cgit v1.2.3 From 9517f47dbf8ab7a7e554e7b34563982cfc63c366 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:26 +0900 Subject: kconfig: gconf: use GdkPixbuf in replace_button_icon() gdk_pixmap_create_from_xpm_d has been deprecated since version 2.22. Use a GdkPixbuf instead. You can use gdk_pixbuf_new_from_xpm_data() to create it. [1] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gdk/gdkpixmap.c#L742 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 82e8edb8a82c..2eac486cec5b 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1103,17 +1103,16 @@ static void fixup_rootmenu(struct menu *menu) static void replace_button_icon(GladeXML *xml, GdkDrawable *window, GtkStyle *style, gchar *btn_name, gchar **xpm) { - GdkPixmap *pixmap; - GdkBitmap *mask; - GtkToolButton *button; + GdkPixbuf *pixbuf; GtkWidget *image; + GtkToolButton *button; - pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, - &style->bg[GTK_STATE_NORMAL], - xpm); + pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)xpm); + image = gtk_image_new_from_pixbuf(pixbuf); + g_object_unref(pixbuf); button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); - image = gtk_image_new_from_pixmap(pixmap, mask); + gtk_widget_show(image); gtk_tool_button_set_icon_widget(button, image); } -- cgit v1.2.3 From b8f660aabcf2dbbd7e8f84c6341e015124bbfc4e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:27 +0900 Subject: kconfig: gconf: refactor replace_button_icon() The "window" and "style" arguments for replace_button_icon() are now unused. Remove them and refactor the function accordingly. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 2eac486cec5b..045729d76feb 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1100,21 +1100,17 @@ static void fixup_rootmenu(struct menu *menu) } /* Main Window Initialization */ -static void replace_button_icon(GladeXML *xml, GdkDrawable *window, - GtkStyle *style, gchar *btn_name, gchar **xpm) +static void replace_button_icon(GtkWidget *widget, const char * const xpm[]) { GdkPixbuf *pixbuf; GtkWidget *image; - GtkToolButton *button; pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)xpm); image = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(pixbuf); - button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); - gtk_widget_show(image); - gtk_tool_button_set_icon_widget(button, image); + gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(widget), image); } static void init_main_window(const gchar *glade_file) @@ -1122,7 +1118,6 @@ static void init_main_window(const gchar *glade_file) GladeXML *xml; GtkWidget *widget; GtkTextBuffer *txtbuf; - GtkStyle *style; xml = glade_xml_new(glade_file, "window1", NULL); if (!xml) @@ -1229,25 +1224,20 @@ static void init_main_window(const gchar *glade_file) g_signal_connect(save_btn, "clicked", G_CALLBACK(on_save_clicked), NULL); - style = gtk_widget_get_style(main_wnd); - single_btn = glade_xml_get_widget(xml, "button4"); g_signal_connect(single_btn, "clicked", G_CALLBACK(on_single_clicked), NULL); - replace_button_icon(xml, main_wnd->window, style, - "button4", (gchar **) xpm_single_view); + replace_button_icon(single_btn, xpm_single_view); split_btn = glade_xml_get_widget(xml, "button5"); g_signal_connect(split_btn, "clicked", G_CALLBACK(on_split_clicked), NULL); - replace_button_icon(xml, main_wnd->window, style, - "button5", (gchar **) xpm_split_view); + replace_button_icon(split_btn, xpm_split_view); full_btn = glade_xml_get_widget(xml, "button6"); g_signal_connect(full_btn, "clicked", G_CALLBACK(on_full_clicked), NULL); - replace_button_icon(xml, main_wnd->window, style, - "button6", (gchar **) xpm_tree_view); + replace_button_icon(full_btn, xpm_tree_view); widget = glade_xml_get_widget(xml, "button7"); g_signal_connect(widget, "clicked", -- cgit v1.2.3 From 20f375cbfe4f7e3870226f68877c9285bd8401fe Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:28 +0900 Subject: kconfig: gconf: make introduction, about, license dialogs modal These are modal dialogs in xconfig. Make them modal in gconfig as well. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 045729d76feb..0e9e078f9c34 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -313,10 +313,8 @@ static void on_introduction1_activate(GtkMenuItem *menuitem, gpointer user_data) GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", intro_text); - g_signal_connect_swapped(GTK_OBJECT(dialog), "response", - G_CALLBACK(gtk_widget_destroy), - GTK_OBJECT(dialog)); - gtk_widget_show_all(dialog); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); } static void on_about1_activate(GtkMenuItem *menuitem, gpointer user_data) @@ -330,10 +328,8 @@ static void on_about1_activate(GtkMenuItem *menuitem, gpointer user_data) GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", about_text); - g_signal_connect_swapped(GTK_OBJECT(dialog), "response", - G_CALLBACK(gtk_widget_destroy), - GTK_OBJECT(dialog)); - gtk_widget_show_all(dialog); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); } static void on_license1_activate(GtkMenuItem *menuitem, gpointer user_data) @@ -348,10 +344,8 @@ static void on_license1_activate(GtkMenuItem *menuitem, gpointer user_data) GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", license_text); - g_signal_connect_swapped(GTK_OBJECT(dialog), "response", - G_CALLBACK(gtk_widget_destroy), - GTK_OBJECT(dialog)); - gtk_widget_show_all(dialog); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); } /* toolbar handlers */ -- cgit v1.2.3 From 1bd81df0b1cc8d17b7818889c4c1cdf53415e606 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:29 +0900 Subject: kconfig: gconf: remove global 'tree' variable Pass the tree store as a function parameter to make it clearer which tree is being updated. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 0e9e078f9c34..b0dffca142a0 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -43,7 +43,7 @@ static GtkWidget *save_menu_item; static GtkTextTag *tag1, *tag2; -static GtkTreeStore *tree1, *tree2, *tree; +static GtkTreeStore *tree1, *tree2; static GtkTreeModel *model1, *model2; static struct menu *current; // current node for SINGLE view @@ -57,7 +57,7 @@ enum { }; static void display_list(void); -static void display_tree(struct menu *menu); +static void display_tree(GtkTreeStore *store, struct menu *menu); static void display_tree_part(void); static void update_tree(struct menu *src, GtkTreeIter * dst); @@ -147,7 +147,7 @@ static void set_view_mode(enum view_mode mode) break; case FULL_VIEW: gtk_tree_store_clear(tree2); - display_tree(&rootmenu); + display_tree(tree2, &rootmenu); gtk_widget_set_sensitive(full_btn, FALSE); break; } @@ -273,7 +273,7 @@ static void on_set_option_mode1_activate(GtkMenuItem *menuitem, { opt_mode = OPT_NORMAL; gtk_tree_store_clear(tree2); - display_tree(&rootmenu); /* instead of update_tree to speed-up */ + display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ } static void on_set_option_mode2_activate(GtkMenuItem *menuitem, @@ -281,7 +281,7 @@ static void on_set_option_mode2_activate(GtkMenuItem *menuitem, { opt_mode = OPT_ALL; gtk_tree_store_clear(tree2); - display_tree(&rootmenu); /* instead of update_tree to speed-up */ + display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ } static void on_set_option_mode3_activate(GtkMenuItem *menuitem, @@ -289,7 +289,7 @@ static void on_set_option_mode3_activate(GtkMenuItem *menuitem, { opt_mode = OPT_PROMPT; gtk_tree_store_clear(tree2); - display_tree(&rootmenu); /* instead of update_tree to speed-up */ + display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ } static void on_introduction1_activate(GtkMenuItem *menuitem, gpointer user_data) @@ -853,7 +853,8 @@ static gchar **fill_row(struct menu *menu) /* Set the node content with a row of strings */ -static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row) +static void set_node(GtkTreeStore *tree, GtkTreeIter *node, + struct menu *menu, gchar **row) { GdkColor color; gboolean success; @@ -977,7 +978,7 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) gtk_tree_store_insert_before(tree2, child2, dst, sibling); - set_node(child2, menu1, fill_row(menu1)); + set_node(tree2, child2, menu1, fill_row(menu1)); if (menu2 == NULL) valid = TRUE; } else { // remove node @@ -991,7 +992,7 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) goto reparse; // next child } } else if (sym && (child1->flags & MENU_CHANGED)) { - set_node(child2, menu1, fill_row(menu1)); + set_node(tree2, child2, menu1, fill_row(menu1)); } update_tree(child1, child2); @@ -1002,7 +1003,8 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) /* Display the whole tree (single/split/full view) */ -static void _display_tree(struct menu *menu, GtkTreeIter *parent) +static void _display_tree(GtkTreeStore *tree, struct menu *menu, + GtkTreeIter *parent) { struct property *prop; struct menu *child; @@ -1030,7 +1032,7 @@ static void _display_tree(struct menu *menu, GtkTreeIter *parent) (opt_mode == OPT_PROMPT && menu_has_prompt(child)) || (opt_mode == OPT_ALL && menu_get_prompt(child))) { gtk_tree_store_append(tree, &iter, parent); - set_node(&iter, child, fill_row(child)); + set_node(tree, &iter, child, fill_row(child)); } if ((view_mode != FULL_VIEW) && (ptype == P_MENU) @@ -1044,13 +1046,13 @@ static void _display_tree(struct menu *menu, GtkTreeIter *parent) if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) || (view_mode == FULL_VIEW) || (view_mode == SPLIT_VIEW)) - _display_tree(child, &iter); + _display_tree(tree, child, &iter); } } -static void display_tree(struct menu *menu) +static void display_tree(GtkTreeStore *store, struct menu *menu) { - _display_tree(menu, NULL); + _display_tree(store, menu, NULL); } /* Display a part of the tree starting at current node (single/split view) */ @@ -1058,11 +1060,11 @@ static void display_tree_part(void) { gtk_tree_store_clear(tree2); if (view_mode == SINGLE_VIEW) - display_tree(current); + display_tree(tree2, current); else if (view_mode == SPLIT_VIEW) - display_tree(browsed); + display_tree(tree2, browsed); else if (view_mode == FULL_VIEW) - display_tree(&rootmenu); + display_tree(tree2, &rootmenu); gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } @@ -1071,10 +1073,8 @@ static void display_list(void) { gtk_tree_store_clear(tree1); - tree = tree1; - display_tree(&rootmenu); + display_tree(tree1, &rootmenu); gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); - tree = tree2; } static void fixup_rootmenu(struct menu *menu) @@ -1259,7 +1259,7 @@ static void init_main_window(const gchar *glade_file) static void init_tree_model(void) { - tree = tree2 = gtk_tree_store_new(COL_NUMBER, + tree2 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, -- cgit v1.2.3 From ecaa87d4e9c2820a376270955cd166cd77f82891 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:30 +0900 Subject: kconfig: gconf: merge 'current' and 'browsed' global variables The 'current' (SINGLE view) and 'browsed' (SPLIT_VIEW) variables serve similar purposes and are not needed at the same time. Merge them. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index b0dffca142a0..bebd18ae07b0 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -46,8 +46,7 @@ static GtkTextTag *tag1, *tag2; static GtkTreeStore *tree1, *tree2; static GtkTreeModel *model1, *model2; -static struct menu *current; // current node for SINGLE view -static struct menu *browsed; // browsed node for SPLIT view +static struct menu *browsed; // browsed menu for SINGLE/SPLIT view enum { COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE, @@ -136,7 +135,7 @@ static void set_view_mode(enum view_mode mode) switch (mode) { case SINGLE_VIEW: - current = &rootmenu; + browsed = &rootmenu; display_tree_part(); gtk_widget_set_sensitive(single_btn, FALSE); break; @@ -353,13 +352,13 @@ static void on_back_clicked(GtkButton *button, gpointer user_data) { enum prop_type ptype; - current = current->parent; - ptype = current->prompt ? current->prompt->type : P_UNKNOWN; + browsed = browsed->parent; + ptype = browsed->prompt ? browsed->prompt->type : P_UNKNOWN; if (ptype != P_MENU) - current = current->parent; + browsed = browsed->parent; display_tree_part(); - if (current == &rootmenu) + if (browsed == &rootmenu) gtk_widget_set_sensitive(back_btn, FALSE); } @@ -612,7 +611,7 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) { // goes down into menu - current = menu; + browsed = menu; display_tree_part(); gtk_widget_set_sensitive(back_btn, TRUE); } else if (col == COL_OPTION) { @@ -711,14 +710,11 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); - if (event->type == GDK_2BUTTON_PRESS) { + if (event->type == GDK_2BUTTON_PRESS) toggle_sym_value(menu); - current = menu; - display_tree_part(); - } else { - browsed = menu; - display_tree_part(); - } + + browsed = menu; + display_tree_part(); gtk_tree_view_set_cursor(view, path, NULL, FALSE); gtk_widget_grab_focus(tree2_w); @@ -1012,7 +1008,7 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, GtkTreeIter iter; if (menu == &rootmenu) - current = &rootmenu; + browsed = &rootmenu; for (child = menu->list; child; child = child->next) { prop = child->prompt; @@ -1059,9 +1055,7 @@ static void display_tree(GtkTreeStore *store, struct menu *menu) static void display_tree_part(void) { gtk_tree_store_clear(tree2); - if (view_mode == SINGLE_VIEW) - display_tree(tree2, current); - else if (view_mode == SPLIT_VIEW) + if (view_mode == SINGLE_VIEW || view_mode == SPLIT_VIEW) display_tree(tree2, browsed); else if (view_mode == FULL_VIEW) display_tree(tree2, &rootmenu); -- cgit v1.2.3 From ab026457d3f8132b62f6855840817467ea92671e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:31 +0900 Subject: kconfig: gconf: preserve menu selection when switching view mode Preserve the current menu selection when switching to a different view mode, as it improves usability. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 87 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index bebd18ae07b0..eed6a10660eb 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -47,6 +47,7 @@ static GtkTreeStore *tree1, *tree2; static GtkTreeModel *model1, *model2; static struct menu *browsed; // browsed menu for SINGLE/SPLIT view +static struct menu *selected; // selected entry enum { COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE, @@ -114,6 +115,49 @@ static void text_insert_msg(const char *title, const char *message) NULL); } +static void _select_menu(GtkTreeView *view, GtkTreeModel *model, + GtkTreeIter *parent, struct menu *match) +{ + GtkTreeIter iter; + gboolean valid; + + valid = gtk_tree_model_iter_children(model, &iter, parent); + while (valid) { + struct menu *menu; + + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); + + if (menu == match) { + GtkTreeSelection *selection; + GtkTreePath *path; + + /* + * Expand parents to reflect the selection, and + * scroll down to it. + */ + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_view_expand_to_path(view, path); + gtk_tree_view_scroll_to_cell(view, path, NULL, TRUE, + 0.5, 0.0); + gtk_tree_path_free(path); + + selection = gtk_tree_view_get_selection(view); + gtk_tree_selection_select_iter(selection, &iter); + + text_insert_help(menu); + } + + _select_menu(view, model, &iter, match); + + valid = gtk_tree_model_iter_next(model, &iter); + } +} + +static void select_menu(GtkTreeView *view, struct menu *match) +{ + _select_menu(view, gtk_tree_view_get_model(view), NULL, match); +} + static void set_view_mode(enum view_mode mode) { view_mode = mode; @@ -135,24 +179,39 @@ static void set_view_mode(enum view_mode mode) switch (mode) { case SINGLE_VIEW: - browsed = &rootmenu; + if (selected) + browsed = menu_get_parent_menu(selected) ?: &rootmenu; + else + browsed = &rootmenu; display_tree_part(); + text_insert_msg("", ""); + select_menu(GTK_TREE_VIEW(tree2_w), selected); gtk_widget_set_sensitive(single_btn, FALSE); break; case SPLIT_VIEW: + browsed = selected; + while (browsed && !(browsed->flags & MENU_ROOT)) + browsed = browsed->parent; gtk_tree_store_clear(tree2); display_list(); + if (browsed) + display_tree(tree2, browsed); + text_insert_msg("", ""); + select_menu(GTK_TREE_VIEW(tree1_w), browsed); + select_menu(GTK_TREE_VIEW(tree2_w), selected); gtk_widget_set_sensitive(split_btn, FALSE); break; case FULL_VIEW: gtk_tree_store_clear(tree2); display_tree(tree2, &rootmenu); + text_insert_msg("", ""); + select_menu(GTK_TREE_VIEW(tree2_w), selected); gtk_widget_set_sensitive(full_btn, FALSE); break; } - if (mode != SINGLE_VIEW) - gtk_widget_set_sensitive(back_btn, FALSE); + gtk_widget_set_sensitive(back_btn, + mode == SINGLE_VIEW && browsed != &rootmenu); } /* Menu & Toolbar Callbacks */ @@ -604,6 +663,8 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, return FALSE; gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); + selected = menu; + col = column2index(column); if (event->type == GDK_2BUTTON_PRESS) { enum prop_type ptype; @@ -713,8 +774,12 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, if (event->type == GDK_2BUTTON_PRESS) toggle_sym_value(menu); - browsed = menu; - display_tree_part(); + selected = menu; + + if (menu->type == M_MENU) { + browsed = menu; + display_tree_part(); + } gtk_tree_view_set_cursor(view, path, NULL, FALSE); gtk_widget_grab_focus(tree2_w); @@ -1007,10 +1072,16 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, enum prop_type ptype; GtkTreeIter iter; - if (menu == &rootmenu) - browsed = &rootmenu; - for (child = menu->list; child; child = child->next) { + /* + * REVISIT: + * menu_finalize() creates empty "if" entries. + * Do not confuse gtk_tree_model_get(), which would otherwise + * return "if" menu entry. + */ + if (child->type == M_IF) + continue; + prop = child->prompt; ptype = prop ? prop->type : P_UNKNOWN; -- cgit v1.2.3 From e3795479366833d576f8e096be8ef9f42f9d124e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:32 +0900 Subject: kconfig: gconf: use GtkTreeModelFilter to control row visibility Currently, update_tree() adds/removes entries to show/hide rows. This approach is extremely complicated. Use the tree model filter to control row visibility instead. Do not toggle the MENU_CHANGED flag, as it is hard to control this correctly. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 217 ++++++++++++++++++------------------------------ 1 file changed, 80 insertions(+), 137 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index eed6a10660eb..e2b98b220513 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -59,7 +59,9 @@ enum { static void display_list(void); static void display_tree(GtkTreeStore *store, struct menu *menu); static void display_tree_part(void); -static void update_tree(struct menu *src, GtkTreeIter * dst); +static gchar **fill_row(struct menu *menu); +static void set_node(GtkTreeStore *tree, GtkTreeIter *node, + struct menu *menu, gchar **row); static void conf_changed(bool dirty) { @@ -158,6 +160,47 @@ static void select_menu(GtkTreeView *view, struct menu *match) _select_menu(view, gtk_tree_view_get_model(view), NULL, match); } +static void _update_row_visibility(GtkTreeView *view) +{ + GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(view)); + + gtk_tree_model_filter_refilter(filter); +} + +static void update_row_visibility(void) +{ + if (view_mode == SPLIT_VIEW) + _update_row_visibility(GTK_TREE_VIEW(tree1_w)); + _update_row_visibility(GTK_TREE_VIEW(tree2_w)); +} + +static void _update_tree(GtkTreeStore *store, GtkTreeIter *parent) +{ + GtkTreeModel *model = GTK_TREE_MODEL(store); + GtkTreeIter iter; + gboolean valid; + + valid = gtk_tree_model_iter_children(model, &iter, parent); + while (valid) { + struct menu *menu; + + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); + + if (menu) + set_node(store, &iter, menu, fill_row(menu)); + + _update_tree(store, &iter); + + valid = gtk_tree_model_iter_next(model, &iter); + } +} + +static void update_tree(GtkTreeStore *store) +{ + _update_tree(store, NULL); + update_row_visibility(); +} + static void set_view_mode(enum view_mode mode) { view_mode = mode; @@ -330,24 +373,21 @@ static void on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data) { opt_mode = OPT_NORMAL; - gtk_tree_store_clear(tree2); - display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ + update_row_visibility(); } static void on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data) { opt_mode = OPT_ALL; - gtk_tree_store_clear(tree2); - display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ + update_row_visibility(); } static void on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data) { opt_mode = OPT_PROMPT; - gtk_tree_store_clear(tree2); - display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ + update_row_visibility(); } static void on_introduction1_activate(GtkMenuItem *menuitem, gpointer user_data) @@ -561,7 +601,7 @@ static void renderer_edited(GtkCellRendererText * cell, sym_set_string_value(sym, new_def); - update_tree(&rootmenu, NULL); + update_tree(tree2); free: gtk_tree_path_free(path); @@ -592,9 +632,9 @@ static void change_sym_value(struct menu *menu, gint col) newval = yes; sym_set_tristate_value(sym, newval); if (view_mode == FULL_VIEW) - update_tree(&rootmenu, NULL); + update_tree(tree2); else if (view_mode == SPLIT_VIEW) { - update_tree(browsed, NULL); + update_tree(tree2); display_list(); } else if (view_mode == SINGLE_VIEW) @@ -615,9 +655,9 @@ static void toggle_sym_value(struct menu *menu) sym_toggle_tristate_value(menu->sym); if (view_mode == FULL_VIEW) - update_tree(&rootmenu, NULL); + update_tree(tree2); else if (view_mode == SPLIT_VIEW) { - update_tree(browsed, NULL); + update_tree(tree2); display_list(); } else if (view_mode == SINGLE_VIEW) @@ -844,7 +884,6 @@ static gchar **fill_row(struct menu *menu) row[COL_NAME] = g_strdup(sym->name); sym_calc_value(sym); - menu->flags &= ~MENU_CHANGED; if (sym_is_choice(sym)) { // parse childs for getting final value struct menu *child; @@ -949,120 +988,6 @@ static void set_node(GtkTreeStore *tree, GtkTreeIter *node, g_object_unref(pix); } -/* Find a node in the GTK+ tree */ -static GtkTreeIter found; - -/* - * Find a menu in the GtkTree starting at parent. - */ -static GtkTreeIter *gtktree_iter_find_node(GtkTreeIter *parent, - struct menu *tofind) -{ - GtkTreeIter iter; - GtkTreeIter *child = &iter; - gboolean valid; - GtkTreeIter *ret; - - valid = gtk_tree_model_iter_children(model2, child, parent); - while (valid) { - struct menu *menu; - - gtk_tree_model_get(model2, child, 6, &menu, -1); - - if (menu == tofind) { - memcpy(&found, child, sizeof(GtkTreeIter)); - return &found; - } - - ret = gtktree_iter_find_node(child, tofind); - if (ret) - return ret; - - valid = gtk_tree_model_iter_next(model2, child); - } - - return NULL; -} - - -/* - * Update the tree by adding/removing entries - * Does not change other nodes - */ -static void update_tree(struct menu *src, GtkTreeIter * dst) -{ - struct menu *child1; - GtkTreeIter iter, tmp; - GtkTreeIter *child2 = &iter; - gboolean valid; - GtkTreeIter *sibling; - struct symbol *sym; - struct menu *menu1, *menu2; - - valid = gtk_tree_model_iter_children(model2, child2, dst); - for (child1 = src->list; child1; child1 = child1->next) { - - sym = child1->sym; - - reparse: - menu1 = child1; - if (valid) - gtk_tree_model_get(model2, child2, COL_MENU, - &menu2, -1); - else - menu2 = NULL; // force adding of a first child - - if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) || - (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) || - (opt_mode == OPT_ALL && !menu_get_prompt(child1))) { - - /* remove node */ - if (gtktree_iter_find_node(dst, menu1) != NULL) { - memcpy(&tmp, child2, sizeof(GtkTreeIter)); - valid = gtk_tree_model_iter_next(model2, - child2); - gtk_tree_store_remove(tree2, &tmp); - if (!valid) - return; /* next parent */ - else - goto reparse; /* next child */ - } else - continue; - } - - if (menu1 != menu2) { - if (gtktree_iter_find_node(dst, menu1) == NULL) { // add node - if (!valid && !menu2) - sibling = NULL; - else - sibling = child2; - gtk_tree_store_insert_before(tree2, - child2, - dst, sibling); - set_node(tree2, child2, menu1, fill_row(menu1)); - if (menu2 == NULL) - valid = TRUE; - } else { // remove node - memcpy(&tmp, child2, sizeof(GtkTreeIter)); - valid = gtk_tree_model_iter_next(model2, - child2); - gtk_tree_store_remove(tree2, &tmp); - if (!valid) - return; // next parent - else - goto reparse; // next child - } - } else if (sym && (child1->flags & MENU_CHANGED)) { - set_node(tree2, child2, menu1, fill_row(menu1)); - } - - update_tree(child1, child2); - - valid = gtk_tree_model_iter_next(model2, child2); - } -} - - /* Display the whole tree (single/split/full view) */ static void _display_tree(GtkTreeStore *tree, struct menu *menu, GtkTreeIter *parent) @@ -1085,8 +1010,6 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, prop = child->prompt; ptype = prop ? prop->type : P_UNKNOWN; - menu->flags &= ~MENU_CHANGED; - if ((view_mode == SPLIT_VIEW) && !(child->flags & MENU_ROOT) && (tree == tree1)) continue; @@ -1095,12 +1018,8 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, && (tree == tree2)) continue; - if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) || - (opt_mode == OPT_PROMPT && menu_has_prompt(child)) || - (opt_mode == OPT_ALL && menu_get_prompt(child))) { - gtk_tree_store_append(tree, &iter, parent); - set_node(tree, &iter, child, fill_row(child)); - } + gtk_tree_store_append(tree, &iter, parent); + set_node(tree, &iter, child, fill_row(child)); if ((view_mode != FULL_VIEW) && (ptype == P_MENU) && (tree == tree2)) @@ -1322,6 +1241,20 @@ static void init_main_window(const gchar *glade_file) conf_set_changed_callback(conf_changed); } +static gboolean visible_func(GtkTreeModel *model, GtkTreeIter *iter, + gpointer data) +{ + struct menu *menu; + + gtk_tree_model_get(model, iter, COL_MENU, &menu, -1); + + if (!menu) + return FALSE; + + return menu_is_visible(menu) || opt_mode == OPT_ALL || + (opt_mode == OPT_PROMPT && menu_has_prompt(menu)); +} + static void init_tree_model(void) { tree2 = gtk_tree_store_new(COL_NUMBER, @@ -1353,8 +1286,13 @@ static void init_left_tree(void) GtkCellRenderer *renderer; GtkTreeSelection *sel; GtkTreeViewColumn *column; + GtkTreeModel *filter; - gtk_tree_view_set_model(view, model1); + filter = gtk_tree_model_filter_new(model1, NULL); + + gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), + visible_func, NULL, NULL); + gtk_tree_view_set_model(view, filter); column = gtk_tree_view_column_new(); gtk_tree_view_append_column(view, column); @@ -1388,9 +1326,14 @@ static void init_right_tree(void) GtkCellRenderer *renderer; GtkTreeSelection *sel; GtkTreeViewColumn *column; + GtkTreeModel *filter; gint i; - gtk_tree_view_set_model(view, model2); + filter = gtk_tree_model_filter_new(model2, NULL); + + gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), + visible_func, NULL, NULL); + gtk_tree_view_set_model(view, filter); column = gtk_tree_view_column_new(); gtk_tree_view_append_column(view, column); -- cgit v1.2.3 From b674af6ec8541151750b424520fc1acec32deae9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:33 +0900 Subject: kconfig: gconf: remove global 'model1' and 'model2' variables These variables are unnecessary because the current model can be retrieved using gtk_tree_view_get_model(). Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index e2b98b220513..bf3151382e5d 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -44,7 +44,6 @@ static GtkWidget *save_menu_item; static GtkTextTag *tag1, *tag2; static GtkTreeStore *tree1, *tree2; -static GtkTreeModel *model1, *model2; static struct menu *browsed; // browsed menu for SINGLE/SPLIT view static struct menu *selected; // selected entry @@ -1266,7 +1265,6 @@ static void init_tree_model(void) G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); - model2 = GTK_TREE_MODEL(tree2); tree1 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, @@ -1277,7 +1275,6 @@ static void init_tree_model(void) G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); - model1 = GTK_TREE_MODEL(tree1); } static void init_left_tree(void) @@ -1288,7 +1285,7 @@ static void init_left_tree(void) GtkTreeViewColumn *column; GtkTreeModel *filter; - filter = gtk_tree_model_filter_new(model1, NULL); + filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree1), NULL); gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), visible_func, NULL, NULL); @@ -1329,7 +1326,7 @@ static void init_right_tree(void) GtkTreeModel *filter; gint i; - filter = gtk_tree_model_filter_new(model2, NULL); + filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree2), NULL); gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), visible_func, NULL, NULL); -- cgit v1.2.3 From ed332436f3ca8a130ee9fc49d0882af5fbc344ef Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:34 +0900 Subject: kconfig: gconf: remove init_tree_model() Move the relevant code into init_left_tree() or init_right_tree(). Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index bf3151382e5d..8c024e93c302 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1254,17 +1254,13 @@ static gboolean visible_func(GtkTreeModel *model, GtkTreeIter *iter, (opt_mode == OPT_PROMPT && menu_has_prompt(menu)); } -static void init_tree_model(void) +static void init_left_tree(void) { - tree2 = gtk_tree_store_new(COL_NUMBER, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, - G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); + GtkTreeView *view = GTK_TREE_VIEW(tree1_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + GtkTreeModel *filter; tree1 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, @@ -1275,15 +1271,6 @@ static void init_tree_model(void) G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); -} - -static void init_left_tree(void) -{ - GtkTreeView *view = GTK_TREE_VIEW(tree1_w); - GtkCellRenderer *renderer; - GtkTreeSelection *sel; - GtkTreeViewColumn *column; - GtkTreeModel *filter; filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree1), NULL); @@ -1326,6 +1313,16 @@ static void init_right_tree(void) GtkTreeModel *filter; gint i; + tree2 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree2), NULL); gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), @@ -1448,7 +1445,6 @@ int main(int ac, char *av[]) /* Load the interface and connect signals */ init_main_window(glade_file); - init_tree_model(); init_left_tree(); init_right_tree(); -- cgit v1.2.3 From 64285dc5c41fc7a031695c2c286a2bfef9eaf2c6 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:35 +0900 Subject: kconfig: gconf: inline fill_row() into set_node() The row[] array is used to prepare data passed to set_node(), but this indirection is unnecessary. Squash fill_row() into set_node() and call gtk_tree_store_set() directly. Also, calling gdk_pixbuf_new_from_xpm_data() for every row is inefficient. Call it once and store the resulting pixbuf in a global variable. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 272 +++++++++++++++++++----------------------------- 1 file changed, 106 insertions(+), 166 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 8c024e93c302..9df32f47bf6b 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -44,6 +44,7 @@ static GtkWidget *save_menu_item; static GtkTextTag *tag1, *tag2; static GtkTreeStore *tree1, *tree2; +static GdkPixbuf *pix_menu; static struct menu *browsed; // browsed menu for SINGLE/SPLIT view static struct menu *selected; // selected entry @@ -58,9 +59,6 @@ enum { static void display_list(void); static void display_tree(GtkTreeStore *store, struct menu *menu); static void display_tree_part(void); -static gchar **fill_row(struct menu *menu); -static void set_node(GtkTreeStore *tree, GtkTreeIter *node, - struct menu *menu, gchar **row); static void conf_changed(bool dirty) { @@ -173,6 +171,104 @@ static void update_row_visibility(void) _update_row_visibility(GTK_TREE_VIEW(tree2_w)); } +static void set_node(GtkTreeStore *tree, GtkTreeIter *node, struct menu *menu) +{ + struct symbol *sym = menu->sym; + tristate val; + gchar *option; + const gchar *_no = ""; + const gchar *_mod = ""; + const gchar *_yes = ""; + const gchar *value = ""; + GdkColor color; + gboolean editable = FALSE; + gboolean btnvis = FALSE; + + option = g_strdup_printf("%s %s %s %s", + menu->type == M_COMMENT ? "***" : "", + menu_get_prompt(menu), + menu->type == M_COMMENT ? "***" : "", + sym && !sym_has_value(sym) ? "(NEW)" : ""); + + gdk_color_parse(menu_is_visible(menu) ? "Black" : "DarkGray", &color); + + if (!sym) + goto set; + + sym_calc_value(sym); + + if (menu->type == M_CHOICE) { // parse children to get a final value + struct symbol *def_sym = sym_calc_choice(menu); + struct menu *def_menu = NULL; + + for (struct menu *child = menu->list; child; child = child->next) { + if (menu_is_visible(child) && child->sym == def_sym) + def_menu = child; + } + + if (def_menu) + value = menu_get_prompt(def_menu); + + goto set; + } + + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + + btnvis = TRUE; + + val = sym_get_tristate_value(sym); + switch (val) { + case no: + _no = "N"; + value = "N"; + break; + case mod: + _mod = "M"; + value = "M"; + break; + case yes: + _yes = "Y"; + value = "Y"; + break; + } + + if (val != no && sym_tristate_within_range(sym, no)) + _no = "_"; + if (val != mod && sym_tristate_within_range(sym, mod)) + _mod = "_"; + if (val != yes && sym_tristate_within_range(sym, yes)) + _yes = "_"; + break; + default: + value = sym_get_string_value(sym); + editable = TRUE; + break; + } + +set: + gtk_tree_store_set(tree, node, + COL_OPTION, option, + COL_NAME, sym ? sym->name : "", + COL_NO, _no, + COL_MOD, _mod, + COL_YES, _yes, + COL_VALUE, value, + COL_MENU, (gpointer) menu, + COL_COLOR, &color, + COL_EDIT, editable, + COL_PIXBUF, pix_menu, + COL_PIXVIS, view_mode == SINGLE_VIEW && menu->type == M_MENU, + COL_BTNVIS, btnvis, + COL_BTNACT, _yes[0] == 'Y', + COL_BTNINC, _mod[0] == 'M', + COL_BTNRAD, sym && sym_is_choice_value(sym), + -1); + + g_free(option); +} + static void _update_tree(GtkTreeStore *store, GtkTreeIter *parent) { GtkTreeModel *model = GTK_TREE_MODEL(store); @@ -186,7 +282,7 @@ static void _update_tree(GtkTreeStore *store, GtkTreeIter *parent) gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); if (menu) - set_node(store, &iter, menu, fill_row(menu)); + set_node(store, &iter, menu); _update_tree(store, &iter); @@ -565,6 +661,9 @@ static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, gtk_widget_destroy(dialog); + if (!ret) + g_object_unref(pix_menu); + return ret; } @@ -826,167 +925,6 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, return FALSE; } - -/* Fill a row of strings */ -static gchar **fill_row(struct menu *menu) -{ - static gchar *row[COL_NUMBER]; - struct symbol *sym = menu->sym; - const char *def; - int stype; - tristate val; - enum prop_type ptype; - int i; - - for (i = COL_OPTION; i <= COL_COLOR; i++) - g_free(row[i]); - bzero(row, sizeof(row)); - - ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; - - row[COL_OPTION] = - g_strdup_printf("%s %s %s %s", - ptype == P_COMMENT ? "***" : "", - menu_get_prompt(menu), - ptype == P_COMMENT ? "***" : "", - sym && !sym_has_value(sym) ? "(NEW)" : ""); - - if (opt_mode == OPT_ALL && !menu_is_visible(menu)) - row[COL_COLOR] = g_strdup("DarkGray"); - else if (opt_mode == OPT_PROMPT && - menu_has_prompt(menu) && !menu_is_visible(menu)) - row[COL_COLOR] = g_strdup("DarkGray"); - else - row[COL_COLOR] = g_strdup("Black"); - - switch (ptype) { - case P_MENU: - row[COL_PIXBUF] = (gchar *) xpm_menu; - if (view_mode == SINGLE_VIEW) - row[COL_PIXVIS] = GINT_TO_POINTER(TRUE); - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - break; - case P_COMMENT: - row[COL_PIXBUF] = (gchar *) xpm_void; - row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - break; - default: - row[COL_PIXBUF] = (gchar *) xpm_void; - row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); - row[COL_BTNVIS] = GINT_TO_POINTER(TRUE); - break; - } - - if (!sym) - return row; - row[COL_NAME] = g_strdup(sym->name); - - sym_calc_value(sym); - - if (sym_is_choice(sym)) { // parse childs for getting final value - struct menu *child; - struct symbol *def_sym = sym_calc_choice(menu); - struct menu *def_menu = NULL; - - for (child = menu->list; child; child = child->next) { - if (menu_is_visible(child) - && child->sym == def_sym) - def_menu = child; - } - - if (def_menu) - row[COL_VALUE] = - g_strdup(menu_get_prompt(def_menu)); - - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - return row; - } - if (sym_is_choice_value(sym)) - row[COL_BTNRAD] = GINT_TO_POINTER(TRUE); - - stype = sym_get_type(sym); - switch (stype) { - case S_BOOLEAN: - case S_TRISTATE: - val = sym_get_tristate_value(sym); - switch (val) { - case no: - row[COL_NO] = g_strdup("N"); - row[COL_VALUE] = g_strdup("N"); - row[COL_BTNACT] = GINT_TO_POINTER(FALSE); - row[COL_BTNINC] = GINT_TO_POINTER(FALSE); - break; - case mod: - row[COL_MOD] = g_strdup("M"); - row[COL_VALUE] = g_strdup("M"); - row[COL_BTNINC] = GINT_TO_POINTER(TRUE); - break; - case yes: - row[COL_YES] = g_strdup("Y"); - row[COL_VALUE] = g_strdup("Y"); - row[COL_BTNACT] = GINT_TO_POINTER(TRUE); - row[COL_BTNINC] = GINT_TO_POINTER(FALSE); - break; - } - - if (val != no && sym_tristate_within_range(sym, no)) - row[COL_NO] = g_strdup("_"); - if (val != mod && sym_tristate_within_range(sym, mod)) - row[COL_MOD] = g_strdup("_"); - if (val != yes && sym_tristate_within_range(sym, yes)) - row[COL_YES] = g_strdup("_"); - break; - case S_INT: - case S_HEX: - case S_STRING: - def = sym_get_string_value(sym); - row[COL_VALUE] = g_strdup(def); - row[COL_EDIT] = GINT_TO_POINTER(TRUE); - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - break; - } - - return row; -} - - -/* Set the node content with a row of strings */ -static void set_node(GtkTreeStore *tree, GtkTreeIter *node, - struct menu *menu, gchar **row) -{ - GdkColor color; - gboolean success; - GdkPixbuf *pix; - - pix = gdk_pixbuf_new_from_xpm_data((const char **) - row[COL_PIXBUF]); - - gdk_color_parse(row[COL_COLOR], &color); - gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1, - FALSE, FALSE, &success); - - gtk_tree_store_set(tree, node, - COL_OPTION, row[COL_OPTION], - COL_NAME, row[COL_NAME], - COL_NO, row[COL_NO], - COL_MOD, row[COL_MOD], - COL_YES, row[COL_YES], - COL_VALUE, row[COL_VALUE], - COL_MENU, (gpointer) menu, - COL_COLOR, &color, - COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]), - COL_PIXBUF, pix, - COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]), - COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]), - COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]), - COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]), - COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]), - -1); - - g_object_unref(pix); -} - /* Display the whole tree (single/split/full view) */ static void _display_tree(GtkTreeStore *tree, struct menu *menu, GtkTreeIter *parent) @@ -1018,7 +956,7 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, continue; gtk_tree_store_append(tree, &iter, parent); - set_node(tree, &iter, child, fill_row(child)); + set_node(tree, &iter, child); if ((view_mode != FULL_VIEW) && (ptype == P_MENU) && (tree == tree2)) @@ -1393,6 +1331,8 @@ static void init_right_tree(void) g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(renderer_edited), tree2_w); + pix_menu = gdk_pixbuf_new_from_xpm_data((const char **)xpm_menu); + for (i = 0; i < COL_VALUE; i++) { column = gtk_tree_view_get_column(view, i); gtk_tree_view_column_set_resizable(column, TRUE); -- cgit v1.2.3 From 035c2f56f57432caa78378e3ab498a5fb9bd276b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:36 +0900 Subject: kconfig: gconf: do not reconstruct tree store when a symbol is changed There is no need to reconstruct the entire tree store when a symbol's value changes. Simply call gtk_tree_store_set() to update the row data. Introduce update_trees() to factor out the common update logic. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 9df32f47bf6b..73736f79ddcb 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -296,6 +296,13 @@ static void update_tree(GtkTreeStore *store) update_row_visibility(); } +static void update_trees(void) +{ + if (view_mode == SPLIT_VIEW) + update_tree(tree1); + update_tree(tree2); +} + static void set_view_mode(enum view_mode mode) { view_mode = mode; @@ -380,7 +387,7 @@ static void on_load1_activate(GtkMenuItem *menuitem, gpointer user_data) text_insert_msg("Error", "Unable to load configuration!"); else - display_tree_part(); + update_trees(); g_free(filename); } @@ -699,7 +706,7 @@ static void renderer_edited(GtkCellRendererText * cell, sym_set_string_value(sym, new_def); - update_tree(tree2); + update_trees(); free: gtk_tree_path_free(path); @@ -729,14 +736,7 @@ static void change_sym_value(struct menu *menu, gint col) if (!sym_tristate_within_range(sym, newval)) newval = yes; sym_set_tristate_value(sym, newval); - if (view_mode == FULL_VIEW) - update_tree(tree2); - else if (view_mode == SPLIT_VIEW) { - update_tree(tree2); - display_list(); - } - else if (view_mode == SINGLE_VIEW) - display_tree_part(); //fixme: keep exp/coll + update_trees(); break; case S_INT: case S_HEX: @@ -752,14 +752,7 @@ static void toggle_sym_value(struct menu *menu) return; sym_toggle_tristate_value(menu->sym); - if (view_mode == FULL_VIEW) - update_tree(tree2); - else if (view_mode == SPLIT_VIEW) { - update_tree(tree2); - display_list(); - } - else if (view_mode == SINGLE_VIEW) - display_tree_part(); //fixme: keep exp/coll + update_trees(); } static gint column2index(GtkTreeViewColumn * column) -- cgit v1.2.3 From 063a274a5e297720e18b3a1d7bbfe2d039e12192 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:37 +0900 Subject: kconfig: gconf: inline display_list() into set_view_mode() This function is now only called by set_view_mode(), so inline it for simplicity. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 73736f79ddcb..3c2e6be30c00 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -56,7 +56,6 @@ enum { COL_NUMBER }; -static void display_list(void); static void display_tree(GtkTreeStore *store, struct menu *menu); static void display_tree_part(void); @@ -337,8 +336,10 @@ static void set_view_mode(enum view_mode mode) browsed = selected; while (browsed && !(browsed->flags & MENU_ROOT)) browsed = browsed->parent; + gtk_tree_store_clear(tree1); + display_tree(tree1, &rootmenu); + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); gtk_tree_store_clear(tree2); - display_list(); if (browsed) display_tree(tree2, browsed); text_insert_msg("", ""); @@ -982,15 +983,6 @@ static void display_tree_part(void) gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } -/* Display the list in the left frame (split view) */ -static void display_list(void) -{ - gtk_tree_store_clear(tree1); - - display_tree(tree1, &rootmenu); - gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); -} - static void fixup_rootmenu(struct menu *menu) { struct menu *child; -- cgit v1.2.3 From bf5792da5ac14c5e95f1e8612df70096ee5a44d1 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:38 +0900 Subject: kconfig: gconf: remove dead code in display_tree_part() This function is no longer called in the FULL_VIEW mode, so remove the dead code. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 3c2e6be30c00..6afdba85158a 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -976,10 +976,7 @@ static void display_tree(GtkTreeStore *store, struct menu *menu) static void display_tree_part(void) { gtk_tree_store_clear(tree2); - if (view_mode == SINGLE_VIEW || view_mode == SPLIT_VIEW) - display_tree(tree2, browsed); - else if (view_mode == FULL_VIEW) - display_tree(tree2, &rootmenu); + display_tree(tree2, browsed); gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } -- cgit v1.2.3 From 475c878f971661511fb3911af96c0ee0cb533527 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:42 +0900 Subject: kconfig: gconf: replace GDK_space with GDK_KEY_space In GTK3, keysyms changed to have a KEY_ prefix. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gdk/gdkkeysyms-compat.h#L24 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 6afdba85158a..b24d02972090 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -841,7 +841,7 @@ static gboolean on_treeview2_key_press_event(GtkWidget *widget, if (path == NULL) return FALSE; - if (event->keyval == GDK_space) { + if (event->keyval == GDK_KEY_space) { if (gtk_tree_view_row_expanded(view, path)) gtk_tree_view_collapse_row(view, path); else -- cgit v1.2.3 From 7783290143b37c568538d8e699d81d1e6c8af72b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:43 +0900 Subject: kconfig: gconf: replace GTK_STOCK_{OK,NO,CANCEL} These are deprecated with GTK 3.10. [1] Use "_OK", "_no", "_Cancel". [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.10.0/gtk/deprecated/gtkstock.h#L827 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index b24d02972090..085a06667a21 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -640,11 +640,11 @@ static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), - GTK_STOCK_OK, + "_OK", GTK_RESPONSE_YES, - GTK_STOCK_NO, + "_No", GTK_RESPONSE_NO, - GTK_STOCK_CANCEL, + "_Cancel", GTK_RESPONSE_CANCEL, NULL); gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL); -- cgit v1.2.3 From ad452c27aeb80d1c3ee449250c3f790e7bd8ffaa Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:44 +0900 Subject: kconfig: gconf: remove "tooltips" property from glade The tips are still displayed without this. This property does not exist in GtkBuilder with GTK 3. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.glade | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index 8519104a3c2b..c0ada331a5bf 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -225,7 +225,6 @@ True GTK_ORIENTATION_HORIZONTAL GTK_TOOLBAR_BOTH - True True -- cgit v1.2.3 From 9d0e47c4c879dfbaa1f407ecbd4176682f711871 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:45 +0900 Subject: kconfig: gconf: replace "tooltip" property with "tooltip-text" This is no longer available in GTK 3. Use "tooltip-text" instead. Also reword "Goes up of one level" to "Goes up one level" while I am here. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.glade | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index c0ada331a5bf..35d5257f33c9 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -40,7 +40,7 @@ True - Load a config file + Load a config file _Load True @@ -50,7 +50,7 @@ True - Save the config in .config + Save the config in .config _Save True @@ -60,7 +60,7 @@ True - Save the config in a file + Save the config in a file Save _as True @@ -97,7 +97,7 @@ True - Show name + Show name Show _name True False @@ -107,7 +107,7 @@ True - Show range (Y/M/N) + Show range (Y/M/N) Show _range True False @@ -117,7 +117,7 @@ True - Show value of the option + Show value of the option Show _data True False @@ -133,7 +133,7 @@ True - Show normal options + Show normal options Show normal options True True @@ -143,7 +143,7 @@ True - Show all options + Show all options Show all _options True False @@ -154,7 +154,7 @@ True - Show all options with prompts + Show all options with prompts Show all prompt options True False @@ -230,7 +230,7 @@ True - Goes up of one level (single view) + Goes up one level (single view) Back True gtk-undo @@ -266,7 +266,7 @@ True - Load a config file + Load a config file Load True gtk-open @@ -283,7 +283,7 @@ True - Save a config file + Save a config file Save True gtk-save @@ -319,7 +319,7 @@ True - Single view + Single view Single True gtk-missing-image @@ -336,7 +336,7 @@ True - Split view + Split view Split True gtk-missing-image @@ -353,7 +353,7 @@ True - Full view + Full view Full True gtk-missing-image @@ -389,7 +389,7 @@ True - Collapse the whole tree in the right frame + Collapse the whole tree in the right frame Collapse True gtk-remove @@ -406,7 +406,7 @@ True - Expand the whole tree in the right frame + Expand the whole tree in the right frame Expand True gtk-add -- cgit v1.2.3 From ea1438f720aa2fa287237fbeb7e76a1e83a10af8 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:46 +0900 Subject: kconfig: gconf: remove unnecessary default message in text view This message looks odd because it is displayed when nothing is selected. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.glade | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index 35d5257f33c9..488342e6fce1 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -517,7 +517,6 @@ 0 0 0 - Sorry, no help available for this option yet. -- cgit v1.2.3 From 07944f94fc8c02344f283e461b1ea817a9108e17 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:47 +0900 Subject: kconfig: gconf: use gtk_check_menu_item_get_active() accessor GTK 3 removes many implementation details and struct members from its public headers. Use the gtk_check_menu_item_get_active() accessor. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/docs/reference/gtk/compiling.sgml#L85 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 085a06667a21..a02078926274 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -439,7 +439,7 @@ static void on_show_name1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; - show_name = GTK_CHECK_MENU_ITEM(menuitem)->active; + show_name = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)); col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME); if (col) gtk_tree_view_column_set_visible(col, show_name); @@ -449,7 +449,7 @@ static void on_show_range1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; - show_range = GTK_CHECK_MENU_ITEM(menuitem)->active; + show_range = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)); col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO); if (col) gtk_tree_view_column_set_visible(col, show_range); @@ -466,7 +466,7 @@ static void on_show_data1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; - show_value = GTK_CHECK_MENU_ITEM(menuitem)->active; + show_value = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)); col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE); if (col) gtk_tree_view_column_set_visible(col, show_value); -- cgit v1.2.3 From c34d632370592cb503991728afdf5287b2d1f7ed Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:48 +0900 Subject: kconfig: gconf: use gtk_dialog_get_content_area() accessor GTK 3 removes many implementation details and struct members from its public headers. Use the gtk_check_menu_item_get_active() accessor. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/docs/reference/gtk/compiling.sgml#L85 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index a02078926274..28c4b5b37448 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -628,7 +628,7 @@ static void on_window1_size_request(GtkWidget *widget, static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) { - GtkWidget *dialog, *label; + GtkWidget *dialog, *label, *content_area; gint result; gint ret = FALSE; @@ -650,7 +650,8 @@ static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, GTK_RESPONSE_CANCEL); label = gtk_label_new("\nSave configuration ?\n"); - gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); + content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_container_add(GTK_CONTAINER(content_area), label); gtk_widget_show(label); result = gtk_dialog_run(GTK_DIALOG(dialog)); -- cgit v1.2.3 From 9e0bd6db622c7c19aec5a8b248bb34493c9998e6 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:49 +0900 Subject: kconfig: gconf: remove GtkHandleBox from glade GtkHandleBox is deprecated with GTK 3.4. [1] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.4.0/gtk/deprecated/gtkhandlebox.c#L426 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.glade | 9 --------- 1 file changed, 9 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index 488342e6fce1..cd714e64cff1 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -214,13 +214,6 @@ - - True - GTK_SHADOW_OUT - GTK_POS_LEFT - GTK_POS_TOP - - True GTK_ORIENTATION_HORIZONTAL @@ -420,8 +413,6 @@ - - 0 False -- cgit v1.2.3 From 5eee4c2b2aebfd3c8f11d9722e49d838da4e4150 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Mon, 16 Jun 2025 09:59:13 +0200 Subject: checkpatch: use utf-8 match for spell checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code that checks for misspelling verifies, in a more complex regex, if $rawline matches [^\w]($misspellings)[^\w] Being $rawline a byte-string, a utf-8 character in $rawline can match the non-word-char [^\w]. E.g.: ./scripts/checkpatch.pl --git 81c2f059ab9 WARNING: 'ment' may be misspelled - perhaps 'meant'? #36: FILE: MAINTAINERS:14360: +M: Clément Léger ^^^^ Use a utf-8 version of $rawline for spell checking. Link: https://lkml.kernel.org/r/20250616-b4-checkpatch-upstream-v2-1-5600ce4a3b43@foss.st.com Signed-off-by: Antonio Borneo Signed-off-by: Clément Le Goffic Cc: Andy Whitcroft Cc: Dwaipayan Ray Cc: Joe Perches Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- scripts/checkpatch.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 664f7b7a622c..489b74d52abe 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3502,9 +3502,10 @@ sub process { # Check for various typo / spelling mistakes if (defined($misspellings) && ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { - while ($rawline =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) { + my $rawline_utf8 = decode("utf8", $rawline); + while ($rawline_utf8 =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) { my $typo = $1; - my $blank = copy_spacing($rawline); + my $blank = copy_spacing($rawline_utf8); my $ptr = substr($blank, 0, $-[1]) . "^" x length($typo); my $hereptr = "$hereline$ptr\n"; my $typo_fix = $spelling_fix{lc($typo)}; -- cgit v1.2.3 From 41a7f737685eed2700654720d3faaffdf0132135 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Jun 2025 15:46:02 +0200 Subject: scripts: gdb: move MNT_* constants to gdb-parsed Since these are now no longer defines, but in an enum. Link: https://lkml.kernel.org/r/20250618134629.25700-2-johannes@sipsolutions.net Fixes: 101f2bbab541 ("fs: convert mount flags to enum") Reviewed-by: Benjamin Berg Signed-off-by: Johannes Berg Cc: Jan Kiszka Cc: Kieran Bingham Cc: Stephen Brennan Signed-off-by: Andrew Morton --- scripts/gdb/linux/constants.py.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in index fd6bd69c5096..d5e3069f42a7 100644 --- a/scripts/gdb/linux/constants.py.in +++ b/scripts/gdb/linux/constants.py.in @@ -73,12 +73,12 @@ if IS_BUILTIN(CONFIG_MODULES): LX_GDBPARSED(MOD_RO_AFTER_INIT) /* linux/mount.h */ -LX_VALUE(MNT_NOSUID) -LX_VALUE(MNT_NODEV) -LX_VALUE(MNT_NOEXEC) -LX_VALUE(MNT_NOATIME) -LX_VALUE(MNT_NODIRATIME) -LX_VALUE(MNT_RELATIME) +LX_GDBPARSED(MNT_NOSUID) +LX_GDBPARSED(MNT_NODEV) +LX_GDBPARSED(MNT_NOEXEC) +LX_GDBPARSED(MNT_NOATIME) +LX_GDBPARSED(MNT_NODIRATIME) +LX_GDBPARSED(MNT_RELATIME) /* linux/threads.h */ LX_VALUE(NR_CPUS) -- cgit v1.2.3 From 22c2ed6996ac34df506040a069fac3e5100b5c0e Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 2 Jul 2025 16:52:00 -0700 Subject: checkpatch: check for missing sentinels in ID arrays All of the ID tables based on (of_device_id, pci_device_id, ...) require their arrays to end in an empty sentinel value. That's usually spelled with an empty initializer entry (e.g., "{}"), but also sometimes with explicit 0 entries, field initializers (e.g., '.id = ""'), or even a macro entry (like PCMCIA_DEVICE_NULL). Without a sentinel, device-matching code may read out of bounds. I've found a number of such bugs in driver reviews, and we even occasionally commit one to the tree. See commit 5751eee5c620 ("i2c: nomadik: Add missing sentinel to match table") for example. Teach checkpatch to find these ID tables, and complain if it looks like there wasn't a sentinel value. Test output: $ git format-patch -1 a0d15cc47f29be6d --stdout | scripts/checkpatch.pl - ERROR: missing sentinel in ID array #57: FILE: drivers/i2c/busses/i2c-nomadik.c:1073: +static const struct of_device_id nmk_i2c_eyeq_match_table[] = { { .compatible = "XXXXXXXXXXXXXXXXXX", .data = (void *)(NMK_I2C_EYEQ_FLAG_32B_BUS | NMK_I2C_EYEQ_FLAG_IS_EYEQ5), }, }; total: 1 errors, 0 warnings, 66 lines checked NOTE: For some of the reported defects, checkpatch may be able to mechanically convert to the typical style using --fix or --fix-inplace. "[PATCH] i2c: nomadik: switch from of_device_is_compatible() to" has style problems, please review. NOTE: If any of the errors are false positives, please report them to the maintainer, see CHECKPATCH in MAINTAINERS. When run across the entire tree (scripts/checkpatch.pl -q --types MISSING_SENTINEL -f ...), false positives exist: * where macros are used that hide the table from analysis (e.g., drivers/gpu/drm/radeon/radeon_drv.c / radeon_PCI_IDS). There are fewer than 5 of these. * where such tables are processed correctly via ARRAY_SIZE() (fewer than 5 instances). This is by far not the typical usage of *_device_id arrays. * some odd parsing artifacts, where ctx_statement_block() seems to quit in the middle of a block due to #if/#else/#endif. Also, not every "struct *_device_id" is in fact a sentinel-requiring structure, but even with such types, false positives are very rare. Link: https://lkml.kernel.org/r/20250702235245.1007351-1-briannorris@chromium.org Signed-off-by: Brian Norris Acked-by: Joe Perches Cc: Andy Whitcroft Cc: Brian Norris Cc: Dwaipayan Ray Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- scripts/checkpatch.pl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 489b74d52abe..d4c24318548c 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -685,6 +685,9 @@ our $tracing_logging_tags = qr{(?xi: [\.\!:\s]* )}; +# Device ID types like found in include/linux/mod_devicetable.h. +our $dev_id_types = qr{\b[a-z]\w*_device_id\b}; + sub edit_distance_min { my (@arr) = @_; my $len = scalar @arr; @@ -7679,6 +7682,31 @@ sub process { WARN("DUPLICATED_SYSCTL_CONST", "duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr); } + +# Check that *_device_id tables have sentinel entries. + if (defined $stat && $line =~ /struct\s+$dev_id_types\s+\w+\s*\[\s*\]\s*=\s*\{/) { + my $stripped = $stat; + + # Strip diff line prefixes. + $stripped =~ s/(^|\n)./$1/g; + # Line continuations. + $stripped =~ s/\\\n/\n/g; + # Strip whitespace, empty strings, zeroes, and commas. + $stripped =~ s/""//g; + $stripped =~ s/0x0//g; + $stripped =~ s/[\s$;,0]//g; + # Strip field assignments. + $stripped =~ s/\.$Ident=//g; + + if (!(substr($stripped, -4) eq "{}};" || + substr($stripped, -6) eq "{{}}};" || + $stripped =~ /ISAPNP_DEVICE_SINGLE_END}};$/ || + $stripped =~ /ISAPNP_CARD_END}};$/ || + $stripped =~ /NULL};$/ || + $stripped =~ /PCMCIA_DEVICE_NULL};$/)) { + ERROR("MISSING_SENTINEL", "missing sentinel in ID array\n" . "$here\n$stat\n"); + } + } } # If we have no input at all, then there is nothing to report on -- cgit v1.2.3 From 249e7ced7271d8a7e733b1fe7ea7a221eb46698f Mon Sep 17 00:00:00 2001 From: Easwar Hariharan Date: Thu, 3 Jul 2025 15:51:32 -0700 Subject: coccinelle: misc: secs_to_jiffies: implement context and report modes As requested by Ricardo and Jakub, implement report and context modes for the secs_to_jiffies Coccinelle script. While here, add the option to look for opportunities to use secs_to_jiffies() in headers. Link: https://lkml.kernel.org/r/20250703225145.152288-1-eahariha@linux.microsoft.com Signed-off-by: Easwar Hariharan Closes: https://lore.kernel.org/all/20250129-secs_to_jiffles-v1-1-35a5e16b9f03@chromium.org/ Closes: https://lore.kernel.org/all/20250221162107.409ae333@kernel.org/ Tested-by: Ricardo Ribalda Cc: Julia Lawall Cc: Nicolas Palix Cc: Jakub Kicinski Cc: Ricardo Ribalda Signed-off-by: Andrew Morton --- scripts/coccinelle/misc/secs_to_jiffies.cocci | 49 ++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/coccinelle/misc/secs_to_jiffies.cocci b/scripts/coccinelle/misc/secs_to_jiffies.cocci index 416f348174ca..f3241ce75a7b 100644 --- a/scripts/coccinelle/misc/secs_to_jiffies.cocci +++ b/scripts/coccinelle/misc/secs_to_jiffies.cocci @@ -7,26 +7,65 @@ // Confidence: High // Copyright: (C) 2024 Easwar Hariharan, Microsoft // Keywords: secs, seconds, jiffies -// +// Options: --include-headers virtual patch +virtual report +virtual context -@depends on patch@ constant C; @@ +@pconst depends on patch@ constant C; @@ - msecs_to_jiffies(C * 1000) + secs_to_jiffies(C) -@depends on patch@ constant C; @@ +@pconstms depends on patch@ constant C; @@ - msecs_to_jiffies(C * MSEC_PER_SEC) + secs_to_jiffies(C) -@depends on patch@ expression E; @@ +@pexpr depends on patch@ expression E; @@ - msecs_to_jiffies(E * 1000) + secs_to_jiffies(E) -@depends on patch@ expression E; @@ +@pexprms depends on patch@ expression E; @@ - msecs_to_jiffies(E * MSEC_PER_SEC) + secs_to_jiffies(E) + +@r depends on report && !patch@ +constant C; +expression E; +position p; +@@ + +( + msecs_to_jiffies(C@p * 1000) +| + msecs_to_jiffies(C@p * MSEC_PER_SEC) +| + msecs_to_jiffies(E@p * 1000) +| + msecs_to_jiffies(E@p * MSEC_PER_SEC) +) + +@c depends on context && !patch@ +constant C; +expression E; +@@ + +( +* msecs_to_jiffies(C * 1000) +| +* msecs_to_jiffies(C * MSEC_PER_SEC) +| +* msecs_to_jiffies(E * 1000) +| +* msecs_to_jiffies(E * MSEC_PER_SEC) +) + +@script:python depends on report@ +p << r.p; +@@ + +coccilib.report.print_report(p[0], "WARNING opportunity for secs_to_jiffies()") -- cgit v1.2.3 From f468992936894c9ce3b1659cf38c230d33b77a16 Mon Sep 17 00:00:00 2001 From: Shankari Anand Date: Thu, 26 Jun 2025 00:36:54 +0530 Subject: kconfig: nconf: Ensure null termination where strncpy is used strncpy() does not guarantee null-termination if the source string is longer than the destination buffer. Ensure the buffer is explicitly null-terminated to prevent potential string overflows or undefined behavior. Signed-off-by: Shankari Anand Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap Tested-by: Nicolas Schier Acked-by: Nicolas Schier --- scripts/kconfig/nconf.c | 2 ++ scripts/kconfig/nconf.gui.c | 1 + 2 files changed, 3 insertions(+) (limited to 'scripts') diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index c0b2dabf6c89..ae1fe5f60327 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -593,6 +593,8 @@ static void item_add_str(const char *fmt, ...) tmp_str, sizeof(k_menu_items[index].str)); + k_menu_items[index].str[sizeof(k_menu_items[index].str) - 1] = '\0'; + free_item(curses_menu_items[index]); curses_menu_items[index] = new_item( k_menu_items[index].str, diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index 4bfdf8ac2a9a..7206437e784a 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -359,6 +359,7 @@ int dialog_inputbox(WINDOW *main_window, x = (columns-win_cols)/2; strncpy(result, init, *result_len); + result[*result_len - 1] = '\0'; /* create the windows */ win = newwin(win_lines, win_cols, y, x); -- cgit v1.2.3 From 0c82f50a06aa13e6fc29e17081094489d57745fd Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:27 +0900 Subject: kconfig: gconf: fix behavior of a menu under a symbol in split view A menu can be created under a symbol. [Example] menu "outer menu" config A bool "A" menu "inner menu" depends on A config B bool "B" endmenu endmenu After being re-parented by menu_finalize(), the menu tree is structured like follows: menu "outer menu" \-- A \-- menu "inner menu" \-- B In split view, the symbol A is shown in the right pane, so all of its descendants must also be shown there. This has never worked correctly. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 28c4b5b37448..7397a51641a7 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -803,7 +803,7 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, enum prop_type ptype; ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; - if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) { + if (ptype == P_MENU && view_mode == SINGLE_VIEW && col == COL_OPTION) { // goes down into menu browsed = menu; display_tree_part(); @@ -953,8 +953,7 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, gtk_tree_store_append(tree, &iter, parent); set_node(tree, &iter, child); - if ((view_mode != FULL_VIEW) && (ptype == P_MENU) - && (tree == tree2)) + if ((view_mode == SINGLE_VIEW) && (ptype == P_MENU)) continue; /* if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT)) -- cgit v1.2.3 From 06ba76dc825703fa61cee72c2ae66508ef5f10ec Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:28 +0900 Subject: kconfig: gconf: use configure-event handler to adjust pane separator The size-request event handler is currently used to adjust the position of the horizontal separator in the right pane. However, the size-request signal is not available in GTK 3. Use the configure-event signal instead. Signed-off-by: Masahiro Yamada Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 7397a51641a7..37eec7a6bf54 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -606,23 +606,12 @@ static void on_window1_destroy(GtkObject *object, gpointer user_data) gtk_main_quit(); } -static void on_window1_size_request(GtkWidget *widget, - GtkRequisition *requisition, - gpointer user_data) +static gboolean on_window1_configure(GtkWidget *self, + GdkEventConfigure *event, + gpointer user_data) { - static gint old_h; - gint w, h; - - if (widget->window == NULL) - gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); - else - gdk_window_get_size(widget->window, &w, &h); - - if (h == old_h) - return; - old_h = h; - - gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); + gtk_paned_set_position(GTK_PANED(vpaned), 2 * event->height / 3); + return FALSE; } static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, @@ -1023,8 +1012,8 @@ static void init_main_window(const gchar *glade_file) main_wnd = glade_xml_get_widget(xml, "window1"); g_signal_connect(main_wnd, "destroy", G_CALLBACK(on_window1_destroy), NULL); - g_signal_connect(main_wnd, "size_request", - G_CALLBACK(on_window1_size_request), NULL); + g_signal_connect(main_wnd, "configure-event", + G_CALLBACK(on_window1_configure), NULL); g_signal_connect(main_wnd, "delete_event", G_CALLBACK(on_window1_delete_event), NULL); -- cgit v1.2.3 From 894ad403439e54d3cdee77a538190dd08ae54789 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:29 +0900 Subject: kconfig: gconf: rename display_tree_part() This function recreates the tree store to update the menu content. Rename it to recreate_tree() to better reflect its purpose. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 37eec7a6bf54..05ee10f5f45b 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -57,7 +57,7 @@ enum { }; static void display_tree(GtkTreeStore *store, struct menu *menu); -static void display_tree_part(void); +static void recreate_tree(void); static void conf_changed(bool dirty) { @@ -327,7 +327,7 @@ static void set_view_mode(enum view_mode mode) browsed = menu_get_parent_menu(selected) ?: &rootmenu; else browsed = &rootmenu; - display_tree_part(); + recreate_tree(); text_insert_msg("", ""); select_menu(GTK_TREE_VIEW(tree2_w), selected); gtk_widget_set_sensitive(single_btn, FALSE); @@ -558,7 +558,7 @@ static void on_back_clicked(GtkButton *button, gpointer user_data) ptype = browsed->prompt ? browsed->prompt->type : P_UNKNOWN; if (ptype != P_MENU) browsed = browsed->parent; - display_tree_part(); + recreate_tree(); if (browsed == &rootmenu) gtk_widget_set_sensitive(back_btn, FALSE); @@ -795,7 +795,7 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, if (ptype == P_MENU && view_mode == SINGLE_VIEW && col == COL_OPTION) { // goes down into menu browsed = menu; - display_tree_part(); + recreate_tree(); gtk_widget_set_sensitive(back_btn, TRUE); } else if (col == COL_OPTION) { toggle_sym_value(menu); @@ -900,7 +900,7 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, if (menu->type == M_MENU) { browsed = menu; - display_tree_part(); + recreate_tree(); } gtk_tree_view_set_cursor(view, path, NULL, FALSE); @@ -961,8 +961,8 @@ static void display_tree(GtkTreeStore *store, struct menu *menu) _display_tree(store, menu, NULL); } -/* Display a part of the tree starting at current node (single/split view) */ -static void display_tree_part(void) +/* Recreate the tree store starting at 'browsed' node */ +static void recreate_tree(void) { gtk_tree_store_clear(tree2); display_tree(tree2, browsed); -- cgit v1.2.3 From e06030c1ae3299f71ae38ccbdd4ae0a2d0aa9189 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:30 +0900 Subject: kconfig: gconf: rename gconf.glade to gconf.ui The next commit will convert this file to GtkBuilder format. Rename it in advance to reflect the intended format. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 6 +- scripts/kconfig/gconf.glade | 536 -------------------------------------------- scripts/kconfig/gconf.ui | 536 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 539 insertions(+), 539 deletions(-) delete mode 100644 scripts/kconfig/gconf.glade create mode 100644 scripts/kconfig/gconf.ui (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 05ee10f5f45b..8006cc547180 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1327,11 +1327,11 @@ int main(int ac, char *av[]) /* Determine GUI path */ env = getenv(SRCTREE); if (env) - glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL); + glade_file = g_strconcat(env, "/scripts/kconfig/gconf.ui", NULL); else if (av[0][0] == '/') - glade_file = g_strconcat(av[0], ".glade", NULL); + glade_file = g_strconcat(av[0], ".ui", NULL); else - glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL); + glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".ui", NULL); /* Conf stuffs */ if (ac > 1 && av[1][0] == '-') { diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade deleted file mode 100644 index cd714e64cff1..000000000000 --- a/scripts/kconfig/gconf.glade +++ /dev/null @@ -1,536 +0,0 @@ - - - - - - True - Gtk Kernel Configurator - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - 640 - 480 - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - - - - True - False - 0 - - - - True - - - - True - _File - True - - - - - - - True - Load a config file - _Load - True - - - - - - - True - Save the config in .config - _Save - True - - - - - - - True - Save the config in a file - Save _as - True - - - - - - True - - - - - - True - _Quit - True - - - - - - - - - - - True - _Options - True - - - - - - - True - Show name - Show _name - True - False - - - - - - True - Show range (Y/M/N) - Show _range - True - False - - - - - - True - Show value of the option - Show _data - True - False - - - - - - True - - - - - - True - Show normal options - Show normal options - True - True - - - - - - True - Show all options - Show all _options - True - False - set_option_mode1 - - - - - - True - Show all options with prompts - Show all prompt options - True - False - set_option_mode1 - - - - - - - - - - - True - _Help - True - - - - - - - True - _Introduction - True - - - - - - - True - _About - True - - - - - - - True - _License - True - - - - - - - - - 0 - False - False - - - - - - True - GTK_ORIENTATION_HORIZONTAL - GTK_TOOLBAR_BOTH - True - - - - True - Goes up one level (single view) - Back - True - gtk-undo - True - True - False - - - False - True - - - - - - True - True - True - False - - - - True - - - - - False - False - - - - - - True - Load a config file - Load - True - gtk-open - True - True - False - - - False - True - - - - - - True - Save a config file - Save - True - gtk-save - True - True - False - - - False - True - - - - - - True - True - True - False - - - - True - - - - - False - False - - - - - - True - Single view - Single - True - gtk-missing-image - True - True - False - - - False - True - - - - - - True - Split view - Split - True - gtk-missing-image - True - True - False - - - False - True - - - - - - True - Full view - Full - True - gtk-missing-image - True - True - False - - - False - True - - - - - - True - True - True - False - - - - True - - - - - False - False - - - - - - True - Collapse the whole tree in the right frame - Collapse - True - gtk-remove - True - True - False - - - False - True - - - - - - True - Expand the whole tree in the right frame - Expand - True - gtk-add - True - True - False - - - False - True - - - - - 0 - False - False - - - - - - 1 - True - True - 0 - - - - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - False - False - False - - - - - True - False - - - - - - True - True - 0 - - - - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - True - False - False - False - - - - - True - False - - - - - - True - GTK_POLICY_NEVER - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - False - False - True - GTK_JUSTIFY_LEFT - GTK_WRAP_WORD - True - 0 - 0 - 0 - 0 - 0 - 0 - - - - - True - True - - - - - True - True - - - - - 0 - True - True - - - - - - - diff --git a/scripts/kconfig/gconf.ui b/scripts/kconfig/gconf.ui new file mode 100644 index 000000000000..cd714e64cff1 --- /dev/null +++ b/scripts/kconfig/gconf.ui @@ -0,0 +1,536 @@ + + + + + + True + Gtk Kernel Configurator + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + 640 + 480 + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + + + + True + False + 0 + + + + True + + + + True + _File + True + + + + + + + True + Load a config file + _Load + True + + + + + + + True + Save the config in .config + _Save + True + + + + + + + True + Save the config in a file + Save _as + True + + + + + + True + + + + + + True + _Quit + True + + + + + + + + + + + True + _Options + True + + + + + + + True + Show name + Show _name + True + False + + + + + + True + Show range (Y/M/N) + Show _range + True + False + + + + + + True + Show value of the option + Show _data + True + False + + + + + + True + + + + + + True + Show normal options + Show normal options + True + True + + + + + + True + Show all options + Show all _options + True + False + set_option_mode1 + + + + + + True + Show all options with prompts + Show all prompt options + True + False + set_option_mode1 + + + + + + + + + + + True + _Help + True + + + + + + + True + _Introduction + True + + + + + + + True + _About + True + + + + + + + True + _License + True + + + + + + + + + 0 + False + False + + + + + + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_BOTH + True + + + + True + Goes up one level (single view) + Back + True + gtk-undo + True + True + False + + + False + True + + + + + + True + True + True + False + + + + True + + + + + False + False + + + + + + True + Load a config file + Load + True + gtk-open + True + True + False + + + False + True + + + + + + True + Save a config file + Save + True + gtk-save + True + True + False + + + False + True + + + + + + True + True + True + False + + + + True + + + + + False + False + + + + + + True + Single view + Single + True + gtk-missing-image + True + True + False + + + False + True + + + + + + True + Split view + Split + True + gtk-missing-image + True + True + False + + + False + True + + + + + + True + Full view + Full + True + gtk-missing-image + True + True + False + + + False + True + + + + + + True + True + True + False + + + + True + + + + + False + False + + + + + + True + Collapse the whole tree in the right frame + Collapse + True + gtk-remove + True + True + False + + + False + True + + + + + + True + Expand the whole tree in the right frame + Expand + True + gtk-add + True + True + False + + + False + True + + + + + 0 + False + False + + + + + + 1 + True + True + 0 + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + False + False + False + + + + + True + False + + + + + + True + True + 0 + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + True + False + False + False + + + + + True + False + + + + + + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + False + False + True + GTK_JUSTIFY_LEFT + GTK_WRAP_WORD + True + 0 + 0 + 0 + 0 + 0 + 0 + + + + + True + True + + + + + True + True + + + + + 0 + True + True + + + + + + + -- cgit v1.2.3 From 9755d167bf51fad7091bd990f8d57006d6a60669 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:31 +0900 Subject: kconfig: gconf: migrate to GTK 3 This commit switches from GTK 2.x to GTK 3, applying the following necessary changes: - Do not include individual headers - GtkObject is gone - Convert Glade to GtkBuilder Link: https://docs.gtk.org/gtk3/migrating-2to3.html Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf-cfg.sh | 11 +-- scripts/kconfig/gconf.c | 70 ++++++++------- scripts/kconfig/gconf.ui | 200 +++++++++++++++++++++---------------------- 3 files changed, 135 insertions(+), 146 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf-cfg.sh b/scripts/kconfig/gconf-cfg.sh index fc954c0538fa..856c692f480c 100755 --- a/scripts/kconfig/gconf-cfg.sh +++ b/scripts/kconfig/gconf-cfg.sh @@ -6,7 +6,7 @@ set -eu cflags=$1 libs=$2 -PKG="gtk+-2.0 gmodule-2.0 libglade-2.0" +PKG=gtk+-3.0 if [ -z "$(command -v ${HOSTPKG_CONFIG})" ]; then echo >&2 "*" @@ -18,18 +18,11 @@ fi if ! ${HOSTPKG_CONFIG} --exists $PKG; then echo >&2 "*" echo >&2 "* Unable to find the GTK+ installation. Please make sure that" - echo >&2 "* the GTK+ 2.0 development package is correctly installed." + echo >&2 "* the GTK 3 development package is correctly installed." echo >&2 "* You need $PKG" echo >&2 "*" exit 1 fi -if ! ${HOSTPKG_CONFIG} --atleast-version=2.0.0 gtk+-2.0; then - echo >&2 "*" - echo >&2 "* GTK+ is present but version >= 2.0.0 is required." - echo >&2 "*" - exit 1 -fi - ${HOSTPKG_CONFIG} --cflags ${PKG} > ${cflags} ${HOSTPKG_CONFIG} --libs ${PKG} > ${libs} diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 8006cc547180..313250d4fc53 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -7,10 +7,7 @@ #include "lkc.h" #include "images.h" -#include #include -#include -#include #include #include @@ -601,7 +598,7 @@ static void on_expand_clicked(GtkButton *button, gpointer user_data) /* Main Windows Callbacks */ -static void on_window1_destroy(GtkObject *object, gpointer user_data) +static void on_window1_destroy(GtkWidget *widget, gpointer user_data) { gtk_main_quit(); } @@ -1001,15 +998,15 @@ static void replace_button_icon(GtkWidget *widget, const char * const xpm[]) static void init_main_window(const gchar *glade_file) { - GladeXML *xml; + GtkBuilder *builder; GtkWidget *widget; GtkTextBuffer *txtbuf; - xml = glade_xml_new(glade_file, "window1", NULL); - if (!xml) + builder = gtk_builder_new_from_file(glade_file); + if (!builder) g_error("GUI loading failed !\n"); - main_wnd = glade_xml_get_widget(xml, "window1"); + main_wnd = GTK_WIDGET(gtk_builder_get_object(builder, "window1")); g_signal_connect(main_wnd, "destroy", G_CALLBACK(on_window1_destroy), NULL); g_signal_connect(main_wnd, "configure-event", @@ -1017,9 +1014,9 @@ static void init_main_window(const gchar *glade_file) g_signal_connect(main_wnd, "delete_event", G_CALLBACK(on_window1_delete_event), NULL); - hpaned = glade_xml_get_widget(xml, "hpaned1"); - vpaned = glade_xml_get_widget(xml, "vpaned1"); - tree1_w = glade_xml_get_widget(xml, "treeview1"); + hpaned = GTK_WIDGET(gtk_builder_get_object(builder, "hpaned1")); + vpaned = GTK_WIDGET(gtk_builder_get_object(builder, "vpaned1")); + tree1_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview1")); g_signal_connect(tree1_w, "cursor_changed", G_CALLBACK(on_treeview2_cursor_changed), NULL); g_signal_connect(tree1_w, "button_press_event", @@ -1027,7 +1024,7 @@ static void init_main_window(const gchar *glade_file) g_signal_connect(tree1_w, "key_press_event", G_CALLBACK(on_treeview2_key_press_event), NULL); - tree2_w = glade_xml_get_widget(xml, "treeview2"); + tree2_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview2")); g_signal_connect(tree2_w, "cursor_changed", G_CALLBACK(on_treeview2_cursor_changed), NULL); g_signal_connect(tree2_w, "button_press_event", @@ -1035,101 +1032,101 @@ static void init_main_window(const gchar *glade_file) g_signal_connect(tree2_w, "key_press_event", G_CALLBACK(on_treeview2_key_press_event), NULL); - text_w = glade_xml_get_widget(xml, "textview3"); + text_w = GTK_WIDGET(gtk_builder_get_object(builder, "textview3")); /* menubar */ - widget = glade_xml_get_widget(xml, "load1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "load1")); g_signal_connect(widget, "activate", G_CALLBACK(on_load1_activate), NULL); - save_menu_item = glade_xml_get_widget(xml, "save1"); + save_menu_item = GTK_WIDGET(gtk_builder_get_object(builder, "save1")); g_signal_connect(save_menu_item, "activate", G_CALLBACK(on_save_activate), NULL); - widget = glade_xml_get_widget(xml, "save_as1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "save_as1")); g_signal_connect(widget, "activate", G_CALLBACK(on_save_as1_activate), NULL); - widget = glade_xml_get_widget(xml, "quit1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "quit1")); g_signal_connect(widget, "activate", G_CALLBACK(on_quit1_activate), NULL); - widget = glade_xml_get_widget(xml, "show_name1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_name1")); g_signal_connect(widget, "activate", G_CALLBACK(on_show_name1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_name); - widget = glade_xml_get_widget(xml, "show_range1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_range1")); g_signal_connect(widget, "activate", G_CALLBACK(on_show_range1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_range); - widget = glade_xml_get_widget(xml, "show_data1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_data1")); g_signal_connect(widget, "activate", G_CALLBACK(on_show_data1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_value); - widget = glade_xml_get_widget(xml, "set_option_mode1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode1")); g_signal_connect(widget, "activate", G_CALLBACK(on_set_option_mode1_activate), NULL); - widget = glade_xml_get_widget(xml, "set_option_mode2"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode2")); g_signal_connect(widget, "activate", G_CALLBACK(on_set_option_mode2_activate), NULL); - widget = glade_xml_get_widget(xml, "set_option_mode3"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode3")); g_signal_connect(widget, "activate", G_CALLBACK(on_set_option_mode3_activate), NULL); - widget = glade_xml_get_widget(xml, "introduction1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "introduction1")); g_signal_connect(widget, "activate", G_CALLBACK(on_introduction1_activate), NULL); - widget = glade_xml_get_widget(xml, "about1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "about1")); g_signal_connect(widget, "activate", G_CALLBACK(on_about1_activate), NULL); - widget = glade_xml_get_widget(xml, "license1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "license1")); g_signal_connect(widget, "activate", G_CALLBACK(on_license1_activate), NULL); /* toolbar */ - back_btn = glade_xml_get_widget(xml, "button1"); + back_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button1")); g_signal_connect(back_btn, "clicked", G_CALLBACK(on_back_clicked), NULL); gtk_widget_set_sensitive(back_btn, FALSE); - widget = glade_xml_get_widget(xml, "button2"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "button2")); g_signal_connect(widget, "clicked", G_CALLBACK(on_load_clicked), NULL); - save_btn = glade_xml_get_widget(xml, "button3"); + save_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button3")); g_signal_connect(save_btn, "clicked", G_CALLBACK(on_save_clicked), NULL); - single_btn = glade_xml_get_widget(xml, "button4"); + single_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button4")); g_signal_connect(single_btn, "clicked", G_CALLBACK(on_single_clicked), NULL); replace_button_icon(single_btn, xpm_single_view); - split_btn = glade_xml_get_widget(xml, "button5"); + split_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button5")); g_signal_connect(split_btn, "clicked", G_CALLBACK(on_split_clicked), NULL); replace_button_icon(split_btn, xpm_split_view); - full_btn = glade_xml_get_widget(xml, "button6"); + full_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button6")); g_signal_connect(full_btn, "clicked", G_CALLBACK(on_full_clicked), NULL); replace_button_icon(full_btn, xpm_tree_view); - widget = glade_xml_get_widget(xml, "button7"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "button7")); g_signal_connect(widget, "clicked", G_CALLBACK(on_collapse_clicked), NULL); - widget = glade_xml_get_widget(xml, "button8"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "button8")); g_signal_connect(widget, "clicked", G_CALLBACK(on_expand_clicked), NULL); @@ -1144,7 +1141,9 @@ static void init_main_window(const gchar *glade_file) gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); - gtk_widget_show(main_wnd); + gtk_widget_show_all(main_wnd); + + g_object_unref(builder); conf_set_changed_callback(conf_changed); } @@ -1322,7 +1321,6 @@ int main(int ac, char *av[]) /* GTK stuffs */ gtk_init(&ac, &av); - glade_init(); /* Determine GUI path */ env = getenv(SRCTREE); diff --git a/scripts/kconfig/gconf.ui b/scripts/kconfig/gconf.ui index cd714e64cff1..de20a9143aa8 100644 --- a/scripts/kconfig/gconf.ui +++ b/scripts/kconfig/gconf.ui @@ -1,8 +1,8 @@ - + - + True Gtk Kernel Configurator GTK_WINDOW_TOPLEVEL @@ -19,193 +19,193 @@ GDK_GRAVITY_NORTH_WEST - + True False 0 - + True - + True _File True - - + + - + True Load a config file _Load True - + - + True Save the config in .config _Save True - + - + True Save the config in a file Save _as True - + - + True - + - + True _Quit True - + - + - + - + True _Options True - - + + - + True Show name Show _name True False - + - + True Show range (Y/M/N) Show _range True False - + - + True Show value of the option Show _data True False - + - + True - + - + True Show normal options Show normal options True True - + - + True Show all options Show all _options True False set_option_mode1 - + - + True Show all options with prompts Show all prompt options True False set_option_mode1 - + - + - + - + True _Help True - - + + - + True _Introduction True - + - + True _About True - + - + True _License True - + - + - + - + 0 False @@ -214,14 +214,14 @@ - + True GTK_ORIENTATION_HORIZONTAL GTK_TOOLBAR_BOTH True - + True Goes up one level (single view) Back @@ -230,7 +230,7 @@ True True False - + False True @@ -238,18 +238,18 @@ - + True True True False - + True - + - + False False @@ -257,7 +257,7 @@ - + True Load a config file Load @@ -266,7 +266,7 @@ True True False - + False True @@ -274,7 +274,7 @@ - + True Save a config file Save @@ -283,7 +283,7 @@ True True False - + False True @@ -291,18 +291,18 @@ - + True True True False - + True - + - + False False @@ -310,7 +310,7 @@ - + True Single view Single @@ -319,7 +319,7 @@ True True False - + False True @@ -327,7 +327,7 @@ - + True Split view Split @@ -336,7 +336,7 @@ True True False - + False True @@ -344,7 +344,7 @@ - + True Full view Full @@ -353,7 +353,7 @@ True True False - + False True @@ -361,18 +361,18 @@ - + True True True False - + True - + - + False False @@ -380,7 +380,7 @@ - + True Collapse the whole tree in the right frame Collapse @@ -389,7 +389,7 @@ True True False - + False True @@ -397,7 +397,7 @@ - + True Expand the whole tree in the right frame Expand @@ -406,13 +406,13 @@ True True False - + False True - + 0 False @@ -421,14 +421,13 @@ - + 1 True True - 0 - + True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC @@ -436,16 +435,16 @@ GTK_CORNER_TOP_LEFT - + True True True False False False - + - + True False @@ -453,13 +452,12 @@ - + True True - 0 - + True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC @@ -467,7 +465,7 @@ GTK_CORNER_TOP_LEFT - + True True True @@ -475,9 +473,9 @@ False False False - + - + True False @@ -485,7 +483,7 @@ - + True GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC @@ -493,7 +491,7 @@ GTK_CORNER_TOP_LEFT - + True True False @@ -508,29 +506,29 @@ 0 0 0 - + - + True True - + True True - + 0 True True - + - + - + -- cgit v1.2.3 From df889fdbb8d4243504eba94e1c3a809a4996a219 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:32 +0900 Subject: kconfig: gconf: replace GtkVbox with GtkBox GtkVBox is deprecated with GTK 3.2. [1] Use GtkBox instead. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.2.0/gtk/gtkvbox.c#L47 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.ui | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.ui b/scripts/kconfig/gconf.ui index de20a9143aa8..806ea3d1bac4 100644 --- a/scripts/kconfig/gconf.ui +++ b/scripts/kconfig/gconf.ui @@ -19,7 +19,8 @@ GDK_GRAVITY_NORTH_WEST - + + vertical True False 0 -- cgit v1.2.3 From d6f0b652d9b54af5a9cf3e926ecfba81c28e1fc4 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:33 +0900 Subject: kconfig: gconf: replace GdkColor with GdkRGBA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GdkColor is deprecated with GTK 3.14. [1] Use GdkRGBA instead. This fixes warnings such as: scripts/kconfig/gconf.c: In function ‘set_node’: scripts/kconfig/gconf.c:138:9: warning: ‘gdk_color_parse’ is deprecated: Use 'gdk_rgba_parse' instead [-Wdeprecated-declarations] 138 | gdk_color_parse(menu_is_visible(menu) ? "Black" : "DarkGray", &color); | ^~~~~~~~~~~~~~~ [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.14.0/gdk/deprecated/gdkcolor.h#L52 Signed-off-by: Masahiro Yamada Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 313250d4fc53..f4c2b07e0207 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -176,7 +176,7 @@ static void set_node(GtkTreeStore *tree, GtkTreeIter *node, struct menu *menu) const gchar *_mod = ""; const gchar *_yes = ""; const gchar *value = ""; - GdkColor color; + GdkRGBA color; gboolean editable = FALSE; gboolean btnvis = FALSE; @@ -186,7 +186,7 @@ static void set_node(GtkTreeStore *tree, GtkTreeIter *node, struct menu *menu) menu->type == M_COMMENT ? "***" : "", sym && !sym_has_value(sym) ? "(NEW)" : ""); - gdk_color_parse(menu_is_visible(menu) ? "Black" : "DarkGray", &color); + gdk_rgba_parse(&color, menu_is_visible(menu) ? "Black" : "DarkGray"); if (!sym) goto set; @@ -1174,7 +1174,7 @@ static void init_left_tree(void) G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_POINTER, GDK_TYPE_RGBA, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, @@ -1205,7 +1205,7 @@ static void init_left_tree(void) gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer, "text", COL_OPTION, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); sel = gtk_tree_view_get_selection(view); @@ -1225,7 +1225,7 @@ static void init_right_tree(void) G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_POINTER, GDK_TYPE_RGBA, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, @@ -1263,32 +1263,32 @@ static void init_right_tree(void) gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer, "text", COL_OPTION, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, "Name", renderer, "text", COL_NAME, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, "N", renderer, "text", COL_NO, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, "M", renderer, "text", COL_MOD, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, "Y", renderer, "text", COL_YES, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, @@ -1296,7 +1296,7 @@ static void init_right_tree(void) "text", COL_VALUE, "editable", COL_EDIT, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(renderer_edited), tree2_w); -- cgit v1.2.3 From bfa7375c10dfabf6b3289041c12d698861277d90 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:34 +0900 Subject: kconfig: gconf: replace GtkHPaned and GtkVPaned with GtkPaned GtkHPaned and GtkVPaned are deprecated with GTK 3.2. [1] [2] Use GtkPaned instead. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.2.0/gtk/gtkhpaned.c#L44 [2]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.2.0/gtk/gtkvpaned.c#L44 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.ui | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.ui b/scripts/kconfig/gconf.ui index 806ea3d1bac4..c37807e8b782 100644 --- a/scripts/kconfig/gconf.ui +++ b/scripts/kconfig/gconf.ui @@ -422,7 +422,7 @@ - + 1 True True @@ -453,7 +453,8 @@ - + + vertical True True -- cgit v1.2.3 From 65056488e8bfaf6626cd2bba9fa847b264d9fefc Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:35 +0900 Subject: kconfig: gconf: show GTK version in About dialog Likewise xconfig, it is useful to display the GTK version in the About dialog. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index f4c2b07e0207..7725d2c9d92a 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -525,7 +525,11 @@ static void on_about1_activate(GtkMenuItem *menuitem, gpointer user_data) dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, - GTK_BUTTONS_CLOSE, "%s", about_text); + GTK_BUTTONS_CLOSE, "%s\nGTK version: %d.%d.%d", + about_text, + gtk_get_major_version(), + gtk_get_minor_version(), + gtk_get_micro_version()); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); } -- cgit v1.2.3 From 263e70bc42862af18dce43393ef14277827a0c7f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:48:31 +0900 Subject: kconfig: add a function to dump all menu entries in a tree-like format This is useful for debugging purposes. menu_finalize() re-parents menu entries, and this function can be used to dump the final structure of the menu tree. Signed-off-by: Masahiro Yamada --- scripts/kconfig/lkc.h | 1 + scripts/kconfig/menu.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) (limited to 'scripts') diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 37b606c74bff..56548efc14d7 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -102,6 +102,7 @@ struct menu *menu_get_menu_or_parent_menu(struct menu *menu); int get_jump_key_char(void); struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); void menu_get_ext_help(struct menu *menu, struct gstr *help); +void menu_dump(void); /* symbol.c */ void sym_clear_all_valid(void); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index a5e5b4fdcd93..0f1a6513987c 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -788,3 +788,77 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help) if (sym) get_symbol_str(help, sym, NULL); } + +/** + * menu_dump - dump all menu entries in a tree-like format + */ +void menu_dump(void) +{ + struct menu *menu = &rootmenu; + unsigned long long bits = 0; + int indent = 0; + + while (menu) { + + for (int i = indent - 1; i >= 0; i--) { + if (bits & (1ULL << i)) { + if (i > 0) + printf("| "); + else + printf("|-- "); + } else { + if (i > 0) + printf(" "); + else + printf("`-- "); + } + } + + switch (menu->type) { + case M_CHOICE: + printf("choice \"%s\"\n", menu->prompt->text); + break; + case M_COMMENT: + printf("comment \"%s\"\n", menu->prompt->text); + break; + case M_IF: + printf("if\n"); + break; + case M_MENU: + printf("menu \"%s\"", menu->prompt->text); + if (!menu->sym) { + printf("\n"); + break; + } + printf(" + "); + /* fallthrough */ + case M_NORMAL: + printf("symbol %s\n", menu->sym->name); + break; + } + if (menu->list) { + bits <<= 1; + menu = menu->list; + if (menu->next) + bits |= 1; + else + bits &= ~1; + indent++; + continue; + } + + while (menu && !menu->next) { + menu = menu->parent; + bits >>= 1; + indent--; + } + + if (menu) { + menu = menu->next; + if (menu->next) + bits |= 1; + else + bits &= ~1; + } + } +} -- cgit v1.2.3 From 721bfe583c52ba1ea74b3736a31a9dcfe6dd6d95 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:48:56 +0900 Subject: kconfig: qconf: fix ConfigList::updateListAllforAll() ConfigList::updateListForAll() and ConfigList::updateListAllforAll() are identical. Commit f9b918fae678 ("kconfig: qconf: move ConfigView::updateList(All) to ConfigList class") was a misconversion. Fixes: f9b918fae678 ("kconfig: qconf: move ConfigView::updateList(All) to ConfigList class") Signed-off-by: Masahiro Yamada --- scripts/kconfig/qconf.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index dc056b0a8fde..a7c98bbbd8ac 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -486,7 +486,7 @@ void ConfigList::updateListAllForAll() while (it.hasNext()) { ConfigList *list = it.next(); - list->updateList(); + list->updateListAll(); } } -- cgit v1.2.3 From 87433e3e06a6b6a78a541b6ac39000f41779a882 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:50:35 +0900 Subject: kconfig: qconf: confine {begin,end}Group to constructor and destructor Call beginGroup() in the the constructor and endGroup() in the destructor. This looks cleaner. Signed-off-by: Masahiro Yamada --- scripts/kconfig/qconf.cc | 8 ++++++-- scripts/kconfig/qconf.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index a7c98bbbd8ac..f8992db1870a 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -37,6 +37,12 @@ QAction *ConfigMainWindow::saveAction; ConfigSettings::ConfigSettings() : QSettings("kernel.org", "qconf") { + beginGroup("/kconfig/qconf"); +} + +ConfigSettings::~ConfigSettings() +{ + endGroup(); } /** @@ -1829,7 +1835,6 @@ int main(int ac, char** av) configApp = new QApplication(ac, av); configSettings = new ConfigSettings(); - configSettings->beginGroup("/kconfig/qconf"); v = new ConfigMainWindow(); //zconfdump(stdout); @@ -1837,7 +1842,6 @@ int main(int ac, char** av) v->show(); configApp->exec(); - configSettings->endGroup(); delete configSettings; delete v; delete configApp; diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h index 62ab3286d04f..ab4e51f12914 100644 --- a/scripts/kconfig/qconf.h +++ b/scripts/kconfig/qconf.h @@ -24,6 +24,7 @@ class ConfigMainWindow; class ConfigSettings : public QSettings { public: ConfigSettings(); + ~ConfigSettings(void); QList readSizes(const QString& key, bool *ok); bool writeSizes(const QString& key, const QList& value); }; -- cgit v1.2.3 From e06aa69de21b6de2ef83f559768a4005114f5661 Mon Sep 17 00:00:00 2001 From: Giuliano Procida Date: Tue, 1 Jul 2025 16:19:10 +0100 Subject: gendwarfksyms: use preferred form of sizeof for allocation The preferred form is to use the variable being allocated to, rather than explicitly supplying a type name which might become stale. Also do this for memset. Suggested-by: Masahiro Yamada Signed-off-by: Giuliano Procida Reviewed-by: Sami Tolvanen Signed-off-by: Masahiro Yamada --- scripts/gendwarfksyms/cache.c | 2 +- scripts/gendwarfksyms/die.c | 4 ++-- scripts/gendwarfksyms/dwarf.c | 2 +- scripts/gendwarfksyms/kabi.c | 2 +- scripts/gendwarfksyms/symbols.c | 2 +- scripts/gendwarfksyms/types.c | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/gendwarfksyms/cache.c b/scripts/gendwarfksyms/cache.c index c9c19b86a686..1c640db93db3 100644 --- a/scripts/gendwarfksyms/cache.c +++ b/scripts/gendwarfksyms/cache.c @@ -15,7 +15,7 @@ void cache_set(struct cache *cache, unsigned long key, int value) { struct cache_item *ci; - ci = xmalloc(sizeof(struct cache_item)); + ci = xmalloc(sizeof(*ci)); ci->key = key; ci->value = value; hash_add(cache->cache, &ci->hash, hash_32(key)); diff --git a/scripts/gendwarfksyms/die.c b/scripts/gendwarfksyms/die.c index 6183bbbe7b54..052f7a3f975a 100644 --- a/scripts/gendwarfksyms/die.c +++ b/scripts/gendwarfksyms/die.c @@ -33,7 +33,7 @@ static struct die *create_die(Dwarf_Die *die, enum die_state state) { struct die *cd; - cd = xmalloc(sizeof(struct die)); + cd = xmalloc(sizeof(*cd)); init_die(cd); cd->addr = (uintptr_t)die->addr; @@ -123,7 +123,7 @@ static struct die_fragment *append_item(struct die *cd) { struct die_fragment *df; - df = xmalloc(sizeof(struct die_fragment)); + df = xmalloc(sizeof(*df)); df->type = FRAGMENT_EMPTY; list_add_tail(&df->list, &cd->fragments); return df; diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c index 13ea7bf1ae7d..3538a7d9cb07 100644 --- a/scripts/gendwarfksyms/dwarf.c +++ b/scripts/gendwarfksyms/dwarf.c @@ -634,7 +634,7 @@ static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder, * Note that the user of this feature is responsible for ensuring * that the structure actually remains ABI compatible. */ - memset(&state.kabi, 0, sizeof(struct kabi_state)); + memset(&state.kabi, 0, sizeof(state.kabi)); res = checkp(process_die_container(&state, NULL, die, check_union_member_kabi_status, diff --git a/scripts/gendwarfksyms/kabi.c b/scripts/gendwarfksyms/kabi.c index b3ade713778f..e3c2a3ccf51a 100644 --- a/scripts/gendwarfksyms/kabi.c +++ b/scripts/gendwarfksyms/kabi.c @@ -228,7 +228,7 @@ void kabi_read_rules(int fd) if (type == KABI_RULE_TYPE_UNKNOWN) error("unsupported kABI rule type: '%s'", field); - rule = xmalloc(sizeof(struct rule)); + rule = xmalloc(sizeof(*rule)); rule->type = type; rule->target = xstrdup(get_rule_field(&rule_str, &left)); diff --git a/scripts/gendwarfksyms/symbols.c b/scripts/gendwarfksyms/symbols.c index 327f87389c34..35ed594f0749 100644 --- a/scripts/gendwarfksyms/symbols.c +++ b/scripts/gendwarfksyms/symbols.c @@ -146,7 +146,7 @@ void symbol_read_exports(FILE *file) continue; } - sym = xcalloc(1, sizeof(struct symbol)); + sym = xcalloc(1, sizeof(*sym)); sym->name = name; sym->addr.section = SHN_UNDEF; sym->state = SYMBOL_UNPROCESSED; diff --git a/scripts/gendwarfksyms/types.c b/scripts/gendwarfksyms/types.c index 7bd459ea6c59..5344c7b9a9ce 100644 --- a/scripts/gendwarfksyms/types.c +++ b/scripts/gendwarfksyms/types.c @@ -43,7 +43,7 @@ static int type_list_append(struct list_head *list, const char *s, void *owned) if (!s) return 0; - entry = xmalloc(sizeof(struct type_list_entry)); + entry = xmalloc(sizeof(*entry)); entry->str = s; entry->owned = owned; list_add_tail(&entry->list, list); @@ -120,7 +120,7 @@ static struct type_expansion *type_map_add(const char *name, struct type_expansion *e; if (__type_map_get(name, &e)) { - e = xmalloc(sizeof(struct type_expansion)); + e = xmalloc(sizeof(*e)); type_expansion_init(e); e->name = xstrdup(name); -- cgit v1.2.3 From d8f26717c901b7ec88c3151988fe70ecaed990b8 Mon Sep 17 00:00:00 2001 From: Giuliano Procida Date: Tue, 1 Jul 2025 16:19:11 +0100 Subject: gendwarfksyms: order -T symtypes output by name When writing symtypes information, we iterate through the entire hash table containing type expansions. The key order varies unpredictably as new entries are added, making it harder to compare symtypes between builds. Resolve this by sorting the type expansions by name before output. Signed-off-by: Giuliano Procida Acked-by: Greg Kroah-Hartman Reviewed-by: Sami Tolvanen Signed-off-by: Masahiro Yamada --- scripts/gendwarfksyms/types.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/gendwarfksyms/types.c b/scripts/gendwarfksyms/types.c index 5344c7b9a9ce..9c3b053bf061 100644 --- a/scripts/gendwarfksyms/types.c +++ b/scripts/gendwarfksyms/types.c @@ -6,6 +6,8 @@ #define _GNU_SOURCE #include #include +#include +#include #include #include "gendwarfksyms.h" @@ -179,20 +181,41 @@ static int type_map_get(const char *name, struct type_expansion **res) return -1; } +static int cmp_expansion_name(const void *p1, const void *p2) +{ + struct type_expansion *const *e1 = p1; + struct type_expansion *const *e2 = p2; + + return strcmp((*e1)->name, (*e2)->name); +} + static void type_map_write(FILE *file) { struct type_expansion *e; struct hlist_node *tmp; + struct type_expansion **es; + size_t count = 0; + size_t i = 0; if (!file) return; - hash_for_each_safe(type_map, e, tmp, hash) { - checkp(fputs(e->name, file)); + hash_for_each_safe(type_map, e, tmp, hash) + ++count; + es = xmalloc(count * sizeof(*es)); + hash_for_each_safe(type_map, e, tmp, hash) + es[i++] = e; + + qsort(es, count, sizeof(*es), cmp_expansion_name); + + for (i = 0; i < count; ++i) { + checkp(fputs(es[i]->name, file)); checkp(fputs(" ", file)); - type_list_write(&e->expanded, file); + type_list_write(&es[i]->expanded, file); checkp(fputs("\n", file)); } + + free(es); } static void type_map_free(void) -- cgit v1.2.3 From b9f75396ec107628cc5f52fb6e055c1c9dc68401 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Mon, 14 Jul 2025 17:29:23 -0500 Subject: scripts: add zboot support to extract-vmlinux Zboot compressed kernel images are used for arm64 kernels on various distros. extract-vmlinux fails with those kernels because the wrapped image is another PE. While this could be a bit confusing, the tools primary purpose of unwrapping and decompressing the contained kernel image makes it the obvious place for this functionality. Add a 'file' check in check_vmlinux() that detects a contained PE image before trying readelf. Recent (FILES_39, Jun/2020) file implementations output something like: "Linux kernel ARM64 boot executable Image, little-endian, 4K pages" Which is also a stronger statement than readelf provides so drop that part of the comment. At the same time this means that kernel images which don't appear to contain a compressed image will be returned rather than reporting an error. Which matches the behavior for existing ELF files. The extracted PE image can then be inspected, or used as would any other kernel PE. Signed-off-by: Jeremy Linton Signed-off-by: Masahiro Yamada --- scripts/extract-vmlinux | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/extract-vmlinux b/scripts/extract-vmlinux index 8995cd304e6e..189956b5a5c8 100755 --- a/scripts/extract-vmlinux +++ b/scripts/extract-vmlinux @@ -12,13 +12,12 @@ check_vmlinux() { - # Use readelf to check if it's a valid ELF - # TODO: find a better to way to check that it's really vmlinux - # and not just an elf - readelf -h $1 > /dev/null 2>&1 || return 1 - - cat $1 - exit 0 + if file "$1" | grep -q 'Linux kernel.*boot executable' || + readelf -h "$1" > /dev/null 2>&1 + then + cat "$1" + exit 0 + fi } try_decompress() -- cgit v1.2.3 From 6d4d44254e43157bb760aa16367a394c2ab299b8 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:08 +0900 Subject: kconfig: gconf: fix single view to display dependent symbols correctly In the following example, the symbol C was never displayed in Single view. Fix the recursion logic so that all symbols are shown. menu "menu" config A bool "A" config B bool "B" depends on A config C bool "C" depends on B endmenu Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 7725d2c9d92a..c67b35807e8e 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -914,9 +914,7 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, static void _display_tree(GtkTreeStore *tree, struct menu *menu, GtkTreeIter *parent) { - struct property *prop; struct menu *child; - enum prop_type ptype; GtkTreeIter iter; for (child = menu->list; child; child = child->next) { @@ -929,9 +927,6 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, if (child->type == M_IF) continue; - prop = child->prompt; - ptype = prop ? prop->type : P_UNKNOWN; - if ((view_mode == SPLIT_VIEW) && !(child->flags & MENU_ROOT) && (tree == tree1)) continue; @@ -943,16 +938,7 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, gtk_tree_store_append(tree, &iter, parent); set_node(tree, &iter, child); - if ((view_mode == SINGLE_VIEW) && (ptype == P_MENU)) - continue; -/* - if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT)) - || (view_mode == FULL_VIEW) - || (view_mode == SPLIT_VIEW))*/ - - if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) - || (view_mode == FULL_VIEW) - || (view_mode == SPLIT_VIEW)) + if (view_mode != SINGLE_VIEW || child->type != M_MENU) _display_tree(tree, child, &iter); } } -- cgit v1.2.3 From 15a5ae3b0976d1190728044920cf6337a218ae62 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:09 +0900 Subject: kconfig: gconf: Fix Back button behavior Clicking the Back button may navigate to a non-menu hierarchy level. [Example] menu "menu1" config A bool "A" default y config B bool "B" depends on A default y menu "menu2" depends on B config C bool "C" default y endmenu endmenu After being re-parented by menu_finalize(), the menu tree is structured like follows: menu "menu1" \-- A \-- B \-- menu "menu2" \-- C In Single view, visit "menu2" and click the Back button. It should go up to "menu1" and show A, B and "menu2", but instead goes up to A and show only B and "menu2". This is a bug in on_back_clicked(). Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index c67b35807e8e..d9ea71664412 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -553,12 +553,8 @@ static void on_license1_activate(GtkMenuItem *menuitem, gpointer user_data) /* toolbar handlers */ static void on_back_clicked(GtkButton *button, gpointer user_data) { - enum prop_type ptype; + browsed = menu_get_parent_menu(browsed) ?: &rootmenu; - browsed = browsed->parent; - ptype = browsed->prompt ? browsed->prompt->type : P_UNKNOWN; - if (ptype != P_MENU) - browsed = browsed->parent; recreate_tree(); if (browsed == &rootmenu) -- cgit v1.2.3 From 2bc0148f78193865065035fe19095c78c3d8129f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:10 +0900 Subject: kconfig: gconf: replace GtkImageMenuItem with GtkMenuItem GtkImageMenuItem is deprecated with GTK 3.10. [1] Use GtkMenuItem instead. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.10.0/gtk/deprecated/gtkimagemenuitem.c#L797 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.ui | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.ui b/scripts/kconfig/gconf.ui index c37807e8b782..ab4431255fa7 100644 --- a/scripts/kconfig/gconf.ui +++ b/scripts/kconfig/gconf.ui @@ -39,7 +39,7 @@ - + True Load a config file _Load @@ -49,7 +49,7 @@ - + True Save the config in .config _Save @@ -59,7 +59,7 @@ - + True Save the config in a file Save _as @@ -74,7 +74,7 @@ - + True _Quit True @@ -178,7 +178,7 @@ - + True _Introduction True @@ -187,7 +187,7 @@ - + True _About True @@ -196,7 +196,7 @@ - + True _License True -- cgit v1.2.3 From e16f08062f91570aa225bc490e0a92d63ae13769 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:11 +0900 Subject: kconfig: gconf: use hyphens in signals Using hyphens in signal names is the official convention, even though underscores also work. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index d9ea71664412..e4f89270d19f 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -997,25 +997,25 @@ static void init_main_window(const gchar *glade_file) G_CALLBACK(on_window1_destroy), NULL); g_signal_connect(main_wnd, "configure-event", G_CALLBACK(on_window1_configure), NULL); - g_signal_connect(main_wnd, "delete_event", + g_signal_connect(main_wnd, "delete-event", G_CALLBACK(on_window1_delete_event), NULL); hpaned = GTK_WIDGET(gtk_builder_get_object(builder, "hpaned1")); vpaned = GTK_WIDGET(gtk_builder_get_object(builder, "vpaned1")); tree1_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview1")); - g_signal_connect(tree1_w, "cursor_changed", + g_signal_connect(tree1_w, "cursor-changed", G_CALLBACK(on_treeview2_cursor_changed), NULL); - g_signal_connect(tree1_w, "button_press_event", + g_signal_connect(tree1_w, "button-press-event", G_CALLBACK(on_treeview1_button_press_event), NULL); - g_signal_connect(tree1_w, "key_press_event", + g_signal_connect(tree1_w, "key-press-event", G_CALLBACK(on_treeview2_key_press_event), NULL); tree2_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview2")); - g_signal_connect(tree2_w, "cursor_changed", + g_signal_connect(tree2_w, "cursor-changed", G_CALLBACK(on_treeview2_cursor_changed), NULL); - g_signal_connect(tree2_w, "button_press_event", + g_signal_connect(tree2_w, "button-press-event", G_CALLBACK(on_treeview2_button_press_event), NULL); - g_signal_connect(tree2_w, "key_press_event", + g_signal_connect(tree2_w, "key-press-event", G_CALLBACK(on_treeview2_key_press_event), NULL); text_w = GTK_WIDGET(gtk_builder_get_object(builder, "textview3")); -- cgit v1.2.3 From 5ceb15fdc629aa3030e8f8987c561d36678f9559 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:12 +0900 Subject: kconfig: gconf: remove unneeded variable in text_insert_msg The 'msg' and 'message' refer to the same pointer. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index e4f89270d19f..651140af7d13 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -90,11 +90,10 @@ static void text_insert_help(struct menu *menu) } -static void text_insert_msg(const char *title, const char *message) +static void text_insert_msg(const char *title, const char *msg) { GtkTextBuffer *buffer; GtkTextIter start, end; - const char *msg = message; buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); gtk_text_buffer_get_bounds(buffer, &start, &end); -- cgit v1.2.3 From eb549e194bf2d5c86b1b7a71fad54d610dd6c892 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:13 +0900 Subject: kconfig: gconf: refactor text_insert_help() text_insert_help() and text_insert_msg() share similar code. Refactor text_insert_help() to eliminate the code duplication. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 651140af7d13..8b164ccfa008 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -64,15 +64,10 @@ static void conf_changed(bool dirty) /* Utility Functions */ - -static void text_insert_help(struct menu *menu) +static void text_insert_msg(const char *title, const char *msg) { GtkTextBuffer *buffer; GtkTextIter start, end; - const char *prompt = menu_get_prompt(menu); - struct gstr help = str_new(); - - menu_get_ext_help(menu, &help); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); gtk_text_buffer_get_bounds(buffer, &start, &end); @@ -80,33 +75,21 @@ static void text_insert_help(struct menu *menu) gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); gtk_text_buffer_get_end_iter(buffer, &end); - gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1, + gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1, NULL); gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); gtk_text_buffer_get_end_iter(buffer, &end); - gtk_text_buffer_insert_with_tags(buffer, &end, str_get(&help), -1, tag2, + gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2, NULL); - str_free(&help); } - -static void text_insert_msg(const char *title, const char *msg) +static void text_insert_help(struct menu *menu) { - GtkTextBuffer *buffer; - GtkTextIter start, end; - - buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); - gtk_text_buffer_get_bounds(buffer, &start, &end); - gtk_text_buffer_delete(buffer, &start, &end); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); + struct gstr help = str_new(); - gtk_text_buffer_get_end_iter(buffer, &end); - gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1, - NULL); - gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); - gtk_text_buffer_get_end_iter(buffer, &end); - gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2, - NULL); + menu_get_ext_help(menu, &help); + text_insert_msg(menu_get_prompt(menu), str_get(&help)); + str_free(&help); } static void _select_menu(GtkTreeView *view, GtkTreeModel *model, -- cgit v1.2.3 From 53f433891e698e76aaf01b84b30a17a79a53535c Mon Sep 17 00:00:00 2001 From: WangYuli Date: Tue, 22 Jul 2025 15:34:31 +0800 Subject: scripts/spelling.txt: add notifer||notifier to spelling.txt This typo was not listed in scripts/spelling.txt, thus it was more difficult to detect. Add it for convenience. Link: https://lkml.kernel.org/r/02153C05ED7B49B7+20250722073431.21983-8-wangyuli@uniontech.com Signed-off-by: WangYuli Reviewed-by: Jonathan Cameron Signed-off-by: Andrew Morton --- scripts/spelling.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'scripts') diff --git a/scripts/spelling.txt b/scripts/spelling.txt index ac94fa1c2415..1e89b92c2f9a 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -1099,6 +1099,7 @@ notication||notification notications||notifications notifcations||notifications notifed||notified +notifer||notifier notity||notify notfify||notify nubmer||number -- cgit v1.2.3 From 1918f983687aa73bf0e5bc73431898994fce35a8 Mon Sep 17 00:00:00 2001 From: Suchit Karunakaran Date: Sun, 27 Jul 2025 01:13:07 +0530 Subject: kconfig: lxdialog: replace strcpy with snprintf in print_autowrap strcpy() does not perform bounds checking and can lead to buffer overflows if the source string exceeds the destination buffer size. In print_autowrap(), replace strcpy() with snprintf() to safely copy the prompt string into the fixed-size tempstr buffer. Signed-off-by: Suchit Karunakaran Signed-off-by: Masahiro Yamada --- scripts/kconfig/lxdialog/util.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/lxdialog/util.c b/scripts/kconfig/lxdialog/util.c index 964139c87fcb..b34000beb294 100644 --- a/scripts/kconfig/lxdialog/util.c +++ b/scripts/kconfig/lxdialog/util.c @@ -345,8 +345,7 @@ void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x) int prompt_len, room, wlen; char tempstr[MAX_LEN + 1], *word, *sp, *sp2, *newline_separator = 0; - strcpy(tempstr, prompt); - + snprintf(tempstr, sizeof(tempstr), "%s", prompt); prompt_len = strlen(tempstr); if (prompt_len <= width - x * 2) { /* If prompt is short */ -- cgit v1.2.3 From 5ac726653a1029a2eccba93bbe59e01fc9725828 Mon Sep 17 00:00:00 2001 From: Suchit Karunakaran Date: Sun, 27 Jul 2025 22:14:33 +0530 Subject: kconfig: lxdialog: replace strcpy() with strncpy() in inputbox.c strcpy() performs no bounds checking and can lead to buffer overflows if the input string exceeds the destination buffer size. This patch replaces it with strncpy(), and null terminates the input string. Signed-off-by: Suchit Karunakaran Reviewed-by: Nicolas Schier Signed-off-by: Masahiro Yamada --- scripts/kconfig/lxdialog/inputbox.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/lxdialog/inputbox.c b/scripts/kconfig/lxdialog/inputbox.c index 3c6e24b20f5b..5e4a131724f2 100644 --- a/scripts/kconfig/lxdialog/inputbox.c +++ b/scripts/kconfig/lxdialog/inputbox.c @@ -39,8 +39,10 @@ int dialog_inputbox(const char *title, const char *prompt, int height, int width if (!init) instr[0] = '\0'; - else - strcpy(instr, init); + else { + strncpy(instr, init, sizeof(dialog_input_result) - 1); + instr[sizeof(dialog_input_result) - 1] = '\0'; + } do_resize: if (getmaxy(stdscr) <= (height - INPUTBOX_HEIGHT_MIN)) -- cgit v1.2.3 From c072d2b49507420edd57ba3d87690ab81ef5273a Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Fri, 25 Jul 2025 15:06:26 -0500 Subject: scripts/dtc: Update to upstream version v1.7.2-35-g52f07dcca47c This adds the following commits from upstream: 52f07dcca47c dtc: Add informative error for stray identifier 9cabae6b0351 checks: Fix detection of 'i2c-bus' node 605dc044c3fe New helper to add markers 7da5d106c740 fdtput: Fix documentation about existing nodes 53c63dd421d7 dtdiff: Use input format dtb for dtbo files 84d9dd2fcbc8 dtc: Add data_insert_data function 97011d1f4e98 meson: use override_find_program/override_dependency b841391bbd08 srcpos: Define srcpos_free e0b7749c26a9 Add alloc_marker ecb21febfdd3 meson: port python bindings to build natively via meson and meson-python 7ebfcac8520e Makefile: deprecate in favor of Meson f4c53f4ebf78 Use __ASSEMBLER__ instead of __ASSEMBLY__ 205fbef17b7b Fix some typos da85f91931e5 Remove duplicated words in documentation and comments dd1b3e532d22 meson: support building libfdt without static library 1ccd232709d4 meson: don't build test programs by default ce1d8588880a tests: When building .so from -O asm output mark as non-executable stack 915daadbb62d Start with empty __local_fixups__ and __fixups__ nodes 4ea851f5a44d Let get_subnode() not return deleted nodes 175d2a564c47 Use build_root_node() instead of open-coding it 18f4f305fdd7 build: fix -Dtools=false build 267efc7d4694 checks: Warn about missing #address-cells for interrupt parents 755db115355b libfdt: Add fdt_setprop_namelen_string() bdca8612009e libfdt: Add fdt_setprop_namelen() 0f69cedc08fc libfdt_internal: fdt_find_string_len_() 56b2b30c5bd0 libfdt: add fdt_get_property_namelen_w() 1e8c5f60e127 Add clang-format config 6f183c7d9246 checks: Relax avoid_unnecessary_addr_size check to allow child ranges properties 66c7d0e6f4f3 tests/sw_tree1.c: fix unitialized saveptr 9a969f3b70b0 pylibfdt/libfdt.i: fix backwards compatibility of return values 4292b072a23a .github/workflows: update ubuntu runner to supported version 1c745a9bd169 libfdt: Remove fdt parameter from overlay_fixup_one_phandle b3bbee6b1242 libfdt: Move the SBOM authors section d1656730abfb Add a SBOM file in CycloneDX format b75515af4576 libfdt: Remove extra semi-colons outside functions 2d10aa2afe35 Bump version to v1.7.2 48795c82bdb6 pylibfdt: Don't emit warnings from swig generate C code 838f11e830e3 fdtoverlay: provide better error message for missing `/__symbols__` d1e2384185c5 pylibfdt/libfdt.i: Use SWIG_AppendOutput 18aa49a9f68d Escape spaces in depfile with backslashes. f9968fa06921 libfdt.h: whitespace consistency fixups 9b5f65fb3d8d libfdt.h: typo and consistency fixes 99031e3a4a6e Bump version to v1.7.1 3d5e376925fd setup: Move setting of srcdir down to the bottom e277553b9880 setup: Collect top-level code together 7e5a88984081 setup: Move version and full_description into a function 78b6a85c113b Tidy up some pylint warnings 3501d373f0a2 Require Python 3 The added include of string.h in libfdt_internal.h breaks the kernel overriding libfdt_env.h with its own string functions, so it is dropped. An upstream fix is pending. Signed-off-by: Rob Herring (Arm) --- scripts/Makefile.dtbs | 1 + scripts/dtc/checks.c | 23 +++-- scripts/dtc/data.c | 47 ++++++++- scripts/dtc/dtc-lexer.l | 15 +++ scripts/dtc/dtc.c | 6 +- scripts/dtc/dtc.h | 5 +- scripts/dtc/fdtoverlay.c | 8 ++ scripts/dtc/flattree.c | 2 +- scripts/dtc/libfdt/fdt.c | 8 +- scripts/dtc/libfdt/fdt.h | 4 +- scripts/dtc/libfdt/fdt_overlay.c | 8 +- scripts/dtc/libfdt/fdt_rw.c | 41 ++++---- scripts/dtc/libfdt/libfdt.h | 179 ++++++++++++++++++++++++++++------- scripts/dtc/libfdt/libfdt_internal.h | 14 ++- scripts/dtc/livetree.c | 25 +++-- scripts/dtc/srcpos.c | 17 +++- scripts/dtc/srcpos.h | 1 + scripts/dtc/treesource.c | 52 +++++++--- scripts/dtc/util.c | 16 ++++ scripts/dtc/util.h | 5 + scripts/dtc/version_gen.h | 2 +- 21 files changed, 372 insertions(+), 107 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.dtbs b/scripts/Makefile.dtbs index 8d56c0815f33..2d321b813600 100644 --- a/scripts/Makefile.dtbs +++ b/scripts/Makefile.dtbs @@ -97,6 +97,7 @@ DTC_FLAGS += -Wno-unit_address_vs_reg \ -Wno-avoid_unnecessary_addr_size \ -Wno-alias_paths \ -Wno-graph_child_address \ + -Wno-interrupt_map \ -Wno-simple_bus_reg else DTC_FLAGS += -Wunique_unit_address_if_enabled diff --git a/scripts/dtc/checks.c b/scripts/dtc/checks.c index 6e06aeab5503..7e3fed5005b3 100644 --- a/scripts/dtc/checks.c +++ b/scripts/dtc/checks.c @@ -1024,7 +1024,7 @@ static void check_i2c_bus_bridge(struct check *c, struct dt_info *dti, struct no } else if (strprefixeq(node->name, node->basenamelen, "i2c")) { struct node *child; for_each_child(node, child) { - if (strprefixeq(child->name, node->basenamelen, "i2c-bus")) + if (strprefixeq(child->name, child->basenamelen, "i2c-bus")) return; } node->bus = &i2c_bus; @@ -1217,9 +1217,7 @@ WARNING(avoid_default_addr_size, check_avoid_default_addr_size, NULL, static void check_avoid_unnecessary_addr_size(struct check *c, struct dt_info *dti, struct node *node) { - struct property *prop; struct node *child; - bool has_reg = false; if (!node->parent || node->addr_cells < 0 || node->size_cells < 0) return; @@ -1228,13 +1226,18 @@ static void check_avoid_unnecessary_addr_size(struct check *c, struct dt_info *d return; for_each_child(node, child) { - prop = get_property(child, "reg"); - if (prop) - has_reg = true; + /* + * Even if the child devices' address space is not mapped into + * the parent bus (no 'ranges' property on node), children can + * still have registers on a local bus, or map local addresses + * to another subordinate address space. The properties on the + * child nodes then make #address-cells/#size-cells necessary: + */ + if (get_property(child, "reg") || get_property(child, "ranges")) + return; } - if (!has_reg) - FAIL(c, dti, node, "unnecessary #address-cells/#size-cells without \"ranges\", \"dma-ranges\" or child \"reg\" property"); + FAIL(c, dti, node, "unnecessary #address-cells/#size-cells without \"ranges\", \"dma-ranges\" or child \"reg\" or \"ranges\" property"); } WARNING(avoid_unnecessary_addr_size, check_avoid_unnecessary_addr_size, NULL, &avoid_default_addr_size); @@ -1673,6 +1676,10 @@ static void check_interrupt_map(struct check *c, cellprop = get_property(provider_node, "#address-cells"); if (cellprop) parent_cellsize += propval_cell(cellprop); + else + FAIL_PROP(c, dti, node, irq_map_prop, + "Missing property '#address-cells' in node %s, using 0 as fallback", + provider_node->fullpath); cell += 1 + parent_cellsize; if (cell > map_cells) diff --git a/scripts/dtc/data.c b/scripts/dtc/data.c index 14734233ad8b..5b25aa060416 100644 --- a/scripts/dtc/data.c +++ b/scripts/dtc/data.c @@ -228,11 +228,7 @@ struct data data_add_marker(struct data d, enum markertype type, char *ref) { struct marker *m; - m = xmalloc(sizeof(*m)); - m->offset = d.len; - m->type = type; - m->ref = ref; - m->next = NULL; + m = alloc_marker(d.len, type, ref); return data_append_markers(d, m); } @@ -254,3 +250,44 @@ bool data_is_one_string(struct data d) return true; } + +struct data data_insert_data(struct data d, struct marker *m, struct data old) +{ + unsigned int offset = m->offset; + struct marker *next = m->next; + struct marker *marker; + struct data new_data; + char *ref; + + new_data = data_insert_at_marker(d, m, old.val, old.len); + + /* Copy all markers from old value */ + marker = old.markers; + for_each_marker(marker) { + ref = NULL; + + if (marker->ref) + ref = xstrdup(marker->ref); + + m->next = alloc_marker(marker->offset + offset, marker->type, + ref); + m = m->next; + } + m->next = next; + + return new_data; +} + +struct marker *alloc_marker(unsigned int offset, enum markertype type, + char *ref) +{ + struct marker *m; + + m = xmalloc(sizeof(*m)); + m->offset = offset; + m->type = type; + m->ref = ref; + m->next = NULL; + + return m; +} diff --git a/scripts/dtc/dtc-lexer.l b/scripts/dtc/dtc-lexer.l index de60a70b6bdb..15d585c80798 100644 --- a/scripts/dtc/dtc-lexer.l +++ b/scripts/dtc/dtc-lexer.l @@ -151,6 +151,21 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...); return DT_LABEL; } +{LABEL} { + /* Missed includes or macro definitions while + * preprocessing can lead to unexpected identifiers in + * the input. Report a slightly more informative error + * in this case */ + + lexical_error("Unexpected '%s'", yytext); + + /* Treat it as a literal which often generates further + * useful error messages */ + + yylval.integer = 0; + return DT_LITERAL; + } + ([0-9]+|0[xX][0-9a-fA-F]+)(U|L|UL|LL|ULL)? { char *e; DPRINT("Integer Literal: '%s'\n", yytext); diff --git a/scripts/dtc/dtc.c b/scripts/dtc/dtc.c index 0655c2e2c362..b3445b7d6473 100644 --- a/scripts/dtc/dtc.c +++ b/scripts/dtc/dtc.c @@ -15,7 +15,7 @@ int quiet; /* Level of quietness */ unsigned int reservenum;/* Number of memory reservation slots */ int minsize; /* Minimum blob size */ int padsize; /* Additional padding to blob */ -int alignsize; /* Additional padding to blob accroding to the alignsize */ +int alignsize; /* Additional padding to blob according to the alignsize */ int phandle_format = PHANDLE_EPAPR; /* Use linux,phandle or phandle properties */ int generate_symbols; /* enable symbols & fixup support */ int generate_fixups; /* suppress generation of fixups on symbol support */ @@ -289,7 +289,9 @@ int main(int argc, char *argv[]) if (!depfile) die("Couldn't open dependency file %s: %s\n", depname, strerror(errno)); - fprintf(depfile, "%s:", outname); + + fprint_path_escaped(depfile, outname); + fputc(':', depfile); } if (inform == NULL) diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h index 4c4aaca1fc41..3a220b9afc99 100644 --- a/scripts/dtc/dtc.h +++ b/scripts/dtc/dtc.h @@ -38,7 +38,7 @@ extern int quiet; /* Level of quietness */ extern unsigned int reservenum; /* Number of memory reservation slots */ extern int minsize; /* Minimum blob size */ extern int padsize; /* Additional padding to blob */ -extern int alignsize; /* Additional padding to blob accroding to the alignsize */ +extern int alignsize; /* Additional padding to blob according to the alignsize */ extern int phandle_format; /* Use linux,phandle or phandle properties */ extern int generate_symbols; /* generate symbols for nodes with labels */ extern int generate_fixups; /* generate fixups */ @@ -182,7 +182,10 @@ struct data data_append_addr(struct data d, uint64_t addr); struct data data_append_byte(struct data d, uint8_t byte); struct data data_append_zeroes(struct data d, int len); struct data data_append_align(struct data d, int align); +struct data data_insert_data(struct data d, struct marker *m, struct data old); +struct marker *alloc_marker(unsigned int offset, enum markertype type, + char *ref); struct data data_add_marker(struct data d, enum markertype type, char *ref); bool data_is_one_string(struct data d); diff --git a/scripts/dtc/fdtoverlay.c b/scripts/dtc/fdtoverlay.c index 699b4f616502..ee1eb8f3ad28 100644 --- a/scripts/dtc/fdtoverlay.c +++ b/scripts/dtc/fdtoverlay.c @@ -46,6 +46,7 @@ static void *apply_one(char *base, const char *overlay, size_t *buf_len, char *tmp = NULL; char *tmpo; int ret; + bool has_symbols; /* * We take copies first, because a failed apply can trash @@ -62,6 +63,8 @@ static void *apply_one(char *base, const char *overlay, size_t *buf_len, fdt_strerror(ret)); goto fail; } + ret = fdt_path_offset(tmp, "/__symbols__"); + has_symbols = ret >= 0; memcpy(tmpo, overlay, fdt_totalsize(overlay)); @@ -74,6 +77,11 @@ static void *apply_one(char *base, const char *overlay, size_t *buf_len, if (ret) { fprintf(stderr, "\nFailed to apply '%s': %s\n", name, fdt_strerror(ret)); + if (!has_symbols) { + fprintf(stderr, + "base blob does not have a '/__symbols__' node, " + "make sure you have compiled the base blob with '-@' option\n"); + } goto fail; } diff --git a/scripts/dtc/flattree.c b/scripts/dtc/flattree.c index 1bcd8089c5b9..30e6de2044b2 100644 --- a/scripts/dtc/flattree.c +++ b/scripts/dtc/flattree.c @@ -503,7 +503,7 @@ void dt_to_asm(FILE *f, struct dt_info *dti, int version) * Reserve map entries. * Align the reserve map to a doubleword boundary. * Each entry is an (address, size) pair of u64 values. - * Always supply a zero-sized temination entry. + * Always supply a zero-sized termination entry. */ asm_emit_align(f, 8); emit_label(f, symprefix, "reserve_map"); diff --git a/scripts/dtc/libfdt/fdt.c b/scripts/dtc/libfdt/fdt.c index 20c6415b9ced..95f644c31f94 100644 --- a/scripts/dtc/libfdt/fdt.c +++ b/scripts/dtc/libfdt/fdt.c @@ -312,14 +312,14 @@ int fdt_next_subnode(const void *fdt, int offset) return offset; } -const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) +const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s, + int slen) { - int len = strlen(s) + 1; - const char *last = strtab + tabsize - len; + const char *last = strtab + tabsize - (slen + 1); const char *p; for (p = strtab; p <= last; p++) - if (memcmp(p, s, len) == 0) + if (memcmp(p, s, slen) == 0 && p[slen] == '\0') return p; return NULL; } diff --git a/scripts/dtc/libfdt/fdt.h b/scripts/dtc/libfdt/fdt.h index 0c91aa7f67b5..a07abfcc7108 100644 --- a/scripts/dtc/libfdt/fdt.h +++ b/scripts/dtc/libfdt/fdt.h @@ -7,7 +7,7 @@ * Copyright 2012 Kim Phillips, Freescale Semiconductor. */ -#ifndef __ASSEMBLY__ +#ifndef __ASSEMBLER__ struct fdt_header { fdt32_t magic; /* magic word FDT_MAGIC */ @@ -45,7 +45,7 @@ struct fdt_property { char data[]; }; -#endif /* !__ASSEMBLY */ +#endif /* !__ASSEMBLER__ */ #define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ #define FDT_TAGSIZE sizeof(fdt32_t) diff --git a/scripts/dtc/libfdt/fdt_overlay.c b/scripts/dtc/libfdt/fdt_overlay.c index 28b667ffc490..e6b9eb643958 100644 --- a/scripts/dtc/libfdt/fdt_overlay.c +++ b/scripts/dtc/libfdt/fdt_overlay.c @@ -307,7 +307,6 @@ static int overlay_update_local_references(void *fdto, uint32_t delta) /** * overlay_fixup_one_phandle - Set an overlay phandle to the base one - * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * @symbols_off: Node offset of the symbols node in the base device tree * @path: Path to a node holding a phandle in the overlay @@ -328,8 +327,7 @@ static int overlay_update_local_references(void *fdto, uint32_t delta) * 0 on success * Negative error code on failure */ -static int overlay_fixup_one_phandle(void *fdt, void *fdto, - int symbols_off, +static int overlay_fixup_one_phandle(void *fdto, int symbols_off, const char *path, uint32_t path_len, const char *name, uint32_t name_len, int poffset, uint32_t phandle) @@ -351,7 +349,7 @@ static int overlay_fixup_one_phandle(void *fdt, void *fdto, name, name_len, poffset, &phandle_prop, sizeof(phandle_prop)); -}; +} /** * overlay_fixup_phandle - Set an overlay phandle to the base one @@ -443,7 +441,7 @@ static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, if ((*endptr != '\0') || (endptr <= (sep + 1))) return -FDT_ERR_BADOVERLAY; - ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, + ret = overlay_fixup_one_phandle(fdto, symbols_off, path, path_len, name, name_len, poffset, phandle); if (ret) diff --git a/scripts/dtc/libfdt/fdt_rw.c b/scripts/dtc/libfdt/fdt_rw.c index 3621d3651d3f..7475cafce071 100644 --- a/scripts/dtc/libfdt/fdt_rw.c +++ b/scripts/dtc/libfdt/fdt_rw.c @@ -124,31 +124,33 @@ static int fdt_splice_string_(void *fdt, int newlen) * allocated. Ignored if can_assume(NO_ROLLBACK) * @return offset of string in the string table (whether found or added) */ -static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) +static int fdt_find_add_string_(void *fdt, const char *s, int slen, + int *allocated) { char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); const char *p; char *new; - int len = strlen(s) + 1; int err; if (!can_assume(NO_ROLLBACK)) *allocated = 0; - p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s); + p = fdt_find_string_len_(strtab, fdt_size_dt_strings(fdt), s, slen); if (p) /* found it */ return (p - strtab); new = strtab + fdt_size_dt_strings(fdt); - err = fdt_splice_string_(fdt, len); + err = fdt_splice_string_(fdt, slen + 1); if (err) return err; if (!can_assume(NO_ROLLBACK)) *allocated = 1; - memcpy(new, s, len); + memcpy(new, s, slen); + new[slen] = '\0'; + return (new - strtab); } @@ -181,13 +183,15 @@ int fdt_del_mem_rsv(void *fdt, int n) return fdt_splice_mem_rsv_(fdt, re, 1, 0); } -static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name, +static int fdt_resize_property_(void *fdt, int nodeoffset, + const char *name, int namelen, int len, struct fdt_property **prop) { int oldlen; int err; - *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); + *prop = fdt_get_property_namelen_w(fdt, nodeoffset, name, namelen, + &oldlen); if (!*prop) return oldlen; @@ -200,7 +204,7 @@ static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name, } static int fdt_add_property_(void *fdt, int nodeoffset, const char *name, - int len, struct fdt_property **prop) + int namelen, int len, struct fdt_property **prop) { int proplen; int nextoffset; @@ -211,7 +215,7 @@ static int fdt_add_property_(void *fdt, int nodeoffset, const char *name, if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) return nextoffset; - namestroff = fdt_find_add_string_(fdt, name, &allocated); + namestroff = fdt_find_add_string_(fdt, name, namelen, &allocated); if (namestroff < 0) return namestroff; @@ -255,17 +259,18 @@ int fdt_set_name(void *fdt, int nodeoffset, const char *name) return 0; } -int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, - int len, void **prop_data) +int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name, + int namelen, int len, void **prop_data) { struct fdt_property *prop; int err; FDT_RW_PROBE(fdt); - err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop); + err = fdt_resize_property_(fdt, nodeoffset, name, namelen, len, &prop); if (err == -FDT_ERR_NOTFOUND) - err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); + err = fdt_add_property_(fdt, nodeoffset, name, namelen, len, + &prop); if (err) return err; @@ -273,13 +278,14 @@ int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, return 0; } -int fdt_setprop(void *fdt, int nodeoffset, const char *name, - const void *val, int len) +int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name, + int namelen, const void *val, int len) { void *prop_data; int err; - err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); + err = fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, namelen, + len, &prop_data); if (err) return err; @@ -307,7 +313,8 @@ int fdt_appendprop(void *fdt, int nodeoffset, const char *name, prop->len = cpu_to_fdt32(newlen); memcpy(prop->data + oldlen, val, len); } else { - err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); + err = fdt_add_property_(fdt, nodeoffset, name, strlen(name), + len, &prop); if (err) return err; memcpy(prop->data, val, len); diff --git a/scripts/dtc/libfdt/libfdt.h b/scripts/dtc/libfdt/libfdt.h index 2d409d8e829b..914bf90785ab 100644 --- a/scripts/dtc/libfdt/libfdt.h +++ b/scripts/dtc/libfdt/libfdt.h @@ -14,7 +14,7 @@ extern "C" { #endif #define FDT_FIRST_SUPPORTED_VERSION 0x02 -#define FDT_LAST_COMPATIBLE_VERSION 0x10 +#define FDT_LAST_COMPATIBLE_VERSION 0x10 #define FDT_LAST_SUPPORTED_VERSION 0x11 /* Error codes: informative error codes */ @@ -263,16 +263,16 @@ int fdt_next_subnode(const void *fdt, int offset); struct fdt_header *fdth = (struct fdt_header *)fdt; \ fdth->name = cpu_to_fdt32(val); \ } -fdt_set_hdr_(magic); -fdt_set_hdr_(totalsize); -fdt_set_hdr_(off_dt_struct); -fdt_set_hdr_(off_dt_strings); -fdt_set_hdr_(off_mem_rsvmap); -fdt_set_hdr_(version); -fdt_set_hdr_(last_comp_version); -fdt_set_hdr_(boot_cpuid_phys); -fdt_set_hdr_(size_dt_strings); -fdt_set_hdr_(size_dt_struct); +fdt_set_hdr_(magic) +fdt_set_hdr_(totalsize) +fdt_set_hdr_(off_dt_struct) +fdt_set_hdr_(off_dt_strings) +fdt_set_hdr_(off_mem_rsvmap) +fdt_set_hdr_(version) +fdt_set_hdr_(last_comp_version) +fdt_set_hdr_(boot_cpuid_phys) +fdt_set_hdr_(size_dt_strings) +fdt_set_hdr_(size_dt_struct) #undef fdt_set_hdr_ /** @@ -285,7 +285,7 @@ size_t fdt_header_size(const void *fdt); /** * fdt_header_size_ - internal function to get header size from a version number - * @version: devicetree version number + * @version: device tree version number * * Return: size of DTB header in bytes */ @@ -554,7 +554,7 @@ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); * -FDT_ERR_BADPATH, given path does not begin with '/' and the first * component is not a valid alias * -FDT_ERR_NOTFOUND, if the requested node does not exist - * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, @@ -599,7 +599,7 @@ const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); * structure block offset of the property (>=0), on success * -FDT_ERR_NOTFOUND, if the requested node has no properties * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag - * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, @@ -620,7 +620,7 @@ int fdt_first_property_offset(const void *fdt, int nodeoffset); * structure block offset of the next property (>=0), on success * -FDT_ERR_NOTFOUND, if the given property is the last in its node * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag - * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, @@ -712,6 +712,13 @@ const struct fdt_property *fdt_get_property_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp); +static inline struct fdt_property * +fdt_get_property_namelen_w(void *fdt, int nodeoffset, const char *name, + int namelen, int *lenp) +{ + return (struct fdt_property *)(uintptr_t)fdt_get_property_namelen( + fdt, nodeoffset, name, namelen, lenp); +} #endif /** @@ -764,7 +771,7 @@ static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, * to within the device blob itself, not a copy of the value). If * lenp is non-NULL, the length of the property value is also * returned, in the integer pointed to by lenp. If namep is non-NULL, - * the property's namne will also be returned in the char * pointed to + * the property's name will also be returned in the char * pointed to * by namep (this will be a pointer to within the device tree's string * block, not a new copy of the name). * @@ -772,7 +779,7 @@ static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, * pointer to the property's value * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) - * if namep is non-NULL *namep contiains a pointer to the property + * if namep is non-NULL *namep contains a pointer to the property * name. * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): @@ -866,7 +873,7 @@ uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); /** * fdt_get_alias_namelen - get alias based on substring * @fdt: pointer to the device tree blob - * @name: name of the alias th look up + * @name: name of the alias to look up * @namelen: number of characters of name to consider * * Identical to fdt_get_alias(), but only examine the first @namelen @@ -883,7 +890,7 @@ const char *fdt_get_alias_namelen(const void *fdt, /** * fdt_get_alias - retrieve the path referenced by a given alias * @fdt: pointer to the device tree blob - * @name: name of the alias th look up + * @name: name of the alias to look up * * fdt_get_alias() retrieves the value of a given alias. That is, the * value of the property named @name in the node /aliases. @@ -1259,8 +1266,8 @@ const char *fdt_stringlist_get(const void *fdt, int nodeoffset, * * returns: * 0 <= n < FDT_MAX_NCELLS, on success - * 2, if the node has no #address-cells property - * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * 2, if the node has no #address-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid * #address-cells property * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, @@ -1280,8 +1287,8 @@ int fdt_address_cells(const void *fdt, int nodeoffset); * * returns: * 0 <= n < FDT_MAX_NCELLS, on success - * 1, if the node has no #size-cells property - * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * 1, if the node has no #size-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid * #size-cells property * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, @@ -1562,7 +1569,7 @@ static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) * @fdt: pointer to the device tree blob * @name: name of property to add * @len: length of property value in bytes - * @valp: returns a pointer to where where the value should be placed + * @valp: returns a pointer to where the value should be placed * * returns: * 0, on success @@ -1659,6 +1666,38 @@ int fdt_del_mem_rsv(void *fdt, int n); */ int fdt_set_name(void *fdt, int nodeoffset, const char *name); +/** + * fdt_setprop_namelen - create or change a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @namelen: length of the name + * @val: pointer to data to set the property value to + * @len: length of the property value + * + * fdt_setprop_namelen() sets the value of the named property in the given + * node to the given value and length, creating the property if it + * does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name, + int namelen, const void *val, int len); + /** * fdt_setprop - create or change a property * @fdt: pointer to the device tree blob @@ -1687,8 +1726,44 @@ int fdt_set_name(void *fdt, int nodeoffset, const char *name); * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ -int fdt_setprop(void *fdt, int nodeoffset, const char *name, - const void *val, int len); +static inline int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + return fdt_setprop_namelen(fdt, nodeoffset, name, strlen(name), val, + len); +} + +/** + * fdt_setprop_placeholder_namelen - allocate space for a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @namelen: length of the name + * @len: length of the property value + * @prop_data: return pointer to property data + * + * fdt_setprop_placeholder_namelen() allocates the named property in the given node. + * If the property exists it is resized. In either case a pointer to the + * property data is returned. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name, + int namelen, int len, void **prop_data); /** * fdt_setprop_placeholder - allocate space for a property @@ -1698,7 +1773,7 @@ int fdt_setprop(void *fdt, int nodeoffset, const char *name, * @len: length of the property value * @prop_data: return pointer to property data * - * fdt_setprop_placeholer() allocates the named property in the given node. + * fdt_setprop_placeholder() allocates the named property in the given node. * If the property exists it is resized. In either case a pointer to the * property data is returned. * @@ -1718,8 +1793,13 @@ int fdt_setprop(void *fdt, int nodeoffset, const char *name, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ -int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, - int len, void **prop_data); +static inline int fdt_setprop_placeholder(void *fdt, int nodeoffset, + const char *name, int len, + void **prop_data) +{ + return fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, + strlen(name), len, prop_data); +} /** * fdt_setprop_u32 - set a property to a 32-bit integer @@ -1839,6 +1919,38 @@ static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, #define fdt_setprop_string(fdt, nodeoffset, name, str) \ fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) +/** + * fdt_setprop_namelen_string - set a property to a string value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @namelen: number of characters of name to consider + * @str: string value for the property + * + * fdt_setprop_namelen_string() sets the value of the named property in the + * given node to the given string value (using the length of the + * string to determine the new length of the property), or creates a + * new property with that value if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_namelen_string(fdt, nodeoffset, name, namelen, str) \ + fdt_setprop_namelen((fdt), (nodeoffset), (name), (namelen), (str), \ + strlen(str) + 1) /** * fdt_setprop_empty - set a property to an empty value @@ -2059,7 +2171,7 @@ int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, * @nodeoffset: offset of the node whose property to nop * @name: name of the property to nop * - * fdt_del_property() will delete the given property. + * fdt_delprop() will delete the given property. * * This function will delete data from the blob, and will therefore * change the offsets of some existing nodes. @@ -2111,8 +2223,7 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset, * change the offsets of some existing nodes. * * returns: - * structure block offset of the created nodeequested subnode (>=0), on - * success + * structure block offset of the created subnode (>=0), on success * -FDT_ERR_NOTFOUND, if the requested subnode does not exist * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE * tag @@ -2122,7 +2233,7 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset, * blob to contain the new node * -FDT_ERR_NOSPACE * -FDT_ERR_BADLAYOUT - * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, @@ -2167,7 +2278,7 @@ int fdt_del_node(void *fdt, int nodeoffset); * returns: * 0, on success * -FDT_ERR_NOSPACE, there's not enough space in the base device tree - * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or + * -FDT_ERR_NOTFOUND, the overlay points to some nonexistent nodes or * properties in the base DT * -FDT_ERR_BADPHANDLE, * -FDT_ERR_BADOVERLAY, diff --git a/scripts/dtc/libfdt/libfdt_internal.h b/scripts/dtc/libfdt/libfdt_internal.h index 16bda1906a7b..b60b5456f596 100644 --- a/scripts/dtc/libfdt/libfdt_internal.h +++ b/scripts/dtc/libfdt/libfdt_internal.h @@ -20,7 +20,15 @@ int32_t fdt_ro_probe_(const void *fdt); int fdt_check_node_offset_(const void *fdt, int offset); int fdt_check_prop_offset_(const void *fdt, int offset); -const char *fdt_find_string_(const char *strtab, int tabsize, const char *s); + +const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s, + int s_len); +static inline const char *fdt_find_string_(const char *strtab, int tabsize, + const char *s) +{ + return fdt_find_string_len_(strtab, tabsize, s, strlen(s)); +} + int fdt_node_end_offset_(void *fdt, int nodeoffset); static inline const void *fdt_offset_ptr_(const void *fdt, int offset) @@ -47,8 +55,8 @@ static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n) } /* - * Internal helpers to access tructural elements of the device tree - * blob (rather than for exaple reading integers from within property + * Internal helpers to access structural elements of the device tree + * blob (rather than for example reading integers from within property * values). We assume that we are either given a naturally aligned * address for the platform or if we are not, we are on a platform * where unaligned memory reads will be handled in a graceful manner. diff --git a/scripts/dtc/livetree.c b/scripts/dtc/livetree.c index 49f723002f85..d51d05830b18 100644 --- a/scripts/dtc/livetree.c +++ b/scripts/dtc/livetree.c @@ -174,7 +174,7 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node) old_prop->val = new_prop->val; old_prop->deleted = 0; - free(old_prop->srcpos); + srcpos_free(old_prop->srcpos); old_prop->srcpos = new_prop->srcpos; free(new_prop); new_prop = NULL; @@ -504,7 +504,7 @@ struct node *get_subnode(struct node *node, const char *nodename) struct node *child; for_each_child(node, child) - if (streq(child->name, nodename)) + if (streq(child->name, nodename) && !child->deleted) return child; return NULL; @@ -1014,9 +1014,7 @@ static void add_local_fixup_entry(struct dt_info *dti, /* walk the path components creating nodes if they don't exist */ for (wn = lfn, i = 1; i < depth; i++, wn = nwn) { /* if no node exists, create it */ - nwn = get_subnode(wn, compp[i]); - if (!nwn) - nwn = build_and_name_child_node(wn, compp[i]); + nwn = build_root_node(wn, compp[i]); } free(compp); @@ -1058,16 +1056,29 @@ void generate_label_tree(struct dt_info *dti, const char *name, bool allocph) void generate_fixups_tree(struct dt_info *dti, const char *name) { + struct node *n = get_subnode(dti->dt, name); + + /* Start with an empty __fixups__ node to not get duplicates */ + if (n) + n->deleted = true; + if (!any_fixup_tree(dti, dti->dt)) return; - generate_fixups_tree_internal(dti, build_root_node(dti->dt, name), + generate_fixups_tree_internal(dti, + build_and_name_child_node(dti->dt, name), dti->dt); } void generate_local_fixups_tree(struct dt_info *dti, const char *name) { + struct node *n = get_subnode(dti->dt, name); + + /* Start with an empty __local_fixups__ node to not get duplicates */ + if (n) + n->deleted = true; if (!any_local_fixup_tree(dti, dti->dt)) return; - generate_local_fixups_tree_internal(dti, build_root_node(dti->dt, name), + generate_local_fixups_tree_internal(dti, + build_and_name_child_node(dti->dt, name), dti->dt); } diff --git a/scripts/dtc/srcpos.c b/scripts/dtc/srcpos.c index 8e4d18a90b47..5bb57bf6856c 100644 --- a/scripts/dtc/srcpos.c +++ b/scripts/dtc/srcpos.c @@ -160,8 +160,10 @@ FILE *srcfile_relative_open(const char *fname, char **fullnamep) strerror(errno)); } - if (depfile) - fprintf(depfile, " %s", fullname); + if (depfile) { + fputc(' ', depfile); + fprint_path_escaped(depfile, fullname); + } if (fullnamep) *fullnamep = fullname; @@ -285,6 +287,17 @@ struct srcpos *srcpos_extend(struct srcpos *pos, struct srcpos *newtail) return pos; } +void srcpos_free(struct srcpos *pos) +{ + struct srcpos *p_next; + + while (pos) { + p_next = pos->next; + free(pos); + pos = p_next; + } +} + char * srcpos_string(struct srcpos *pos) { diff --git a/scripts/dtc/srcpos.h b/scripts/dtc/srcpos.h index 4318d7ad34d9..4d60b50e3119 100644 --- a/scripts/dtc/srcpos.h +++ b/scripts/dtc/srcpos.h @@ -88,6 +88,7 @@ extern void srcpos_update(struct srcpos *pos, const char *text, int len); extern struct srcpos *srcpos_copy(struct srcpos *pos); extern struct srcpos *srcpos_extend(struct srcpos *new_srcpos, struct srcpos *old_srcpos); +extern void srcpos_free(struct srcpos *pos); extern char *srcpos_string(struct srcpos *pos); extern char *srcpos_string_first(struct srcpos *pos, int level); extern char *srcpos_string_last(struct srcpos *pos, int level); diff --git a/scripts/dtc/treesource.c b/scripts/dtc/treesource.c index ae15839ba6a5..d25f01fc6937 100644 --- a/scripts/dtc/treesource.c +++ b/scripts/dtc/treesource.c @@ -139,26 +139,48 @@ static const char *delim_end[] = { [TYPE_STRING] = "", }; +/* + * The invariants in the marker list are: + * - offsets are non-strictly monotonically increasing + * - for a single offset there is at most one type marker + * - for a single offset that has both a type marker and non-type markers, the + * type marker appears before the others. + */ +static struct marker **add_marker(struct marker **mi, + enum markertype type, unsigned int offset, char *ref) +{ + struct marker *nm; + + while (*mi && (*mi)->offset < offset) + mi = &(*mi)->next; + + if (*mi && (*mi)->offset == offset && is_type_marker((*mi)->type)) { + if (is_type_marker(type)) + return mi; + mi = &(*mi)->next; + } + + if (*mi && (*mi)->offset == offset && type == (*mi)->type) + return mi; + + nm = xmalloc(sizeof(*nm)); + nm->type = type; + nm->offset = offset; + nm->ref = ref; + nm->next = *mi; + *mi = nm; + + return &nm->next; +} + static void add_string_markers(struct property *prop) { int l, len = prop->val.len; const char *p = prop->val.val; + struct marker **mi = &prop->val.markers; - for (l = strlen(p) + 1; l < len; l += strlen(p + l) + 1) { - struct marker *m, **nextp; - - m = xmalloc(sizeof(*m)); - m->offset = l; - m->type = TYPE_STRING; - m->ref = NULL; - m->next = NULL; - - /* Find the end of the markerlist */ - nextp = &prop->val.markers; - while (*nextp) - nextp = &((*nextp)->next); - *nextp = m; - } + for (l = strlen(p) + 1; l < len; l += strlen(p + l) + 1) + mi = add_marker(mi, TYPE_STRING, l, NULL); } static enum markertype guess_value_type(struct property *prop) diff --git a/scripts/dtc/util.c b/scripts/dtc/util.c index 507f0120cd13..412592320265 100644 --- a/scripts/dtc/util.c +++ b/scripts/dtc/util.c @@ -23,6 +23,22 @@ #include "util.h" #include "version_gen.h" +void fprint_path_escaped(FILE *fp, const char *path) +{ + const char *p = path; + + while (*p) { + if (*p == ' ') { + fputc('\\', fp); + fputc(' ', fp); + } else { + fputc(*p, fp); + } + + p++; + } +} + char *xstrdup(const char *s) { int len = strlen(s) + 1; diff --git a/scripts/dtc/util.h b/scripts/dtc/util.h index b448cd79efd3..800f2e2c55b1 100644 --- a/scripts/dtc/util.h +++ b/scripts/dtc/util.h @@ -42,6 +42,11 @@ static inline void NORETURN PRINTF(1, 2) die(const char *str, ...) exit(1); } +/** + * Writes path to fp, escaping spaces with a backslash. + */ +void fprint_path_escaped(FILE *fp, const char *path); + static inline void *xmalloc(size_t len) { void *new = malloc(len); diff --git a/scripts/dtc/version_gen.h b/scripts/dtc/version_gen.h index bf81ce593685..226c48bf75dc 100644 --- a/scripts/dtc/version_gen.h +++ b/scripts/dtc/version_gen.h @@ -1 +1 @@ -#define DTC_VERSION "DTC 1.7.0-gbcd02b52" +#define DTC_VERSION "DTC 1.7.2-g52f07dcc" -- cgit v1.2.3 From 6656ae4df1a6ad6dfb5c4ce4c76136a42abb9bf4 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:28 -0600 Subject: docs: kdoc: consolidate the stripping of private struct/union members There were two locations duplicating the logic of stripping private members and associated comments; coalesce them into one, and add some comments describing what's going on. Output change: we now no longer add extraneous white space around macro definitions. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-2-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index c3fe4bd5eab4..93fcd8807aa8 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -81,6 +81,21 @@ multi_space = KernRe(r'\s\s+') def trim_whitespace(s): return multi_space.sub(' ', s.strip()) +# +# Remove struct/enum members that have been marked "private". +# +def trim_private_members(text): + # + # First look for a "public:" block that ends a private region, then + # handle the "private until the end" case. + # + text = KernRe(r'/\*\s*private:.*?/\*\s*public:.*?\*/', flags=re.S).sub('', text) + text = KernRe(r'/\*\s*private:.*', flags=re.S).sub('', text) + # + # We needed the comments to do the above, but now we can take them out. + # + return KernRe(r'\s*/\*.*?\*/\s*', flags=re.S).sub('', text).strip() + class state: """ State machine enums @@ -568,12 +583,6 @@ class KernelDoc: args_pattern = r'([^,)]+)' sub_prefixes = [ - (KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), ''), - (KernRe(r'\/\*\s*private:.*', re.S | re.I), ''), - - # Strip comments - (KernRe(r'\/\*.*?\*\/', re.S), ''), - # Strip attributes (attribute, ' '), (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), @@ -648,6 +657,7 @@ class KernelDoc: (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), ] + members = trim_private_members(members) for search, sub in sub_prefixes: members = search.sub(sub, members) @@ -797,24 +807,18 @@ class KernelDoc: """ Stores an enum inside self.entries array. """ - - # Ignore members marked private - proto = KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=re.S).sub('', proto) - proto = KernRe(r'\/\*\s*private:.*}', flags=re.S).sub('}', proto) - - # Strip comments - proto = KernRe(r'\/\*.*?\*\/', flags=re.S).sub('', proto) - - # Strip #define macros inside enums + # + # Strip preprocessor directives. Note that this depends on the + # trailing semicolon we added in process_proto_type(). + # proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) - # # Parse out the name and members of the enum. Typedef form first. # r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') if r.search(proto): declaration_name = r.group(2) - members = r.group(1).rstrip() + members = trim_private_members(r.group(1)) # # Failing that, look for a straight enum # @@ -822,7 +826,7 @@ class KernelDoc: r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}') if r.match(proto): declaration_name = r.group(1) - members = r.group(2).rstrip() + members = trim_private_members(r.group(2)) # # OK, this isn't going to work. # -- cgit v1.2.3 From 259feba4dde78f165b03e231ea9985dfe600c202 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:29 -0600 Subject: docs: kdoc: Move a regex line in dump_struct() The complex struct_members regex was defined far from its use; bring the two together. Remove some extraneous backslashes while making the move. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-3-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 93fcd8807aa8..aa6d11bf29b1 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -551,7 +551,6 @@ class KernelDoc: ] definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?" - struct_members = KernRe(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\}\;]*)(\;)') # Extract struct/union definition members = None @@ -683,6 +682,7 @@ class KernelDoc: # So, we need to have an extra loop on Python to override such # re limitation. + struct_members = KernRe(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\};]*)(;)') while True: tuples = struct_members.findall(members) if not tuples: -- cgit v1.2.3 From 5fd513f01169ae93d202b8c30f0837096664e7d7 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:30 -0600 Subject: docs: kdoc: backslashectomy in kdoc_parser A lot of the regular expressions in this file have extraneous backslashes that may have been needed in Perl, but aren't helpful here. Take them out to reduce slightly the visual noise. Escaping of (){}[] has been left in place, even when unnecessary, for visual clarity. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-4-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index aa6d11bf29b1..14ded23f11e0 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -46,7 +46,7 @@ doc_decl = doc_com + KernRe(r'(\w+)', cache=False) known_section_names = 'description|context|returns?|notes?|examples?' known_sections = KernRe(known_section_names, flags = re.I) doc_sect = doc_com + \ - KernRe(r'\s*(\@[.\w]+|\@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$', + KernRe(r'\s*(@[.\w]+|@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$', flags=re.I, cache=False) doc_content = doc_com_body + KernRe(r'(.*)', cache=False) @@ -60,7 +60,7 @@ attribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", export_symbol = KernRe(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False) export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False) -type_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) +type_param = KernRe(r"@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) # # Tests for the beginning of a kerneldoc block in its various forms. @@ -405,7 +405,7 @@ class KernelDoc: for arg in args.split(splitter): # Strip comments - arg = KernRe(r'\/\*.*\*\/').sub('', arg) + arg = KernRe(r'/\*.*\*/').sub('', arg) # Ignore argument attributes arg = KernRe(r'\sPOS0?\s').sub(' ', arg) @@ -428,7 +428,7 @@ class KernelDoc: arg = arg.replace('#', ',') - r = KernRe(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)') + r = KernRe(r'[^\(]+\(\*?\s*([\w\[\].]*)\s*\)') if r.match(arg): param = r.group(1) else: @@ -443,7 +443,7 @@ class KernelDoc: # Array-of-pointers arg = arg.replace('#', ',') - r = KernRe(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)') + r = KernRe(r'[^\(]+\(\s*\*\s*([\w\[\].]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)') if r.match(arg): param = r.group(1) else: @@ -709,7 +709,7 @@ class KernelDoc: if not arg: continue - r = KernRe(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)') + r = KernRe(r'^([^\(]+\(\*?\s*)([\w.]*)(\s*\).*)') if r.match(arg): # Pointer-to-function dtype = r.group(1) @@ -1044,7 +1044,7 @@ class KernelDoc: Stores a typedef inside self.entries array. """ - typedef_type = r'((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s*' + typedef_type = r'((?:\s+[\w*]+\b){0,7}\s+(?:\w+\b|\*+))\s*' typedef_ident = r'\*?\s*(\w\S+)\s*' typedef_args = r'\s*\((.*)\);' @@ -1265,7 +1265,7 @@ class KernelDoc: self.dump_section() # Look for doc_com + + doc_end: - r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') + r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:.]+\*/') if r.match(line): self.emit_msg(ln, f"suspicious ending line: {line}") @@ -1476,7 +1476,7 @@ class KernelDoc: """Ancillary routine to process a function prototype""" # strip C99-style comments to end of line - line = KernRe(r"\/\/.*$", re.S).sub('', line) + line = KernRe(r"//.*$", re.S).sub('', line) # # Soak up the line's worth of prototype text, stopping at { or ; if present. # -- cgit v1.2.3 From 64cf83bcd3217a9583caeb404ff136366a46705c Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:31 -0600 Subject: docs: kdoc: move the prefix transforms out of dump_struct() dump_struct is one of the longest functions in the kdoc_parser class, making it hard to read and reason about. Move the definition of the prefix transformations out of the function, join them with the definition of "attribute" (which was defined at the top of the file but only used here), and reformat the code slightly for shorter line widths. Just code movement in the end. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-5-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 179 +++++++++++++++++++++------------------- 1 file changed, 96 insertions(+), 83 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 14ded23f11e0..3d007d200da6 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -54,8 +54,6 @@ doc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False) doc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False) doc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False) doc_inline_oneline = KernRe(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cache=False) -attribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", - flags=re.I | re.S, cache=False) export_symbol = KernRe(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False) export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False) @@ -74,6 +72,97 @@ doc_begin_func = KernRe(str(doc_com) + # initial " * ' r'(?:[-:].*)?$', # description (not captured) cache = False) +# +# Here begins a long set of transformations to turn structure member prefixes +# and macro invocations into something we can parse and generate kdoc for. +# +struct_attribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", + flags=re.I | re.S, cache=False) +struct_args_pattern = r'([^,)]+)' + +struct_prefixes = [ + # Strip attributes + (struct_attribute, ' '), + (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__packed\s*', re.S), ' '), + (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), + (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '), + (KernRe(r'\s*____cacheline_aligned', re.S), ' '), + # + # Unwrap struct_group macros based on this definition: + # __struct_group(TAG, NAME, ATTRS, MEMBERS...) + # which has variants like: struct_group(NAME, MEMBERS...) + # Only MEMBERS arguments require documentation. + # + # Parsing them happens on two steps: + # + # 1. drop struct group arguments that aren't at MEMBERS, + # storing them as STRUCT_GROUP(MEMBERS) + # + # 2. remove STRUCT_GROUP() ancillary macro. + # + # The original logic used to remove STRUCT_GROUP() using an + # advanced regex: + # + # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; + # + # with two patterns that are incompatible with + # Python re module, as it has: + # + # - a recursive pattern: (?1) + # - an atomic grouping: (?>...) + # + # I tried a simpler version: but it didn't work either: + # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; + # + # As it doesn't properly match the end parenthesis on some cases. + # + # So, a better solution was crafted: there's now a NestedMatch + # class that ensures that delimiters after a search are properly + # matched. So, the implementation to drop STRUCT_GROUP() will be + # handled in separate. + # + (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), + (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('), + (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('), + (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('), + # + # Replace macros + # + # TODO: use NestedMatch for FOO($1, $2, ...) matches + # + # it is better to also move those to the NestedMatch logic, + # to ensure that parenthesis will be properly matched. + # + (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), + r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), + (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), + r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), + (KernRe(r'DECLARE_BITMAP\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)', + re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), + (KernRe(r'DECLARE_HASHTABLE\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)', + re.S), r'unsigned long \1[1 << ((\2) - 1)]'), + (KernRe(r'DECLARE_KFIFO\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + + r',\s*' + struct_args_pattern + r'\)', re.S), r'\2 *\1'), + (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + struct_args_pattern + r',\s*' + + struct_args_pattern + r'\)', re.S), r'\2 *\1'), + (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + struct_args_pattern + r',\s*' + + struct_args_pattern + r'\)', re.S), r'\1 \2[]'), + (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + struct_args_pattern + r'\)', re.S), r'dma_addr_t \1'), + (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + struct_args_pattern + r'\)', re.S), r'__u32 \1'), +] +# +# Regexes here are guaranteed to have the end limiter matching +# the start delimiter. Yet, right now, only one replace group +# is allowed. +# +struct_nested_prefixes = [ + (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), +] + + # # A little helper to get rid of excess white space # @@ -578,91 +667,15 @@ class KernelDoc: self.emit_msg(ln, f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n") return - - args_pattern = r'([^,)]+)' - - sub_prefixes = [ - # Strip attributes - (attribute, ' '), - (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), - (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), - (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), - (KernRe(r'\s*__packed\s*', re.S), ' '), - (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), - (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '), - (KernRe(r'\s*____cacheline_aligned', re.S), ' '), - - # Unwrap struct_group macros based on this definition: - # __struct_group(TAG, NAME, ATTRS, MEMBERS...) - # which has variants like: struct_group(NAME, MEMBERS...) - # Only MEMBERS arguments require documentation. - # - # Parsing them happens on two steps: - # - # 1. drop struct group arguments that aren't at MEMBERS, - # storing them as STRUCT_GROUP(MEMBERS) - # - # 2. remove STRUCT_GROUP() ancillary macro. - # - # The original logic used to remove STRUCT_GROUP() using an - # advanced regex: - # - # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; - # - # with two patterns that are incompatible with - # Python re module, as it has: - # - # - a recursive pattern: (?1) - # - an atomic grouping: (?>...) - # - # I tried a simpler version: but it didn't work either: - # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; - # - # As it doesn't properly match the end parenthesis on some cases. - # - # So, a better solution was crafted: there's now a NestedMatch - # class that ensures that delimiters after a search are properly - # matched. So, the implementation to drop STRUCT_GROUP() will be - # handled in separate. - - (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), - (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('), - (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('), - (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('), - - # Replace macros - # - # TODO: use NestedMatch for FOO($1, $2, ...) matches - # - # it is better to also move those to the NestedMatch logic, - # to ensure that parenthesis will be properly matched. - - (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), - (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), - (KernRe(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), - (KernRe(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'), - (KernRe(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), - (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), - (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'), - (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'), - (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'), - ] - - # Regexes here are guaranteed to have the end limiter matching - # the start delimiter. Yet, right now, only one replace group - # is allowed. - - sub_nested_prefixes = [ - (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), - ] - + # + # Go through the list of members applying all of our transformations. + # members = trim_private_members(members) - for search, sub in sub_prefixes: + for search, sub in struct_prefixes: members = search.sub(sub, members) nested = NestedMatch() - - for search, sub in sub_nested_prefixes: + for search, sub in struct_nested_prefixes: members = nested.sub(search, sub, members) # Keeps the original declaration as-is -- cgit v1.2.3 From 0f7344129434a6b44d4abb5080a9d67dd734ee07 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:32 -0600 Subject: docs: kdoc: split top-level prototype parsing out of dump_struct() Move the initial split of the prototype into its own function in the ongoing effort to cut dump_struct() down to size. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-6-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 43 +++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 23 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 3d007d200da6..ab896dcd9572 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -624,13 +624,11 @@ class KernelDoc: self.emit_msg(ln, f"No description found for return value of '{declaration_name}'") - def dump_struct(self, ln, proto): - """ - Store an entry for an struct or union - """ - + # + # Split apart a structure prototype; returns (struct|union, name, members) or None + # + def split_struct_proto(self, proto): type_pattern = r'(struct|union)' - qualifiers = [ "__attribute__", "__packed", @@ -638,34 +636,33 @@ class KernelDoc: "____cacheline_aligned_in_smp", "____cacheline_aligned", ] - definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?" - # Extract struct/union definition - members = None - declaration_name = None - decl_type = None - r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body) if r.search(proto): - decl_type = r.group(1) - declaration_name = r.group(2) - members = r.group(3) + return (r.group(1), r.group(2), r.group(3)) else: r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;') - if r.search(proto): - decl_type = r.group(1) - declaration_name = r.group(3) - members = r.group(2) + return (r.group(1), r.group(3), r.group(2)) + return None - if not members: + def dump_struct(self, ln, proto): + """ + Store an entry for an struct or union + """ + # + # Do the basic parse to get the pieces of the declaration. + # + struct_parts = self.split_struct_proto(proto) + if not struct_parts: self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!") return + decl_type, declaration_name, members = struct_parts if self.entry.identifier != declaration_name: - self.emit_msg(ln, - f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n") + self.emit_msg(ln, f"expecting prototype for {decl_type} {self.entry.identifier}. " + f"Prototype was for {decl_type} {declaration_name} instead\n") return # # Go through the list of members applying all of our transformations. @@ -695,7 +692,7 @@ class KernelDoc: # So, we need to have an extra loop on Python to override such # re limitation. - struct_members = KernRe(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\};]*)(;)') + struct_members = KernRe(r'(struct|union)([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\};]*)(;)') while True: tuples = struct_members.findall(members) if not tuples: -- cgit v1.2.3 From 77e3c875f0a83d9192079e88d8569ac36c6b6bea Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:33 -0600 Subject: docs: kdoc: split struct-member rewriting out of dump_struct() The massive loop that massages struct members shares no data with the rest of dump_struct(); split it out into its own function. Code movement only, no other changes. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-7-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 65 +++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 31 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index ab896dcd9572..fbd7f6ce3360 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -647,37 +647,7 @@ class KernelDoc: return (r.group(1), r.group(3), r.group(2)) return None - def dump_struct(self, ln, proto): - """ - Store an entry for an struct or union - """ - # - # Do the basic parse to get the pieces of the declaration. - # - struct_parts = self.split_struct_proto(proto) - if not struct_parts: - self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!") - return - decl_type, declaration_name, members = struct_parts - - if self.entry.identifier != declaration_name: - self.emit_msg(ln, f"expecting prototype for {decl_type} {self.entry.identifier}. " - f"Prototype was for {decl_type} {declaration_name} instead\n") - return - # - # Go through the list of members applying all of our transformations. - # - members = trim_private_members(members) - for search, sub in struct_prefixes: - members = search.sub(sub, members) - - nested = NestedMatch() - for search, sub in struct_nested_prefixes: - members = nested.sub(search, sub, members) - - # Keeps the original declaration as-is - declaration = members - + def rewrite_struct_members(self, members): # Split nested struct/union elements # # This loop was simpler at the original kernel-doc perl version, as @@ -768,6 +738,39 @@ class KernelDoc: newmember += f"{dtype} {s_id}.{name}; " members = members.replace(oldmember, newmember) + return members + + def dump_struct(self, ln, proto): + """ + Store an entry for an struct or union + """ + # + # Do the basic parse to get the pieces of the declaration. + # + struct_parts = self.split_struct_proto(proto) + if not struct_parts: + self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!") + return + decl_type, declaration_name, members = struct_parts + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, f"expecting prototype for {decl_type} {self.entry.identifier}. " + f"Prototype was for {decl_type} {declaration_name} instead\n") + return + # + # Go through the list of members applying all of our transformations. + # + members = trim_private_members(members) + for search, sub in struct_prefixes: + members = search.sub(sub, members) + + nested = NestedMatch() + for search, sub in struct_nested_prefixes: + members = nested.sub(search, sub, members) + + # Keeps the original declaration as-is + declaration = members + members = self.rewrite_struct_members(members) # Ignore other nested elements, like enums members = re.sub(r'(\{[^\{\}]*\})', '', members) -- cgit v1.2.3 From f8208676c1c85c0b91e726954c05f5859b890ccb Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:34 -0600 Subject: docs: kdoc: rework the rewrite_struct_members() main loop Adopt a more Pythonic form for the main loop of this function, getting rid of the "while True:" construction and making the actual loop invariant explicit. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-8-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index fbd7f6ce3360..e11f3d6e9469 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -663,11 +663,8 @@ class KernelDoc: # re limitation. struct_members = KernRe(r'(struct|union)([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\};]*)(;)') - while True: - tuples = struct_members.findall(members) - if not tuples: - break - + tuples = struct_members.findall(members) + while tuples: for t in tuples: newmember = "" maintype = t[0] @@ -738,6 +735,7 @@ class KernelDoc: newmember += f"{dtype} {s_id}.{name}; " members = members.replace(oldmember, newmember) + tuples = struct_members.findall(members) return members def dump_struct(self, ln, proto): -- cgit v1.2.3 From fb20e610393b02a832a0bf4964e12c20a7ffa2f8 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:35 -0600 Subject: docs: kdoc: remove an extraneous strip() call ...the variable in question was already strip()ed at the top of the loop. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-9-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index e11f3d6e9469..0c279aa802a0 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -703,7 +703,6 @@ class KernelDoc: newmember += f"{dtype}{s_id}.{name}{extra}; " else: - arg = arg.strip() # Handle bitmaps arg = KernRe(r':\s*\d+\s*').sub('', arg) -- cgit v1.2.3 From a8c4b0a8f1969e5ee0e8abb62cde8f0d7bcc2009 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:36 -0600 Subject: docs: kdoc: Some rewrite_struct_members() commenting Add comments to rewrite_struct_members() describing what it is actually doing, and reformat/comment the main struct_members regex so that it is (more) comprehensible to humans. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-10-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 0c279aa802a0..e3d0270b1a19 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -647,22 +647,28 @@ class KernelDoc: return (r.group(1), r.group(3), r.group(2)) return None + # + # Rewrite the members of a structure or union for easier formatting later on. + # Among other things, this function will turn a member like: + # + # struct { inner_members; } foo; + # + # into: + # + # struct foo; inner_members; + # def rewrite_struct_members(self, members): - # Split nested struct/union elements - # - # This loop was simpler at the original kernel-doc perl version, as - # while ($members =~ m/$struct_members/) { ... } - # reads 'members' string on each interaction. # - # Python behavior is different: it parses 'members' only once, - # creating a list of tuples from the first interaction. + # Process struct/union members from the most deeply nested outward. The + # trick is in the ^{ below - it prevents a match of an outer struct/union + # until the inner one has been munged (removing the "{" in the process). # - # On other words, this won't get nested structs. - # - # So, we need to have an extra loop on Python to override such - # re limitation. - - struct_members = KernRe(r'(struct|union)([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\};]*)(;)') + struct_members = KernRe(r'(struct|union)' # 0: declaration type + r'([^\{\};]+)' # 1: possible name + r'(\{)' + r'([^\{\}]*)' # 3: Contents of declaration + r'(\})' + r'([^\{\};]*)(;)') # 5: Remaining stuff after declaration tuples = struct_members.findall(members) while tuples: for t in tuples: -- cgit v1.2.3 From e6dd4e2a5ca1c1e2fb168249532da1d95b5b24af Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:37 -0600 Subject: docs: kdoc: further rewrite_struct_members() cleanup Get rid of some redundant checks, and generally tighten up the code; no logical change. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-11-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 86 ++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 45 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index e3d0270b1a19..b3f937901037 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -673,73 +673,69 @@ class KernelDoc: while tuples: for t in tuples: newmember = "" - maintype = t[0] - s_ids = t[5] - content = t[3] - - oldmember = "".join(t) - - for s_id in s_ids.split(','): + oldmember = "".join(t) # Reconstruct the original formatting + dtype, name, lbr, content, rbr, rest, semi = t + # + # Pass through each field name, normalizing the form and formatting. + # + for s_id in rest.split(','): s_id = s_id.strip() - - newmember += f"{maintype} {s_id}; " + newmember += f"{dtype} {s_id}; " + # + # Remove bitfield/array/pointer info, getting the bare name. + # s_id = KernRe(r'[:\[].*').sub('', s_id) s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) - + # + # Pass through the members of this inner structure/union. + # for arg in content.split(';'): arg = arg.strip() - - if not arg: - continue - + # + # Look for (type)(*name)(args) - pointer to function + # r = KernRe(r'^([^\(]+\(\*?\s*)([\w.]*)(\s*\).*)') if r.match(arg): + dtype, name, extra = r.group(1), r.group(2), r.group(3) # Pointer-to-function - dtype = r.group(1) - name = r.group(2) - extra = r.group(3) - - if not name: - continue - if not s_id: # Anonymous struct/union newmember += f"{dtype}{name}{extra}; " else: newmember += f"{dtype}{s_id}.{name}{extra}; " - + # + # Otherwise a non-function member. + # else: - # Handle bitmaps + # + # Remove bitmap and array portions and spaces around commas + # arg = KernRe(r':\s*\d+\s*').sub('', arg) - - # Handle arrays arg = KernRe(r'\[.*\]').sub('', arg) - - # Handle multiple IDs arg = KernRe(r'\s*,\s*').sub(',', arg) - + # + # Look for a normal decl - "type name[,name...]" + # r = KernRe(r'(.*)\s+([\S+,]+)') - if r.search(arg): - dtype = r.group(1) - names = r.group(2) + for name in r.group(2).split(','): + name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name) + if not s_id: + # Anonymous struct/union + newmember += f"{r.group(1)} {name}; " + else: + newmember += f"{r.group(1)} {s_id}.{name}; " else: newmember += f"{arg}; " - continue - - for name in names.split(','): - name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name).strip() - - if not name: - continue - - if not s_id: - # Anonymous struct/union - newmember += f"{dtype} {name}; " - else: - newmember += f"{dtype} {s_id}.{name}; " - + # + # At the end of the s_id loop, replace the original declaration with + # the munged version. + # members = members.replace(oldmember, newmember) + # + # End of the tuple loop - search again and see if there are outer members + # that now turn up. + # tuples = struct_members.findall(members) return members -- cgit v1.2.3 From 23c47b09315935df140ca5ce2ddddb85453ed64d Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:38 -0600 Subject: docs: kdoc: extract output formatting from dump_struct() The last thing done in dump_struct() is to format the structure for printing. That, too, is a separate activity; split it out into its own function. dump_struct() now fits in a single, full-hight editor screen. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-12-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 72 +++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 35 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index b3f937901037..878fbfab4ac7 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -739,6 +739,42 @@ class KernelDoc: tuples = struct_members.findall(members) return members + # + # Format the struct declaration into a standard form for inclusion in the + # resulting docs. + # + def format_struct_decl(self, declaration): + # + # Insert newlines, get rid of extra spaces. + # + declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) + declaration = KernRe(r'\}\s+;').sub('};', declaration) + # + # Format inline enums with each member on its own line. + # + r = KernRe(r'(enum\s+\{[^\}]+),([^\n])') + while r.search(declaration): + declaration = r.sub(r'\1,\n\2', declaration) + # + # Now go through and supply the right number of tabs + # for each line. + # + def_args = declaration.split('\n') + level = 1 + declaration = "" + for clause in def_args: + clause = KernRe(r'\s+').sub(' ', clause.strip(), count=1) + if clause: + if '}' in clause and level > 1: + level -= 1 + if not clause.startswith('#'): + declaration += "\t" * level + declaration += "\t" + clause + "\n" + if "{" in clause and "}" not in clause: + level += 1 + return declaration + + def dump_struct(self, ln, proto): """ Store an entry for an struct or union @@ -777,42 +813,8 @@ class KernelDoc: self.create_parameter_list(ln, decl_type, members, ';', declaration_name) self.check_sections(ln, declaration_name, decl_type) - - # Adjust declaration for better display - declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) - declaration = KernRe(r'\}\s+;').sub('};', declaration) - - # Better handle inlined enums - while True: - r = KernRe(r'(enum\s+\{[^\}]+),([^\n])') - if not r.search(declaration): - break - - declaration = r.sub(r'\1,\n\2', declaration) - - def_args = declaration.split('\n') - level = 1 - declaration = "" - for clause in def_args: - - clause = clause.strip() - clause = KernRe(r'\s+').sub(' ', clause, count=1) - - if not clause: - continue - - if '}' in clause and level > 1: - level -= 1 - - if not KernRe(r'^\s*#').match(clause): - declaration += "\t" * level - - declaration += "\t" + clause + "\n" - if "{" in clause and "}" not in clause: - level += 1 - self.output_declaration(decl_type, declaration_name, - definition=declaration, + definition=self.format_struct_decl(declaration), purpose=self.entry.declaration_purpose) def dump_enum(self, ln, proto): -- cgit v1.2.3 From e282303e718b2007b3db77c6db75ecaf4419a1af Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 7 Aug 2025 15:16:39 -0600 Subject: docs: kdoc: a few final dump_struct() touches Add a couple more comments so that each phase of the process is now clearly marked. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250807211639.47286-13-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 878fbfab4ac7..9b21fb86709a 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -802,14 +802,15 @@ class KernelDoc: nested = NestedMatch() for search, sub in struct_nested_prefixes: members = nested.sub(search, sub, members) - - # Keeps the original declaration as-is + # + # Deal with embedded struct and union members, and drop enums entirely. + # declaration = members members = self.rewrite_struct_members(members) - - # Ignore other nested elements, like enums members = re.sub(r'(\{[^\{\}]*\})', '', members) - + # + # Output the result and we are done. + # self.create_parameter_list(ln, decl_type, members, ';', declaration_name) self.check_sections(ln, declaration_name, decl_type) -- cgit v1.2.3 From fc973dcd73f242480c61eccb1aa7306adafd2907 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 29 Jul 2025 18:43:03 +0200 Subject: docs: kernel-doc: avoid script crash on ancient Python While we do need at least 3.6 for kernel-doc to work, and at least 3.7 for it to output functions and structs with parameters at the right order, let the python binary be compatible with legacy versions. The rationale is that the Kernel build nowadays calls kernel-doc with -none on some places. Better not to bail out when older versions are found. With that, potentially this will run with python 2.7 and 3.2+, according with vermin: $ vermin --no-tips -v ./scripts/kernel-doc Detecting python files.. Analyzing using 24 processes.. 2.7, 3.2 /new_devel/v4l/docs/scripts/kernel-doc Minimum required versions: 2.7, 3.2 3.2 minimal requirement is due to argparse. The minimal version I could check was version 3.4 (using anaconda). Anaconda doesn't support 3.2 or 3.3 anymore, and 3.2 doesn't even compile (I tested compiling Python 3.2 on Fedora 42 and on Fedora 32 - no show). With 3.4, the script didn't crash and emitted the right warning: $ conda create -n py34 python=3.4 $ conda activate py34 python --version Python 3.4.5 $ python ./scripts/kernel-doc --none include/media Error: Python 3.6 or later is required by kernel-doc $ conda deactivate $ python --version Python 3.13.5 $ python ./scripts/kernel-doc --none include/media (no warnings and script ran properly) Supporting 2.7 is out of scope, as it is EOL for 5 years, and changing shebang to point to "python" instead of "python3" would have a wider impact. I did some extra checks about the differences from 3.2 and 3.4, and didn't find anything that would cause troubles: grep -rE "yield from|asyncio|pathlib|async|await|enum" scripts/kernel-doc Also, it doesn't use "@" operator. So, I'm confident that it should run (producing the exit warning) since Python 3.2. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/87d55e76b0b1391cb7a83e3e965dbddb83fa9786.1753806485.git.mchehab+huawei@kernel.org --- scripts/kernel-doc.py | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) (limited to 'scripts') diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index fc3d46ef519f..d9fe2bcbd39c 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -2,8 +2,17 @@ # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2025: Mauro Carvalho Chehab . # -# pylint: disable=C0103,R0915 -# +# pylint: disable=C0103,R0912,R0914,R0915 + +# NOTE: While kernel-doc requires at least version 3.6 to run, the +# command line should work with Python 3.2+ (tested with 3.4). +# The rationale is that it shall fail gracefully during Kernel +# compilation with older Kernel versions. Due to that: +# - encoding line is needed here; +# - no f-strings can be used on this file. +# - the libraries that require newer versions can only be included +# after Python version is checked. + # Converted from the kernel-doc script originally written in Perl # under GPLv2, copyrighted since 1998 by the following authors: # @@ -107,9 +116,6 @@ SRC_DIR = os.path.dirname(os.path.realpath(__file__)) sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) -from kdoc_files import KernelFiles # pylint: disable=C0413 -from kdoc_output import RestFormat, ManFormat # pylint: disable=C0413 - DESC = """ Read C language source or header FILEs, extract embedded documentation comments, and print formatted documentation to standard output. @@ -273,14 +279,22 @@ def main(): python_ver = sys.version_info[:2] if python_ver < (3,6): - logger.warning("Python 3.6 or later is required by kernel-doc") + # Depending on Kernel configuration, kernel-doc --none is called at + # build time. As we don't want to break compilation due to the + # usage of an old Python version, return 0 here. + if args.none: + logger.error("Python 3.6 or later is required by kernel-doc. skipping checks") + sys.exit(0) - # Return 0 here to avoid breaking compilation - sys.exit(0) + sys.exit("Python 3.6 or later is required by kernel-doc. Aborting.") if python_ver < (3,7): logger.warning("Python 3.7 or later is required for correct results") + # Import kernel-doc libraries only after checking Python version + from kdoc_files import KernelFiles # pylint: disable=C0415 + from kdoc_output import RestFormat, ManFormat # pylint: disable=C0415 + if args.man: out_style = ManFormat(modulename=args.modulename) elif args.none: @@ -308,11 +322,11 @@ def main(): sys.exit(0) if args.werror: - print(f"{error_count} warnings as errors") + print("%s warnings as errors" % error_count) # pylint: disable=C0209 sys.exit(error_count) if args.verbose: - print(f"{error_count} errors") + print("%s errors" % error_count) # pylint: disable=C0209 if args.none: sys.exit(0) -- cgit v1.2.3 From 7bb184222e9fa8b839247c3a1cb1bcf628e519a5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:18 +0200 Subject: scripts: sphinx-pre-install: fix version check for Fedora The script is now picking the wrong version. Fix it. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/9d1e5c9906534e2bae586f891770066346463146.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index 3f8d6925e896..07234d482fa8 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -413,7 +413,7 @@ sub give_redhat_hints() my $old = 0; my $rel; my $noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts"; - $rel = $1 if ($system_release =~ /(release|Linux)\s+(\d+)/); + $rel = $2 if ($system_release =~ /(release|Linux)\s+(\d+)/); if (!($system_release =~ /Fedora/)) { $map{"virtualenv"} = "python-virtualenv"; -- cgit v1.2.3 From a9c50a51e0129057dbaf58fbaf7ba1a8a69e9b21 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:19 +0200 Subject: scripts: sphinx-pre-install: rename it to scripts/sphinx-pre-install.pl That helps us to later replace the scripts. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/08d9a32a5aaf3784fef016594efe505d7c5a2697.1754992972.git.mchehab+huawei@kernel.org --- Documentation/Makefile | 14 +- scripts/sphinx-pre-install | 1056 ----------------------------------------- scripts/sphinx-pre-install.pl | 1056 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1063 insertions(+), 1063 deletions(-) delete mode 100755 scripts/sphinx-pre-install create mode 100755 scripts/sphinx-pre-install.pl (limited to 'scripts') diff --git a/Documentation/Makefile b/Documentation/Makefile index b98477df5ddf..c486fe3cc5e1 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -46,7 +46,7 @@ ifeq ($(HAVE_SPHINX),0) .DEFAULT: $(warning The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed and in PATH, or set the SPHINXBUILD make variable to point to the full path of the '$(SPHINXBUILD)' executable.) @echo - @$(srctree)/scripts/sphinx-pre-install + @$(srctree)/scripts/sphinx-pre-install.pl @echo " SKIP Sphinx $@ target." else # HAVE_SPHINX @@ -121,7 +121,7 @@ $(YNL_RST_DIR)/%.rst: $(YNL_YAML_DIR)/%.yaml $(YNL_TOOL) htmldocs texinfodocs latexdocs epubdocs xmldocs: $(YNL_INDEX) htmldocs: - @$(srctree)/scripts/sphinx-pre-install --version-check + @$(srctree)/scripts/sphinx-pre-install.pl --version-check @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,html,$(var),,$(var))) # If Rust support is available and .config exists, add rustdoc generated contents. @@ -135,7 +135,7 @@ endif endif texinfodocs: - @$(srctree)/scripts/sphinx-pre-install --version-check + @$(srctree)/scripts/sphinx-pre-install.pl --version-check @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,texinfo,$(var),texinfo,$(var))) # Note: the 'info' Make target is generated by sphinx itself when @@ -147,7 +147,7 @@ linkcheckdocs: @$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,linkcheck,$(var),,$(var))) latexdocs: - @$(srctree)/scripts/sphinx-pre-install --version-check + @$(srctree)/scripts/sphinx-pre-install.pl --version-check @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,latex,$(var),latex,$(var))) ifeq ($(HAVE_PDFLATEX),0) @@ -160,7 +160,7 @@ else # HAVE_PDFLATEX pdfdocs: DENY_VF = XDG_CONFIG_HOME=$(FONTS_CONF_DENY_VF) pdfdocs: latexdocs - @$(srctree)/scripts/sphinx-pre-install --version-check + @$(srctree)/scripts/sphinx-pre-install.pl --version-check $(foreach var,$(SPHINXDIRS), \ $(MAKE) PDFLATEX="$(PDFLATEX)" LATEXOPTS="$(LATEXOPTS)" $(DENY_VF) -C $(BUILDDIR)/$(var)/latex || sh $(srctree)/scripts/check-variable-fonts.sh || exit; \ mkdir -p $(BUILDDIR)/$(var)/pdf; \ @@ -170,11 +170,11 @@ pdfdocs: latexdocs endif # HAVE_PDFLATEX epubdocs: - @$(srctree)/scripts/sphinx-pre-install --version-check + @$(srctree)/scripts/sphinx-pre-install.pl --version-check @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,epub,$(var),epub,$(var))) xmldocs: - @$(srctree)/scripts/sphinx-pre-install --version-check + @$(srctree)/scripts/sphinx-pre-install.pl --version-check @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,xml,$(var),xml,$(var))) endif # HAVE_SPHINX diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install deleted file mode 100755 index 07234d482fa8..000000000000 --- a/scripts/sphinx-pre-install +++ /dev/null @@ -1,1056 +0,0 @@ -#!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0-or-later -use strict; - -# Copyright (c) 2017-2020 Mauro Carvalho Chehab -# - -my $prefix = "./"; -$prefix = "$ENV{'srctree'}/" if ($ENV{'srctree'}); - -my $conf = $prefix . "Documentation/conf.py"; -my $requirement_file = $prefix . "Documentation/sphinx/requirements.txt"; -my $virtenv_prefix = "sphinx_"; - -# -# Static vars -# - -my %missing; -my $system_release; -my $need = 0; -my $optional = 0; -my $need_symlink = 0; -my $need_sphinx = 0; -my $need_pip = 0; -my $need_virtualenv = 0; -my $rec_sphinx_upgrade = 0; -my $verbose_warn_install = 1; -my $install = ""; -my $virtenv_dir = ""; -my $python_cmd = ""; -my $activate_cmd; -my $min_version; -my $cur_version; -my $rec_version = "3.4.3"; -my $latest_avail_ver; - -# -# Command line arguments -# - -my $pdf = 1; -my $virtualenv = 1; -my $version_check = 0; - -# -# List of required texlive packages on Fedora and OpenSuse -# - -my %texlive = ( - 'amsfonts.sty' => 'texlive-amsfonts', - 'amsmath.sty' => 'texlive-amsmath', - 'amssymb.sty' => 'texlive-amsfonts', - 'amsthm.sty' => 'texlive-amscls', - 'anyfontsize.sty' => 'texlive-anyfontsize', - 'atbegshi.sty' => 'texlive-oberdiek', - 'bm.sty' => 'texlive-tools', - 'capt-of.sty' => 'texlive-capt-of', - 'cmap.sty' => 'texlive-cmap', - 'ecrm1000.tfm' => 'texlive-ec', - 'eqparbox.sty' => 'texlive-eqparbox', - 'eu1enc.def' => 'texlive-euenc', - 'fancybox.sty' => 'texlive-fancybox', - 'fancyvrb.sty' => 'texlive-fancyvrb', - 'float.sty' => 'texlive-float', - 'fncychap.sty' => 'texlive-fncychap', - 'footnote.sty' => 'texlive-mdwtools', - 'framed.sty' => 'texlive-framed', - 'luatex85.sty' => 'texlive-luatex85', - 'multirow.sty' => 'texlive-multirow', - 'needspace.sty' => 'texlive-needspace', - 'palatino.sty' => 'texlive-psnfss', - 'parskip.sty' => 'texlive-parskip', - 'polyglossia.sty' => 'texlive-polyglossia', - 'tabulary.sty' => 'texlive-tabulary', - 'threeparttable.sty' => 'texlive-threeparttable', - 'titlesec.sty' => 'texlive-titlesec', - 'ucs.sty' => 'texlive-ucs', - 'upquote.sty' => 'texlive-upquote', - 'wrapfig.sty' => 'texlive-wrapfig', - 'ctexhook.sty' => 'texlive-ctex', -); - -# -# Subroutines that checks if a feature exists -# - -sub check_missing(%) -{ - my %map = %{$_[0]}; - - foreach my $prog (sort keys %missing) { - my $is_optional = $missing{$prog}; - - # At least on some LTS distros like CentOS 7, texlive doesn't - # provide all packages we need. When such distros are - # detected, we have to disable PDF output. - # - # So, we need to ignore the packages that distros would - # need for LaTeX to work - if ($is_optional == 2 && !$pdf) { - $optional--; - next; - } - - if ($verbose_warn_install) { - if ($is_optional) { - print "Warning: better to also install \"$prog\".\n"; - } else { - print "ERROR: please install \"$prog\", otherwise, build won't work.\n"; - } - } - if (defined($map{$prog})) { - $install .= " " . $map{$prog}; - } else { - $install .= " " . $prog; - } - } - - $install =~ s/^\s//; -} - -sub add_package($$) -{ - my $package = shift; - my $is_optional = shift; - - $missing{$package} = $is_optional; - if ($is_optional) { - $optional++; - } else { - $need++; - } -} - -sub check_missing_file($$$) -{ - my $files = shift; - my $package = shift; - my $is_optional = shift; - - for (@$files) { - return if(-e $_); - } - - add_package($package, $is_optional); -} - -sub findprog($) -{ - foreach(split(/:/, $ENV{PATH})) { - return "$_/$_[0]" if(-x "$_/$_[0]"); - } -} - -sub find_python_no_venv() -{ - my $prog = shift; - - my $cur_dir = qx(pwd); - $cur_dir =~ s/\s+$//; - - foreach my $dir (split(/:/, $ENV{PATH})) { - next if ($dir =~ m,($cur_dir)/sphinx,); - return "$dir/python3" if(-x "$dir/python3"); - } - foreach my $dir (split(/:/, $ENV{PATH})) { - next if ($dir =~ m,($cur_dir)/sphinx,); - return "$dir/python" if(-x "$dir/python"); - } - return "python"; -} - -sub check_program($$) -{ - my $prog = shift; - my $is_optional = shift; - - return $prog if findprog($prog); - - add_package($prog, $is_optional); -} - -sub check_perl_module($$) -{ - my $prog = shift; - my $is_optional = shift; - - my $err = system("perl -M$prog -e 1 2>/dev/null /dev/null"); - return if ($err == 0); - - add_package($prog, $is_optional); -} - -sub check_python_module($$) -{ - my $prog = shift; - my $is_optional = shift; - - return if (!$python_cmd); - - my $err = system("$python_cmd -c 'import $prog' 2>/dev/null /dev/null"); - return if ($err == 0); - - add_package($prog, $is_optional); -} - -sub check_rpm_missing($$) -{ - my @pkgs = @{$_[0]}; - my $is_optional = $_[1]; - - foreach my $prog(@pkgs) { - my $err = system("rpm -q '$prog' 2>/dev/null >/dev/null"); - add_package($prog, $is_optional) if ($err); - } -} - -sub check_pacman_missing($$) -{ - my @pkgs = @{$_[0]}; - my $is_optional = $_[1]; - - foreach my $prog(@pkgs) { - my $err = system("pacman -Q '$prog' 2>/dev/null >/dev/null"); - add_package($prog, $is_optional) if ($err); - } -} - -sub check_missing_tex($) -{ - my $is_optional = shift; - my $kpsewhich = findprog("kpsewhich"); - - foreach my $prog(keys %texlive) { - my $package = $texlive{$prog}; - if (!$kpsewhich) { - add_package($package, $is_optional); - next; - } - my $file = qx($kpsewhich $prog); - add_package($package, $is_optional) if ($file =~ /^\s*$/); - } -} - -sub get_sphinx_fname() -{ - if ($ENV{'SPHINXBUILD'}) { - return $ENV{'SPHINXBUILD'}; - } - - my $fname = "sphinx-build"; - return $fname if findprog($fname); - - $fname = "sphinx-build-3"; - if (findprog($fname)) { - $need_symlink = 1; - return $fname; - } - - return ""; -} - -sub get_sphinx_version($) -{ - my $cmd = shift; - my $ver; - - open IN, "$cmd --version 2>&1 |"; - while () { - if (m/^\s*sphinx-build\s+([\d\.]+)((\+\/[\da-f]+)|(b\d+))?$/) { - $ver=$1; - last; - } - # Sphinx 1.2.x uses a different format - if (m/^\s*Sphinx.*\s+([\d\.]+)$/) { - $ver=$1; - last; - } - } - close IN; - return $ver; -} - -sub check_sphinx() -{ - open IN, $conf or die "Can't open $conf"; - while () { - if (m/^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]/) { - $min_version=$1; - last; - } - } - close IN; - - die "Can't get needs_sphinx version from $conf" if (!$min_version); - - $virtenv_dir = $virtenv_prefix . "latest"; - - my $sphinx = get_sphinx_fname(); - if ($sphinx eq "") { - $need_sphinx = 1; - return; - } - - $cur_version = get_sphinx_version($sphinx); - die "$sphinx didn't return its version" if (!$cur_version); - - if ($cur_version lt $min_version) { - printf "ERROR: Sphinx version is %s. It should be >= %s\n", - $cur_version, $min_version; - $need_sphinx = 1; - return; - } - - return if ($cur_version lt $rec_version); - - # On version check mode, just assume Sphinx has all mandatory deps - exit (0) if ($version_check); -} - -# -# Ancillary subroutines -# - -sub catcheck($) -{ - my $res = ""; - $res = qx(cat $_[0]) if (-r $_[0]); - return $res; -} - -sub which($) -{ - my $file = shift; - my @path = split ":", $ENV{PATH}; - - foreach my $dir(@path) { - my $name = $dir.'/'.$file; - return $name if (-x $name ); - } - return undef; -} - -# -# Subroutines that check distro-specific hints -# - -sub give_debian_hints() -{ - my %map = ( - "python-sphinx" => "python3-sphinx", - "yaml" => "python3-yaml", - "ensurepip" => "python3-venv", - "virtualenv" => "virtualenv", - "dot" => "graphviz", - "convert" => "imagemagick", - "Pod::Usage" => "perl-modules", - "xelatex" => "texlive-xetex", - "rsvg-convert" => "librsvg2-bin", - ); - - if ($pdf) { - check_missing_file(["/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty"], - "texlive-lang-chinese", 2); - - check_missing_file(["/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"], - "fonts-dejavu", 2); - - check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc"], - "fonts-noto-cjk", 2); - } - - check_program("dvipng", 2) if ($pdf); - check_missing(\%map); - - return if (!$need && !$optional); - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo apt-get install $install\n"); -} - -sub give_redhat_hints() -{ - my %map = ( - "python-sphinx" => "python3-sphinx", - "yaml" => "python3-pyyaml", - "virtualenv" => "python3-virtualenv", - "dot" => "graphviz", - "convert" => "ImageMagick", - "Pod::Usage" => "perl-Pod-Usage", - "xelatex" => "texlive-xetex-bin", - "rsvg-convert" => "librsvg2-tools", - ); - - my @fedora26_opt_pkgs = ( - "graphviz-gd", # Fedora 26: needed for PDF support - ); - - my @fedora_tex_pkgs = ( - "texlive-collection-fontsrecommended", - "texlive-collection-latex", - "texlive-xecjk", - "dejavu-sans-fonts", - "dejavu-serif-fonts", - "dejavu-sans-mono-fonts", - ); - - # - # Checks valid for RHEL/CentOS version 7.x. - # - my $old = 0; - my $rel; - my $noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts"; - $rel = $2 if ($system_release =~ /(release|Linux)\s+(\d+)/); - - if (!($system_release =~ /Fedora/)) { - $map{"virtualenv"} = "python-virtualenv"; - - if ($rel && $rel < 8) { - $old = 1; - $pdf = 0; - - printf("Note: texlive packages on RHEL/CENTOS <= 7 are incomplete. Can't support PDF output\n"); - printf("If you want to build PDF, please read:\n"); - printf("\thttps://www.systutorials.com/241660/how-to-install-tex-live-on-centos-7-linux/\n"); - } - } else { - if ($rel && $rel < 26) { - $old = 1; - } - if ($rel && $rel >= 38) { - $noto_sans_redhat = "google-noto-sans-cjk-fonts"; - } - } - if (!$rel) { - printf("Couldn't identify release number\n"); - $old = 1; - $pdf = 0; - } - - if ($pdf) { - check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc"], - $noto_sans_redhat, 2); - } - - check_rpm_missing(\@fedora26_opt_pkgs, 2) if ($pdf && !$old); - check_rpm_missing(\@fedora_tex_pkgs, 2) if ($pdf); - check_missing_tex(2) if ($pdf); - check_missing(\%map); - - return if (!$need && !$optional); - - if (!$old) { - # dnf, for Fedora 18+ - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo dnf install -y $install\n"); - } else { - # yum, for RHEL (and clones) or Fedora version < 18 - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo yum install -y $install\n"); - } -} - -sub give_opensuse_hints() -{ - my %map = ( - "python-sphinx" => "python3-sphinx", - "yaml" => "python3-pyyaml", - "virtualenv" => "python3-virtualenv", - "dot" => "graphviz", - "convert" => "ImageMagick", - "Pod::Usage" => "perl-Pod-Usage", - "xelatex" => "texlive-xetex-bin", - ); - - # On Tumbleweed, this package is also named rsvg-convert - $map{"rsvg-convert"} = "rsvg-view" if (!($system_release =~ /Tumbleweed/)); - - my @suse_tex_pkgs = ( - "texlive-babel-english", - "texlive-caption", - "texlive-colortbl", - "texlive-courier", - "texlive-dvips", - "texlive-helvetic", - "texlive-makeindex", - "texlive-metafont", - "texlive-metapost", - "texlive-palatino", - "texlive-preview", - "texlive-times", - "texlive-zapfchan", - "texlive-zapfding", - ); - - $map{"latexmk"} = "texlive-latexmk-bin"; - - # FIXME: add support for installing CJK fonts - # - # I tried hard, but was unable to find a way to install - # "Noto Sans CJK SC" on openSUSE - - check_rpm_missing(\@suse_tex_pkgs, 2) if ($pdf); - check_missing_tex(2) if ($pdf); - check_missing(\%map); - - return if (!$need && !$optional); - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo zypper install --no-recommends $install\n"); -} - -sub give_mageia_hints() -{ - my %map = ( - "python-sphinx" => "python3-sphinx", - "yaml" => "python3-yaml", - "virtualenv" => "python3-virtualenv", - "dot" => "graphviz", - "convert" => "ImageMagick", - "Pod::Usage" => "perl-Pod-Usage", - "xelatex" => "texlive", - "rsvg-convert" => "librsvg2", - ); - - my @tex_pkgs = ( - "texlive-fontsextra", - ); - - $map{"latexmk"} = "texlive-collection-basic"; - - my $packager_cmd; - my $noto_sans; - if ($system_release =~ /OpenMandriva/) { - $packager_cmd = "dnf install"; - $noto_sans = "noto-sans-cjk-fonts"; - @tex_pkgs = ( "texlive-collection-fontsextra" ); - } else { - $packager_cmd = "urpmi"; - $noto_sans = "google-noto-sans-cjk-ttc-fonts"; - } - - - if ($pdf) { - check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/TTF/NotoSans-Regular.ttf"], - $noto_sans, 2); - } - - check_rpm_missing(\@tex_pkgs, 2) if ($pdf); - check_missing(\%map); - - return if (!$need && !$optional); - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo $packager_cmd $install\n"); -} - -sub give_arch_linux_hints() -{ - my %map = ( - "yaml" => "python-yaml", - "virtualenv" => "python-virtualenv", - "dot" => "graphviz", - "convert" => "imagemagick", - "xelatex" => "texlive-xetex", - "latexmk" => "texlive-core", - "rsvg-convert" => "extra/librsvg", - ); - - my @archlinux_tex_pkgs = ( - "texlive-core", - "texlive-latexextra", - "ttf-dejavu", - ); - check_pacman_missing(\@archlinux_tex_pkgs, 2) if ($pdf); - - if ($pdf) { - check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], - "noto-fonts-cjk", 2); - } - - check_missing(\%map); - - return if (!$need && !$optional); - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo pacman -S $install\n"); -} - -sub give_gentoo_hints() -{ - my %map = ( - "yaml" => "dev-python/pyyaml", - "virtualenv" => "dev-python/virtualenv", - "dot" => "media-gfx/graphviz", - "convert" => "media-gfx/imagemagick", - "xelatex" => "dev-texlive/texlive-xetex media-fonts/dejavu", - "rsvg-convert" => "gnome-base/librsvg", - ); - - check_missing_file(["/usr/share/fonts/dejavu/DejaVuSans.ttf"], - "media-fonts/dejavu", 2) if ($pdf); - - if ($pdf) { - check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf", - "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc"], - "media-fonts/noto-cjk", 2); - } - - check_missing(\%map); - - return if (!$need && !$optional); - - printf("You should run:\n") if ($verbose_warn_install); - printf("\n"); - - my $imagemagick = "media-gfx/imagemagick svg png"; - my $cairo = "media-gfx/graphviz cairo pdf"; - my $portage_imagemagick = "/etc/portage/package.use/imagemagick"; - my $portage_cairo = "/etc/portage/package.use/graphviz"; - - if (qx(grep imagemagick $portage_imagemagick 2>/dev/null) eq "") { - printf("\tsudo su -c 'echo \"$imagemagick\" > $portage_imagemagick'\n") - } - if (qx(grep graphviz $portage_cairo 2>/dev/null) eq "") { - printf("\tsudo su -c 'echo \"$cairo\" > $portage_cairo'\n"); - } - - printf("\tsudo emerge --ask $install\n"); - -} - -sub check_distros() -{ - # Distro-specific hints - if ($system_release =~ /Red Hat Enterprise Linux/) { - give_redhat_hints; - return; - } - if ($system_release =~ /CentOS/) { - give_redhat_hints; - return; - } - if ($system_release =~ /Scientific Linux/) { - give_redhat_hints; - return; - } - if ($system_release =~ /Oracle Linux Server/) { - give_redhat_hints; - return; - } - if ($system_release =~ /Fedora/) { - give_redhat_hints; - return; - } - if ($system_release =~ /Ubuntu/) { - give_debian_hints; - return; - } - if ($system_release =~ /Debian/) { - give_debian_hints; - return; - } - if ($system_release =~ /openSUSE/) { - give_opensuse_hints; - return; - } - if ($system_release =~ /Mageia/) { - give_mageia_hints; - return; - } - if ($system_release =~ /OpenMandriva/) { - give_mageia_hints; - return; - } - if ($system_release =~ /Arch Linux/) { - give_arch_linux_hints; - return; - } - if ($system_release =~ /Gentoo/) { - give_gentoo_hints; - return; - } - - # - # Fall-back to generic hint code for other distros - # That's far from ideal, specially for LaTeX dependencies. - # - my %map = ( - "sphinx-build" => "sphinx" - ); - check_missing_tex(2) if ($pdf); - check_missing(\%map); - print "I don't know distro $system_release.\n"; - print "So, I can't provide you a hint with the install procedure.\n"; - print "There are likely missing dependencies.\n"; -} - -# -# Common dependencies -# - -sub deactivate_help() -{ - printf "\n If you want to exit the virtualenv, you can use:\n"; - printf "\tdeactivate\n"; -} - -sub get_virtenv() -{ - my $ver; - my $min_activate = "$ENV{'PWD'}/${virtenv_prefix}${min_version}/bin/activate"; - my @activates = glob "$ENV{'PWD'}/${virtenv_prefix}*/bin/activate"; - - @activates = sort {$b cmp $a} @activates; - - foreach my $f (@activates) { - next if ($f lt $min_activate); - - my $sphinx_cmd = $f; - $sphinx_cmd =~ s/activate/sphinx-build/; - next if (! -f $sphinx_cmd); - - my $ver = get_sphinx_version($sphinx_cmd); - - if (!$ver) { - $f =~ s#/bin/activate##; - print("Warning: virtual environment $f is not working.\nPython version upgrade? Remove it with:\n\n\trm -rf $f\n\n"); - } - - if ($need_sphinx && ($ver ge $min_version)) { - return ($f, $ver); - } elsif ($ver gt $cur_version) { - return ($f, $ver); - } - } - return ("", ""); -} - -sub recommend_sphinx_upgrade() -{ - my $venv_ver; - - # Avoid running sphinx-builds from venv if $cur_version is good - if ($cur_version && ($cur_version ge $rec_version)) { - $latest_avail_ver = $cur_version; - return; - } - - # Get the highest version from sphinx_*/bin/sphinx-build and the - # corresponding command to activate the venv/virtenv - ($activate_cmd, $venv_ver) = get_virtenv(); - - # Store the highest version from Sphinx existing virtualenvs - if (($activate_cmd ne "") && ($venv_ver gt $cur_version)) { - $latest_avail_ver = $venv_ver; - } else { - $latest_avail_ver = $cur_version if ($cur_version); - } - - # As we don't know package version of Sphinx, and there's no - # virtual environments, don't check if upgrades are needed - if (!$virtualenv) { - return if (!$latest_avail_ver); - } - - # Either there are already a virtual env or a new one should be created - $need_pip = 1; - - return if (!$latest_avail_ver); - - # Return if the reason is due to an upgrade or not - if ($latest_avail_ver lt $rec_version) { - $rec_sphinx_upgrade = 1; - } - - return $latest_avail_ver; -} - -# -# The logic here is complex, as it have to deal with different versions: -# - minimal supported version; -# - minimal PDF version; -# - recommended version. -# It also needs to work fine with both distro's package and venv/virtualenv -sub recommend_sphinx_version($) -{ - my $virtualenv_cmd = shift; - - # Version is OK. Nothing to do. - if ($cur_version && ($cur_version ge $rec_version)) { - return; - }; - - if (!$need_sphinx) { - # sphinx-build is present and its version is >= $min_version - - #only recommend enabling a newer virtenv version if makes sense. - if ($latest_avail_ver gt $cur_version) { - printf "\nYou may also use the newer Sphinx version $latest_avail_ver with:\n"; - printf "\tdeactivate\n" if ($ENV{'PWD'} =~ /${virtenv_prefix}/); - printf "\t. $activate_cmd\n"; - deactivate_help(); - - return; - } - return if ($latest_avail_ver ge $rec_version); - } - - if (!$virtualenv) { - # No sphinx either via package or via virtenv. As we can't - # Compare the versions here, just return, recommending the - # user to install it from the package distro. - return if (!$latest_avail_ver); - - # User doesn't want a virtenv recommendation, but he already - # installed one via virtenv with a newer version. - # So, print commands to enable it - if ($latest_avail_ver gt $cur_version) { - printf "\nYou may also use the Sphinx virtualenv version $latest_avail_ver with:\n"; - printf "\tdeactivate\n" if ($ENV{'PWD'} =~ /${virtenv_prefix}/); - printf "\t. $activate_cmd\n"; - deactivate_help(); - - return; - } - print "\n"; - } else { - $need++ if ($need_sphinx); - } - - # Suggest newer versions if current ones are too old - if ($latest_avail_ver && $latest_avail_ver ge $min_version) { - # If there's a good enough version, ask the user to enable it - if ($latest_avail_ver ge $rec_version) { - printf "\nNeed to activate Sphinx (version $latest_avail_ver) on virtualenv with:\n"; - printf "\t. $activate_cmd\n"; - deactivate_help(); - - return; - } - - # Version is above the minimal required one, but may be - # below the recommended one. So, print warnings/notes - - if ($latest_avail_ver lt $rec_version) { - print "Warning: It is recommended at least Sphinx version $rec_version.\n"; - } - } - - # At this point, either it needs Sphinx or upgrade is recommended, - # both via pip - - if ($rec_sphinx_upgrade) { - if (!$virtualenv) { - print "Instead of install/upgrade Python Sphinx pkg, you could use pip/pypi with:\n\n"; - } else { - print "To upgrade Sphinx, use:\n\n"; - } - } else { - print "\nSphinx needs to be installed either:\n1) via pip/pypi with:\n\n"; - } - - $python_cmd = find_python_no_venv(); - - printf "\t$virtualenv_cmd $virtenv_dir\n"; - - printf "\t. $virtenv_dir/bin/activate\n"; - printf "\tpip install -r $requirement_file\n"; - deactivate_help(); - - printf "\n2) As a package with:\n"; - - my $old_need = $need; - my $old_optional = $optional; - %missing = (); - $pdf = 0; - $optional = 0; - $install = ""; - $verbose_warn_install = 0; - - add_package("python-sphinx", 0); - - check_distros(); - - $need = $old_need; - $optional = $old_optional; - - printf "\n Please note that Sphinx >= 3.0 will currently produce false-positive\n"; - printf " warning when the same name is used for more than one type (functions,\n"; - printf " structs, enums,...). This is known Sphinx bug. For more details, see:\n"; - printf "\thttps://github.com/sphinx-doc/sphinx/pull/8313\n"; -} - -sub check_needs() -{ - # Check if Sphinx is already accessible from current environment - check_sphinx(); - - if ($system_release) { - print "Detected OS: $system_release.\n"; - } else { - print "Unknown OS\n"; - } - printf "Sphinx version: %s\n\n", $cur_version if ($cur_version); - - # Check python command line, trying first python3 - $python_cmd = findprog("python3"); - $python_cmd = check_program("python", 0) if (!$python_cmd); - - # Check the type of virtual env, depending on Python version - if ($python_cmd) { - if ($virtualenv) { - my $tmp = qx($python_cmd --version 2>&1); - if ($tmp =~ m/(\d+\.)(\d+\.)/) { - if ($1 < 3) { - # Fail if it finds python2 (or worse) - die "Python 3 is required to build the kernel docs\n"; - } - if ($1 == 3 && $2 < 3) { - # Need Python 3.3 or upper for venv - $need_virtualenv = 1; - } - } else { - die "Warning: couldn't identify $python_cmd version!"; - } - } else { - add_package("python-sphinx", 0); - } - } - - my $venv_ver = recommend_sphinx_upgrade(); - - my $virtualenv_cmd; - - if ($need_pip) { - # Set virtualenv command line, if python < 3.3 - if ($need_virtualenv) { - $virtualenv_cmd = findprog("virtualenv-3"); - $virtualenv_cmd = findprog("virtualenv-3.5") if (!$virtualenv_cmd); - if (!$virtualenv_cmd) { - check_program("virtualenv", 0); - $virtualenv_cmd = "virtualenv"; - } - } else { - $virtualenv_cmd = "$python_cmd -m venv"; - check_python_module("ensurepip", 0); - } - } - - # Check for needed programs/tools - check_perl_module("Pod::Usage", 0); - check_python_module("yaml", 0); - check_program("make", 0); - check_program("gcc", 0); - check_program("dot", 1); - check_program("convert", 1); - - # Extra PDF files - should use 2 for is_optional - check_program("xelatex", 2) if ($pdf); - check_program("rsvg-convert", 2) if ($pdf); - check_program("latexmk", 2) if ($pdf); - - # Do distro-specific checks and output distro-install commands - check_distros(); - - if (!$python_cmd) { - if ($need == 1) { - die "Can't build as $need mandatory dependency is missing"; - } elsif ($need) { - die "Can't build as $need mandatory dependencies are missing"; - } - } - - # Check if sphinx-build is called sphinx-build-3 - if ($need_symlink) { - printf "\tsudo ln -sf %s /usr/bin/sphinx-build\n\n", - which("sphinx-build-3"); - } - - recommend_sphinx_version($virtualenv_cmd); - printf "\n"; - - print "All optional dependencies are met.\n" if (!$optional); - - if ($need == 1) { - die "Can't build as $need mandatory dependency is missing"; - } elsif ($need) { - die "Can't build as $need mandatory dependencies are missing"; - } - - print "Needed package dependencies are met.\n"; -} - -# -# Main -# - -while (@ARGV) { - my $arg = shift(@ARGV); - - if ($arg eq "--no-virtualenv") { - $virtualenv = 0; - } elsif ($arg eq "--no-pdf"){ - $pdf = 0; - } elsif ($arg eq "--version-check"){ - $version_check = 1; - } else { - print "Usage:\n\t$0 <--no-virtualenv> <--no-pdf> <--version-check>\n\n"; - print "Where:\n"; - print "\t--no-virtualenv\t- Recommend installing Sphinx instead of using a virtualenv\n"; - print "\t--version-check\t- if version is compatible, don't check for missing dependencies\n"; - print "\t--no-pdf\t- don't check for dependencies required to build PDF docs\n\n"; - exit -1; - } -} - -# -# Determine the system type. There's no standard unique way that would -# work with all distros with a minimal package install. So, several -# methods are used here. -# -# By default, it will use lsb_release function. If not available, it will -# fail back to reading the known different places where the distro name -# is stored -# - -$system_release = qx(lsb_release -d) if which("lsb_release"); -$system_release =~ s/Description:\s*// if ($system_release); -$system_release = catcheck("/etc/system-release") if !$system_release; -$system_release = catcheck("/etc/redhat-release") if !$system_release; -$system_release = catcheck("/etc/lsb-release") if !$system_release; -$system_release = catcheck("/etc/gentoo-release") if !$system_release; - -# This seems more common than LSB these days -if (!$system_release) { - my %os_var; - if (open IN, "cat /etc/os-release|") { - while () { - if (m/^([\w\d\_]+)=\"?([^\"]*)\"?\n/) { - $os_var{$1}=$2; - } - } - $system_release = $os_var{"NAME"}; - if (defined($os_var{"VERSION_ID"})) { - $system_release .= " " . $os_var{"VERSION_ID"} if (defined($os_var{"VERSION_ID"})); - } else { - $system_release .= " " . $os_var{"VERSION"}; - } - } -} -$system_release = catcheck("/etc/issue") if !$system_release; -$system_release =~ s/\s+$//; - -check_needs; diff --git a/scripts/sphinx-pre-install.pl b/scripts/sphinx-pre-install.pl new file mode 100755 index 000000000000..07234d482fa8 --- /dev/null +++ b/scripts/sphinx-pre-install.pl @@ -0,0 +1,1056 @@ +#!/usr/bin/env perl +# SPDX-License-Identifier: GPL-2.0-or-later +use strict; + +# Copyright (c) 2017-2020 Mauro Carvalho Chehab +# + +my $prefix = "./"; +$prefix = "$ENV{'srctree'}/" if ($ENV{'srctree'}); + +my $conf = $prefix . "Documentation/conf.py"; +my $requirement_file = $prefix . "Documentation/sphinx/requirements.txt"; +my $virtenv_prefix = "sphinx_"; + +# +# Static vars +# + +my %missing; +my $system_release; +my $need = 0; +my $optional = 0; +my $need_symlink = 0; +my $need_sphinx = 0; +my $need_pip = 0; +my $need_virtualenv = 0; +my $rec_sphinx_upgrade = 0; +my $verbose_warn_install = 1; +my $install = ""; +my $virtenv_dir = ""; +my $python_cmd = ""; +my $activate_cmd; +my $min_version; +my $cur_version; +my $rec_version = "3.4.3"; +my $latest_avail_ver; + +# +# Command line arguments +# + +my $pdf = 1; +my $virtualenv = 1; +my $version_check = 0; + +# +# List of required texlive packages on Fedora and OpenSuse +# + +my %texlive = ( + 'amsfonts.sty' => 'texlive-amsfonts', + 'amsmath.sty' => 'texlive-amsmath', + 'amssymb.sty' => 'texlive-amsfonts', + 'amsthm.sty' => 'texlive-amscls', + 'anyfontsize.sty' => 'texlive-anyfontsize', + 'atbegshi.sty' => 'texlive-oberdiek', + 'bm.sty' => 'texlive-tools', + 'capt-of.sty' => 'texlive-capt-of', + 'cmap.sty' => 'texlive-cmap', + 'ecrm1000.tfm' => 'texlive-ec', + 'eqparbox.sty' => 'texlive-eqparbox', + 'eu1enc.def' => 'texlive-euenc', + 'fancybox.sty' => 'texlive-fancybox', + 'fancyvrb.sty' => 'texlive-fancyvrb', + 'float.sty' => 'texlive-float', + 'fncychap.sty' => 'texlive-fncychap', + 'footnote.sty' => 'texlive-mdwtools', + 'framed.sty' => 'texlive-framed', + 'luatex85.sty' => 'texlive-luatex85', + 'multirow.sty' => 'texlive-multirow', + 'needspace.sty' => 'texlive-needspace', + 'palatino.sty' => 'texlive-psnfss', + 'parskip.sty' => 'texlive-parskip', + 'polyglossia.sty' => 'texlive-polyglossia', + 'tabulary.sty' => 'texlive-tabulary', + 'threeparttable.sty' => 'texlive-threeparttable', + 'titlesec.sty' => 'texlive-titlesec', + 'ucs.sty' => 'texlive-ucs', + 'upquote.sty' => 'texlive-upquote', + 'wrapfig.sty' => 'texlive-wrapfig', + 'ctexhook.sty' => 'texlive-ctex', +); + +# +# Subroutines that checks if a feature exists +# + +sub check_missing(%) +{ + my %map = %{$_[0]}; + + foreach my $prog (sort keys %missing) { + my $is_optional = $missing{$prog}; + + # At least on some LTS distros like CentOS 7, texlive doesn't + # provide all packages we need. When such distros are + # detected, we have to disable PDF output. + # + # So, we need to ignore the packages that distros would + # need for LaTeX to work + if ($is_optional == 2 && !$pdf) { + $optional--; + next; + } + + if ($verbose_warn_install) { + if ($is_optional) { + print "Warning: better to also install \"$prog\".\n"; + } else { + print "ERROR: please install \"$prog\", otherwise, build won't work.\n"; + } + } + if (defined($map{$prog})) { + $install .= " " . $map{$prog}; + } else { + $install .= " " . $prog; + } + } + + $install =~ s/^\s//; +} + +sub add_package($$) +{ + my $package = shift; + my $is_optional = shift; + + $missing{$package} = $is_optional; + if ($is_optional) { + $optional++; + } else { + $need++; + } +} + +sub check_missing_file($$$) +{ + my $files = shift; + my $package = shift; + my $is_optional = shift; + + for (@$files) { + return if(-e $_); + } + + add_package($package, $is_optional); +} + +sub findprog($) +{ + foreach(split(/:/, $ENV{PATH})) { + return "$_/$_[0]" if(-x "$_/$_[0]"); + } +} + +sub find_python_no_venv() +{ + my $prog = shift; + + my $cur_dir = qx(pwd); + $cur_dir =~ s/\s+$//; + + foreach my $dir (split(/:/, $ENV{PATH})) { + next if ($dir =~ m,($cur_dir)/sphinx,); + return "$dir/python3" if(-x "$dir/python3"); + } + foreach my $dir (split(/:/, $ENV{PATH})) { + next if ($dir =~ m,($cur_dir)/sphinx,); + return "$dir/python" if(-x "$dir/python"); + } + return "python"; +} + +sub check_program($$) +{ + my $prog = shift; + my $is_optional = shift; + + return $prog if findprog($prog); + + add_package($prog, $is_optional); +} + +sub check_perl_module($$) +{ + my $prog = shift; + my $is_optional = shift; + + my $err = system("perl -M$prog -e 1 2>/dev/null /dev/null"); + return if ($err == 0); + + add_package($prog, $is_optional); +} + +sub check_python_module($$) +{ + my $prog = shift; + my $is_optional = shift; + + return if (!$python_cmd); + + my $err = system("$python_cmd -c 'import $prog' 2>/dev/null /dev/null"); + return if ($err == 0); + + add_package($prog, $is_optional); +} + +sub check_rpm_missing($$) +{ + my @pkgs = @{$_[0]}; + my $is_optional = $_[1]; + + foreach my $prog(@pkgs) { + my $err = system("rpm -q '$prog' 2>/dev/null >/dev/null"); + add_package($prog, $is_optional) if ($err); + } +} + +sub check_pacman_missing($$) +{ + my @pkgs = @{$_[0]}; + my $is_optional = $_[1]; + + foreach my $prog(@pkgs) { + my $err = system("pacman -Q '$prog' 2>/dev/null >/dev/null"); + add_package($prog, $is_optional) if ($err); + } +} + +sub check_missing_tex($) +{ + my $is_optional = shift; + my $kpsewhich = findprog("kpsewhich"); + + foreach my $prog(keys %texlive) { + my $package = $texlive{$prog}; + if (!$kpsewhich) { + add_package($package, $is_optional); + next; + } + my $file = qx($kpsewhich $prog); + add_package($package, $is_optional) if ($file =~ /^\s*$/); + } +} + +sub get_sphinx_fname() +{ + if ($ENV{'SPHINXBUILD'}) { + return $ENV{'SPHINXBUILD'}; + } + + my $fname = "sphinx-build"; + return $fname if findprog($fname); + + $fname = "sphinx-build-3"; + if (findprog($fname)) { + $need_symlink = 1; + return $fname; + } + + return ""; +} + +sub get_sphinx_version($) +{ + my $cmd = shift; + my $ver; + + open IN, "$cmd --version 2>&1 |"; + while () { + if (m/^\s*sphinx-build\s+([\d\.]+)((\+\/[\da-f]+)|(b\d+))?$/) { + $ver=$1; + last; + } + # Sphinx 1.2.x uses a different format + if (m/^\s*Sphinx.*\s+([\d\.]+)$/) { + $ver=$1; + last; + } + } + close IN; + return $ver; +} + +sub check_sphinx() +{ + open IN, $conf or die "Can't open $conf"; + while () { + if (m/^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]/) { + $min_version=$1; + last; + } + } + close IN; + + die "Can't get needs_sphinx version from $conf" if (!$min_version); + + $virtenv_dir = $virtenv_prefix . "latest"; + + my $sphinx = get_sphinx_fname(); + if ($sphinx eq "") { + $need_sphinx = 1; + return; + } + + $cur_version = get_sphinx_version($sphinx); + die "$sphinx didn't return its version" if (!$cur_version); + + if ($cur_version lt $min_version) { + printf "ERROR: Sphinx version is %s. It should be >= %s\n", + $cur_version, $min_version; + $need_sphinx = 1; + return; + } + + return if ($cur_version lt $rec_version); + + # On version check mode, just assume Sphinx has all mandatory deps + exit (0) if ($version_check); +} + +# +# Ancillary subroutines +# + +sub catcheck($) +{ + my $res = ""; + $res = qx(cat $_[0]) if (-r $_[0]); + return $res; +} + +sub which($) +{ + my $file = shift; + my @path = split ":", $ENV{PATH}; + + foreach my $dir(@path) { + my $name = $dir.'/'.$file; + return $name if (-x $name ); + } + return undef; +} + +# +# Subroutines that check distro-specific hints +# + +sub give_debian_hints() +{ + my %map = ( + "python-sphinx" => "python3-sphinx", + "yaml" => "python3-yaml", + "ensurepip" => "python3-venv", + "virtualenv" => "virtualenv", + "dot" => "graphviz", + "convert" => "imagemagick", + "Pod::Usage" => "perl-modules", + "xelatex" => "texlive-xetex", + "rsvg-convert" => "librsvg2-bin", + ); + + if ($pdf) { + check_missing_file(["/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty"], + "texlive-lang-chinese", 2); + + check_missing_file(["/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"], + "fonts-dejavu", 2); + + check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc"], + "fonts-noto-cjk", 2); + } + + check_program("dvipng", 2) if ($pdf); + check_missing(\%map); + + return if (!$need && !$optional); + printf("You should run:\n") if ($verbose_warn_install); + printf("\n\tsudo apt-get install $install\n"); +} + +sub give_redhat_hints() +{ + my %map = ( + "python-sphinx" => "python3-sphinx", + "yaml" => "python3-pyyaml", + "virtualenv" => "python3-virtualenv", + "dot" => "graphviz", + "convert" => "ImageMagick", + "Pod::Usage" => "perl-Pod-Usage", + "xelatex" => "texlive-xetex-bin", + "rsvg-convert" => "librsvg2-tools", + ); + + my @fedora26_opt_pkgs = ( + "graphviz-gd", # Fedora 26: needed for PDF support + ); + + my @fedora_tex_pkgs = ( + "texlive-collection-fontsrecommended", + "texlive-collection-latex", + "texlive-xecjk", + "dejavu-sans-fonts", + "dejavu-serif-fonts", + "dejavu-sans-mono-fonts", + ); + + # + # Checks valid for RHEL/CentOS version 7.x. + # + my $old = 0; + my $rel; + my $noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts"; + $rel = $2 if ($system_release =~ /(release|Linux)\s+(\d+)/); + + if (!($system_release =~ /Fedora/)) { + $map{"virtualenv"} = "python-virtualenv"; + + if ($rel && $rel < 8) { + $old = 1; + $pdf = 0; + + printf("Note: texlive packages on RHEL/CENTOS <= 7 are incomplete. Can't support PDF output\n"); + printf("If you want to build PDF, please read:\n"); + printf("\thttps://www.systutorials.com/241660/how-to-install-tex-live-on-centos-7-linux/\n"); + } + } else { + if ($rel && $rel < 26) { + $old = 1; + } + if ($rel && $rel >= 38) { + $noto_sans_redhat = "google-noto-sans-cjk-fonts"; + } + } + if (!$rel) { + printf("Couldn't identify release number\n"); + $old = 1; + $pdf = 0; + } + + if ($pdf) { + check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc"], + $noto_sans_redhat, 2); + } + + check_rpm_missing(\@fedora26_opt_pkgs, 2) if ($pdf && !$old); + check_rpm_missing(\@fedora_tex_pkgs, 2) if ($pdf); + check_missing_tex(2) if ($pdf); + check_missing(\%map); + + return if (!$need && !$optional); + + if (!$old) { + # dnf, for Fedora 18+ + printf("You should run:\n") if ($verbose_warn_install); + printf("\n\tsudo dnf install -y $install\n"); + } else { + # yum, for RHEL (and clones) or Fedora version < 18 + printf("You should run:\n") if ($verbose_warn_install); + printf("\n\tsudo yum install -y $install\n"); + } +} + +sub give_opensuse_hints() +{ + my %map = ( + "python-sphinx" => "python3-sphinx", + "yaml" => "python3-pyyaml", + "virtualenv" => "python3-virtualenv", + "dot" => "graphviz", + "convert" => "ImageMagick", + "Pod::Usage" => "perl-Pod-Usage", + "xelatex" => "texlive-xetex-bin", + ); + + # On Tumbleweed, this package is also named rsvg-convert + $map{"rsvg-convert"} = "rsvg-view" if (!($system_release =~ /Tumbleweed/)); + + my @suse_tex_pkgs = ( + "texlive-babel-english", + "texlive-caption", + "texlive-colortbl", + "texlive-courier", + "texlive-dvips", + "texlive-helvetic", + "texlive-makeindex", + "texlive-metafont", + "texlive-metapost", + "texlive-palatino", + "texlive-preview", + "texlive-times", + "texlive-zapfchan", + "texlive-zapfding", + ); + + $map{"latexmk"} = "texlive-latexmk-bin"; + + # FIXME: add support for installing CJK fonts + # + # I tried hard, but was unable to find a way to install + # "Noto Sans CJK SC" on openSUSE + + check_rpm_missing(\@suse_tex_pkgs, 2) if ($pdf); + check_missing_tex(2) if ($pdf); + check_missing(\%map); + + return if (!$need && !$optional); + printf("You should run:\n") if ($verbose_warn_install); + printf("\n\tsudo zypper install --no-recommends $install\n"); +} + +sub give_mageia_hints() +{ + my %map = ( + "python-sphinx" => "python3-sphinx", + "yaml" => "python3-yaml", + "virtualenv" => "python3-virtualenv", + "dot" => "graphviz", + "convert" => "ImageMagick", + "Pod::Usage" => "perl-Pod-Usage", + "xelatex" => "texlive", + "rsvg-convert" => "librsvg2", + ); + + my @tex_pkgs = ( + "texlive-fontsextra", + ); + + $map{"latexmk"} = "texlive-collection-basic"; + + my $packager_cmd; + my $noto_sans; + if ($system_release =~ /OpenMandriva/) { + $packager_cmd = "dnf install"; + $noto_sans = "noto-sans-cjk-fonts"; + @tex_pkgs = ( "texlive-collection-fontsextra" ); + } else { + $packager_cmd = "urpmi"; + $noto_sans = "google-noto-sans-cjk-ttc-fonts"; + } + + + if ($pdf) { + check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/TTF/NotoSans-Regular.ttf"], + $noto_sans, 2); + } + + check_rpm_missing(\@tex_pkgs, 2) if ($pdf); + check_missing(\%map); + + return if (!$need && !$optional); + printf("You should run:\n") if ($verbose_warn_install); + printf("\n\tsudo $packager_cmd $install\n"); +} + +sub give_arch_linux_hints() +{ + my %map = ( + "yaml" => "python-yaml", + "virtualenv" => "python-virtualenv", + "dot" => "graphviz", + "convert" => "imagemagick", + "xelatex" => "texlive-xetex", + "latexmk" => "texlive-core", + "rsvg-convert" => "extra/librsvg", + ); + + my @archlinux_tex_pkgs = ( + "texlive-core", + "texlive-latexextra", + "ttf-dejavu", + ); + check_pacman_missing(\@archlinux_tex_pkgs, 2) if ($pdf); + + if ($pdf) { + check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], + "noto-fonts-cjk", 2); + } + + check_missing(\%map); + + return if (!$need && !$optional); + printf("You should run:\n") if ($verbose_warn_install); + printf("\n\tsudo pacman -S $install\n"); +} + +sub give_gentoo_hints() +{ + my %map = ( + "yaml" => "dev-python/pyyaml", + "virtualenv" => "dev-python/virtualenv", + "dot" => "media-gfx/graphviz", + "convert" => "media-gfx/imagemagick", + "xelatex" => "dev-texlive/texlive-xetex media-fonts/dejavu", + "rsvg-convert" => "gnome-base/librsvg", + ); + + check_missing_file(["/usr/share/fonts/dejavu/DejaVuSans.ttf"], + "media-fonts/dejavu", 2) if ($pdf); + + if ($pdf) { + check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf", + "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc"], + "media-fonts/noto-cjk", 2); + } + + check_missing(\%map); + + return if (!$need && !$optional); + + printf("You should run:\n") if ($verbose_warn_install); + printf("\n"); + + my $imagemagick = "media-gfx/imagemagick svg png"; + my $cairo = "media-gfx/graphviz cairo pdf"; + my $portage_imagemagick = "/etc/portage/package.use/imagemagick"; + my $portage_cairo = "/etc/portage/package.use/graphviz"; + + if (qx(grep imagemagick $portage_imagemagick 2>/dev/null) eq "") { + printf("\tsudo su -c 'echo \"$imagemagick\" > $portage_imagemagick'\n") + } + if (qx(grep graphviz $portage_cairo 2>/dev/null) eq "") { + printf("\tsudo su -c 'echo \"$cairo\" > $portage_cairo'\n"); + } + + printf("\tsudo emerge --ask $install\n"); + +} + +sub check_distros() +{ + # Distro-specific hints + if ($system_release =~ /Red Hat Enterprise Linux/) { + give_redhat_hints; + return; + } + if ($system_release =~ /CentOS/) { + give_redhat_hints; + return; + } + if ($system_release =~ /Scientific Linux/) { + give_redhat_hints; + return; + } + if ($system_release =~ /Oracle Linux Server/) { + give_redhat_hints; + return; + } + if ($system_release =~ /Fedora/) { + give_redhat_hints; + return; + } + if ($system_release =~ /Ubuntu/) { + give_debian_hints; + return; + } + if ($system_release =~ /Debian/) { + give_debian_hints; + return; + } + if ($system_release =~ /openSUSE/) { + give_opensuse_hints; + return; + } + if ($system_release =~ /Mageia/) { + give_mageia_hints; + return; + } + if ($system_release =~ /OpenMandriva/) { + give_mageia_hints; + return; + } + if ($system_release =~ /Arch Linux/) { + give_arch_linux_hints; + return; + } + if ($system_release =~ /Gentoo/) { + give_gentoo_hints; + return; + } + + # + # Fall-back to generic hint code for other distros + # That's far from ideal, specially for LaTeX dependencies. + # + my %map = ( + "sphinx-build" => "sphinx" + ); + check_missing_tex(2) if ($pdf); + check_missing(\%map); + print "I don't know distro $system_release.\n"; + print "So, I can't provide you a hint with the install procedure.\n"; + print "There are likely missing dependencies.\n"; +} + +# +# Common dependencies +# + +sub deactivate_help() +{ + printf "\n If you want to exit the virtualenv, you can use:\n"; + printf "\tdeactivate\n"; +} + +sub get_virtenv() +{ + my $ver; + my $min_activate = "$ENV{'PWD'}/${virtenv_prefix}${min_version}/bin/activate"; + my @activates = glob "$ENV{'PWD'}/${virtenv_prefix}*/bin/activate"; + + @activates = sort {$b cmp $a} @activates; + + foreach my $f (@activates) { + next if ($f lt $min_activate); + + my $sphinx_cmd = $f; + $sphinx_cmd =~ s/activate/sphinx-build/; + next if (! -f $sphinx_cmd); + + my $ver = get_sphinx_version($sphinx_cmd); + + if (!$ver) { + $f =~ s#/bin/activate##; + print("Warning: virtual environment $f is not working.\nPython version upgrade? Remove it with:\n\n\trm -rf $f\n\n"); + } + + if ($need_sphinx && ($ver ge $min_version)) { + return ($f, $ver); + } elsif ($ver gt $cur_version) { + return ($f, $ver); + } + } + return ("", ""); +} + +sub recommend_sphinx_upgrade() +{ + my $venv_ver; + + # Avoid running sphinx-builds from venv if $cur_version is good + if ($cur_version && ($cur_version ge $rec_version)) { + $latest_avail_ver = $cur_version; + return; + } + + # Get the highest version from sphinx_*/bin/sphinx-build and the + # corresponding command to activate the venv/virtenv + ($activate_cmd, $venv_ver) = get_virtenv(); + + # Store the highest version from Sphinx existing virtualenvs + if (($activate_cmd ne "") && ($venv_ver gt $cur_version)) { + $latest_avail_ver = $venv_ver; + } else { + $latest_avail_ver = $cur_version if ($cur_version); + } + + # As we don't know package version of Sphinx, and there's no + # virtual environments, don't check if upgrades are needed + if (!$virtualenv) { + return if (!$latest_avail_ver); + } + + # Either there are already a virtual env or a new one should be created + $need_pip = 1; + + return if (!$latest_avail_ver); + + # Return if the reason is due to an upgrade or not + if ($latest_avail_ver lt $rec_version) { + $rec_sphinx_upgrade = 1; + } + + return $latest_avail_ver; +} + +# +# The logic here is complex, as it have to deal with different versions: +# - minimal supported version; +# - minimal PDF version; +# - recommended version. +# It also needs to work fine with both distro's package and venv/virtualenv +sub recommend_sphinx_version($) +{ + my $virtualenv_cmd = shift; + + # Version is OK. Nothing to do. + if ($cur_version && ($cur_version ge $rec_version)) { + return; + }; + + if (!$need_sphinx) { + # sphinx-build is present and its version is >= $min_version + + #only recommend enabling a newer virtenv version if makes sense. + if ($latest_avail_ver gt $cur_version) { + printf "\nYou may also use the newer Sphinx version $latest_avail_ver with:\n"; + printf "\tdeactivate\n" if ($ENV{'PWD'} =~ /${virtenv_prefix}/); + printf "\t. $activate_cmd\n"; + deactivate_help(); + + return; + } + return if ($latest_avail_ver ge $rec_version); + } + + if (!$virtualenv) { + # No sphinx either via package or via virtenv. As we can't + # Compare the versions here, just return, recommending the + # user to install it from the package distro. + return if (!$latest_avail_ver); + + # User doesn't want a virtenv recommendation, but he already + # installed one via virtenv with a newer version. + # So, print commands to enable it + if ($latest_avail_ver gt $cur_version) { + printf "\nYou may also use the Sphinx virtualenv version $latest_avail_ver with:\n"; + printf "\tdeactivate\n" if ($ENV{'PWD'} =~ /${virtenv_prefix}/); + printf "\t. $activate_cmd\n"; + deactivate_help(); + + return; + } + print "\n"; + } else { + $need++ if ($need_sphinx); + } + + # Suggest newer versions if current ones are too old + if ($latest_avail_ver && $latest_avail_ver ge $min_version) { + # If there's a good enough version, ask the user to enable it + if ($latest_avail_ver ge $rec_version) { + printf "\nNeed to activate Sphinx (version $latest_avail_ver) on virtualenv with:\n"; + printf "\t. $activate_cmd\n"; + deactivate_help(); + + return; + } + + # Version is above the minimal required one, but may be + # below the recommended one. So, print warnings/notes + + if ($latest_avail_ver lt $rec_version) { + print "Warning: It is recommended at least Sphinx version $rec_version.\n"; + } + } + + # At this point, either it needs Sphinx or upgrade is recommended, + # both via pip + + if ($rec_sphinx_upgrade) { + if (!$virtualenv) { + print "Instead of install/upgrade Python Sphinx pkg, you could use pip/pypi with:\n\n"; + } else { + print "To upgrade Sphinx, use:\n\n"; + } + } else { + print "\nSphinx needs to be installed either:\n1) via pip/pypi with:\n\n"; + } + + $python_cmd = find_python_no_venv(); + + printf "\t$virtualenv_cmd $virtenv_dir\n"; + + printf "\t. $virtenv_dir/bin/activate\n"; + printf "\tpip install -r $requirement_file\n"; + deactivate_help(); + + printf "\n2) As a package with:\n"; + + my $old_need = $need; + my $old_optional = $optional; + %missing = (); + $pdf = 0; + $optional = 0; + $install = ""; + $verbose_warn_install = 0; + + add_package("python-sphinx", 0); + + check_distros(); + + $need = $old_need; + $optional = $old_optional; + + printf "\n Please note that Sphinx >= 3.0 will currently produce false-positive\n"; + printf " warning when the same name is used for more than one type (functions,\n"; + printf " structs, enums,...). This is known Sphinx bug. For more details, see:\n"; + printf "\thttps://github.com/sphinx-doc/sphinx/pull/8313\n"; +} + +sub check_needs() +{ + # Check if Sphinx is already accessible from current environment + check_sphinx(); + + if ($system_release) { + print "Detected OS: $system_release.\n"; + } else { + print "Unknown OS\n"; + } + printf "Sphinx version: %s\n\n", $cur_version if ($cur_version); + + # Check python command line, trying first python3 + $python_cmd = findprog("python3"); + $python_cmd = check_program("python", 0) if (!$python_cmd); + + # Check the type of virtual env, depending on Python version + if ($python_cmd) { + if ($virtualenv) { + my $tmp = qx($python_cmd --version 2>&1); + if ($tmp =~ m/(\d+\.)(\d+\.)/) { + if ($1 < 3) { + # Fail if it finds python2 (or worse) + die "Python 3 is required to build the kernel docs\n"; + } + if ($1 == 3 && $2 < 3) { + # Need Python 3.3 or upper for venv + $need_virtualenv = 1; + } + } else { + die "Warning: couldn't identify $python_cmd version!"; + } + } else { + add_package("python-sphinx", 0); + } + } + + my $venv_ver = recommend_sphinx_upgrade(); + + my $virtualenv_cmd; + + if ($need_pip) { + # Set virtualenv command line, if python < 3.3 + if ($need_virtualenv) { + $virtualenv_cmd = findprog("virtualenv-3"); + $virtualenv_cmd = findprog("virtualenv-3.5") if (!$virtualenv_cmd); + if (!$virtualenv_cmd) { + check_program("virtualenv", 0); + $virtualenv_cmd = "virtualenv"; + } + } else { + $virtualenv_cmd = "$python_cmd -m venv"; + check_python_module("ensurepip", 0); + } + } + + # Check for needed programs/tools + check_perl_module("Pod::Usage", 0); + check_python_module("yaml", 0); + check_program("make", 0); + check_program("gcc", 0); + check_program("dot", 1); + check_program("convert", 1); + + # Extra PDF files - should use 2 for is_optional + check_program("xelatex", 2) if ($pdf); + check_program("rsvg-convert", 2) if ($pdf); + check_program("latexmk", 2) if ($pdf); + + # Do distro-specific checks and output distro-install commands + check_distros(); + + if (!$python_cmd) { + if ($need == 1) { + die "Can't build as $need mandatory dependency is missing"; + } elsif ($need) { + die "Can't build as $need mandatory dependencies are missing"; + } + } + + # Check if sphinx-build is called sphinx-build-3 + if ($need_symlink) { + printf "\tsudo ln -sf %s /usr/bin/sphinx-build\n\n", + which("sphinx-build-3"); + } + + recommend_sphinx_version($virtualenv_cmd); + printf "\n"; + + print "All optional dependencies are met.\n" if (!$optional); + + if ($need == 1) { + die "Can't build as $need mandatory dependency is missing"; + } elsif ($need) { + die "Can't build as $need mandatory dependencies are missing"; + } + + print "Needed package dependencies are met.\n"; +} + +# +# Main +# + +while (@ARGV) { + my $arg = shift(@ARGV); + + if ($arg eq "--no-virtualenv") { + $virtualenv = 0; + } elsif ($arg eq "--no-pdf"){ + $pdf = 0; + } elsif ($arg eq "--version-check"){ + $version_check = 1; + } else { + print "Usage:\n\t$0 <--no-virtualenv> <--no-pdf> <--version-check>\n\n"; + print "Where:\n"; + print "\t--no-virtualenv\t- Recommend installing Sphinx instead of using a virtualenv\n"; + print "\t--version-check\t- if version is compatible, don't check for missing dependencies\n"; + print "\t--no-pdf\t- don't check for dependencies required to build PDF docs\n\n"; + exit -1; + } +} + +# +# Determine the system type. There's no standard unique way that would +# work with all distros with a minimal package install. So, several +# methods are used here. +# +# By default, it will use lsb_release function. If not available, it will +# fail back to reading the known different places where the distro name +# is stored +# + +$system_release = qx(lsb_release -d) if which("lsb_release"); +$system_release =~ s/Description:\s*// if ($system_release); +$system_release = catcheck("/etc/system-release") if !$system_release; +$system_release = catcheck("/etc/redhat-release") if !$system_release; +$system_release = catcheck("/etc/lsb-release") if !$system_release; +$system_release = catcheck("/etc/gentoo-release") if !$system_release; + +# This seems more common than LSB these days +if (!$system_release) { + my %os_var; + if (open IN, "cat /etc/os-release|") { + while () { + if (m/^([\w\d\_]+)=\"?([^\"]*)\"?\n/) { + $os_var{$1}=$2; + } + } + $system_release = $os_var{"NAME"}; + if (defined($os_var{"VERSION_ID"})) { + $system_release .= " " . $os_var{"VERSION_ID"} if (defined($os_var{"VERSION_ID"})); + } else { + $system_release .= " " . $os_var{"VERSION"}; + } + } +} +$system_release = catcheck("/etc/issue") if !$system_release; +$system_release =~ s/\s+$//; + +check_needs; -- cgit v1.2.3 From ca9087f50772aa5794dd655ff5a0719dd4b83b1f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:20 +0200 Subject: scripts: sphinx-pre-install: Convert script to Python Port scripts/sphinx-pre-install to Python. That allows a better maintainance of the file. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/8d236079545aff796900bde4cdaaf79411d04074.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 1102 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1102 insertions(+) create mode 100755 scripts/sphinx-pre-install.py (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py new file mode 100755 index 000000000000..dcee2181b72f --- /dev/null +++ b/scripts/sphinx-pre-install.py @@ -0,0 +1,1102 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2017-2025 Mauro Carvalho Chehab +# +# pylint: disable=C0103,C0114,C0115,C0116,C0301 +# pylint: disable=R0902,R0904,R0912,R0915,R1705,R1710,E1121 + + +import argparse +import os +import re +import subprocess +import sys +from glob import glob + + +def parse_version(version): + """Convert a major.minor.patch version into a tuple""" +# + return tuple(int(x) for x in version.split(".")) + + +def ver_str(version): + """Returns a version tuple as major.minor.patch""" + + return ".".join([str(x) for x in version]) + + +RECOMMENDED_VERSION = parse_version("3.4.3") + + +class SphinxDependencyChecker: + # List of required texlive packages on Fedora and OpenSuse + texlive = { + "amsfonts.sty": "texlive-amsfonts", + "amsmath.sty": "texlive-amsmath", + "amssymb.sty": "texlive-amsfonts", + "amsthm.sty": "texlive-amscls", + "anyfontsize.sty": "texlive-anyfontsize", + "atbegshi.sty": "texlive-oberdiek", + "bm.sty": "texlive-tools", + "capt-of.sty": "texlive-capt-of", + "cmap.sty": "texlive-cmap", + "ctexhook.sty": "texlive-ctex", + "ecrm1000.tfm": "texlive-ec", + "eqparbox.sty": "texlive-eqparbox", + "eu1enc.def": "texlive-euenc", + "fancybox.sty": "texlive-fancybox", + "fancyvrb.sty": "texlive-fancyvrb", + "float.sty": "texlive-float", + "fncychap.sty": "texlive-fncychap", + "footnote.sty": "texlive-mdwtools", + "framed.sty": "texlive-framed", + "luatex85.sty": "texlive-luatex85", + "multirow.sty": "texlive-multirow", + "needspace.sty": "texlive-needspace", + "palatino.sty": "texlive-psnfss", + "parskip.sty": "texlive-parskip", + "polyglossia.sty": "texlive-polyglossia", + "tabulary.sty": "texlive-tabulary", + "threeparttable.sty": "texlive-threeparttable", + "titlesec.sty": "texlive-titlesec", + "ucs.sty": "texlive-ucs", + "upquote.sty": "texlive-upquote", + "wrapfig.sty": "texlive-wrapfig", + } + + def __init__(self, args): + self.pdf = args.pdf + self.virtualenv = args.virtualenv + self.version_check = args.version_check + + self.missing = {} + + self.need = 0 + self.optional = 0 + self.need_symlink = 0 + self.need_sphinx = 0 + self.need_pip = 0 + self.need_virtualenv = 0 + self.rec_sphinx_upgrade = 0 + self.verbose_warn_install = 1 + + self.system_release = "" + self.install = "" + self.virtenv_dir = "" + self.python_cmd = "" + self.activate_cmd = "" + + self.min_version = (0, 0, 0) + self.cur_version = (0, 0, 0) + self.latest_avail_ver = (0, 0, 0) + self.venv_ver = (0, 0, 0) + + prefix = os.environ.get("srctree", ".") + "/" + + self.conf = prefix + "Documentation/conf.py" + self.requirement_file = prefix + "Documentation/sphinx/requirements.txt" + self.virtenv_prefix = ["sphinx_", "Sphinx_" ] + + # + # Ancillary methods that don't depend on self + # + + @staticmethod + def which(prog): + for path in os.environ.get("PATH", "").split(":"): + full_path = os.path.join(path, prog) + if os.access(full_path, os.X_OK): + return full_path + + return None + + @staticmethod + def find_python_no_venv(): + # FIXME: does it makes sense now that this script is in Python? + + result = subprocess.run(["pwd"], capture_output=True, text=True) + cur_dir = result.stdout.strip() + + python_names = ["python3", "python"] + + for d in os.environ.get("PATH", "").split(":"): + if f"{cur_dir}/sphinx" in d: + continue + + for p in python_names: + if os.access(os.path.join(d, p), os.X_OK): + return os.path.join(d, p) + + # Python not found at the PATH + return python_names[-1] + + @staticmethod + def run(*args, **kwargs): + """Excecute a command, hiding its output by default""" + + if not kwargs.get('capture_output', False): + if 'stdout' not in kwargs: + kwargs['stdout'] = subprocess.DEVNULL + if 'stderr' not in kwargs: + kwargs['stderr'] = subprocess.DEVNULL + + return subprocess.run(*args, **kwargs) + + # + # Methods to check if a feature exists + # + + # Note: is_optional has 3 states: + # - 0: mandatory + # - 1: optional, but nice to have + # - 2: LaTeX optional - pdf builds without it, but may have visual impact + + def check_missing(self, progs): + for prog, is_optional in sorted(self.missing.items()): + # At least on some LTS distros like CentOS 7, texlive doesn't + # provide all packages we need. When such distros are + # detected, we have to disable PDF output. + # + # So, we need to ignore the packages that distros would + # need for LaTeX to work + if is_optional == 2 and not self.pdf: + self.optional -= 1 + continue + + if self.verbose_warn_install: + if is_optional: + print(f'Warning: better to also install "{prog}".') + else: + print(f'ERROR: please install "{prog}", otherwise, build won\'t work.') + + self.install += " " + progs.get(prog, prog) + + self.install = self.install.lstrip() + + def add_package(self, package, is_optional): + self.missing[package] = is_optional + if is_optional: + self.optional += 1 + else: + self.need += 1 + + def check_missing_file(self, files, package, is_optional): + for f in files: + if os.path.exists(f): + return + self.add_package(package, is_optional) + + def check_program(self, prog, is_optional): + found = self.which(prog) + if found: + return found + + self.add_package(prog, is_optional) + + return None + + def check_perl_module(self, prog, is_optional): + # While testing with lxc download template, one of the + # distros (Oracle) didn't have perl - nor even an option to install + # before installing oraclelinux-release-el9 package. + # + # Check it before running an error. If perl is not there, + # add it as a mandatory package, as some parts of the doc builder + # needs it. + if not self.which("perl"): + self.add_package("perl", 0) + self.add_package(prog, is_optional) + return + + try: + self.run(["perl", f"-M{prog}", "-e", "1"], check=True) + except subprocess.CalledProcessError: + self.add_package(prog, is_optional) + + def check_python_module(self, module, is_optional): + # FIXME: is it needed at the Python version? Maybe due to venv? + if not self.python_cmd: + return + + try: + self.run([self.python_cmd, "-c", f"import {module}"], check=True) + except subprocess.CalledProcessError: + self.add_package(module, is_optional) + + def check_rpm_missing(self, pkgs, is_optional): + for prog in pkgs: + try: + self.run(["rpm", "-q", prog], check=True) + except subprocess.CalledProcessError: + self.add_package(prog, is_optional) + + def check_pacman_missing(self, pkgs, is_optional): + for prog in pkgs: + try: + self.run(["pacman", "-Q", prog], check=True) + except subprocess.CalledProcessError: + self.add_package(prog, is_optional) + + def check_missing_tex(self, is_optional): + kpsewhich = self.which("kpsewhich") + for prog, package in self.texlive.items(): + + # If kpsewhich is not there, just add it to deps + if not kpsewhich: + self.add_package(package, is_optional) + continue + + # Check if the package is needed + try: + result = self.run( + [kpsewhich, prog], stdout=subprocess.PIPE, text=True, check=True + ) + + # Didn't find. Add it + if not result.stdout.strip(): + self.add_package(package, is_optional) + + except subprocess.CalledProcessError: + # kpsewhich returned an error. Add it, just in case + self.add_package(package, is_optional) + + def get_sphinx_fname(self): + if "SPHINXBUILD" in os.environ: + return os.environ["SPHINXBUILD"] + + fname = "sphinx-build" + if self.which(fname): + return fname + + fname = "sphinx-build-3" + if self.which(fname): + self.need_symlink = 1 + return fname + + return "" + + def get_sphinx_version(self, cmd): + try: + result = self.run([cmd, "--version"], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, check=True) + except (subprocess.CalledProcessError, FileNotFoundError): + return None + + for line in result.stdout.split("\n"): + match = re.match(r"^sphinx-build\s+([\d\.]+)(?:\+(?:/[\da-f]+)|b\d+)?\s*$", line) + if match: + return parse_version(match.group(1)) + + match = re.match(r"^Sphinx.*\s+([\d\.]+)\s*$", line) + if match: + return parse_version(match.group(1)) + + def check_sphinx(self): + try: + with open(self.conf, "r", encoding="utf-8") as f: + for line in f: + match = re.match(r"^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]", line) + if match: + self.min_version = parse_version(match.group(1)) + break + except IOError: + sys.exit(f"Can't open {self.conf}") + + if not self.min_version: + sys.exit(f"Can't get needs_sphinx version from {self.conf}") + + self.virtenv_dir = self.virtenv_prefix[0] + "latest" + + sphinx = self.get_sphinx_fname() + if not sphinx: + self.need_sphinx = 1 + return + + self.cur_version = self.get_sphinx_version(sphinx) + if not self.cur_version: + sys.exit(f"{sphinx} didn't return its version") + + if self.cur_version < self.min_version: + curver = ver_str(self.cur_version) + minver = ver_str(self.min_version) + + print(f"ERROR: Sphinx version is {curver}. It should be >= {minver}") + self.need_sphinx = 1 + return + + # On version check mode, just assume Sphinx has all mandatory deps + if self.version_check and self.cur_version >= RECOMMENDED_VERSION: + sys.exit(0) + + def catcheck(self, filename): + if os.path.exists(filename): + with open(filename, "r", encoding="utf-8") as f: + return f.read().strip() + return "" + + # + # Distro-specific hints methods + # + + def give_debian_hints(self): + progs = { + "Pod::Usage": "perl-modules", + "convert": "imagemagick", + "dot": "graphviz", + "ensurepip": "python3-venv", + "python-sphinx": "python3-sphinx", + "rsvg-convert": "librsvg2-bin", + "virtualenv": "virtualenv", + "xelatex": "texlive-xetex", + "yaml": "python3-yaml", + } + + if self.pdf: + pdf_pkgs = { + "texlive-lang-chinese": [ + "/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty", + ], + "fonts-dejavu": [ + "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", + ], + "fonts-noto-cjk": [ + "/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc", + ], + } + + for package, files in pdf_pkgs.items(): + self.check_missing_file(files, package, 2) + + self.check_program("dvipng", 2) + + self.check_missing(progs) + + if not self.need and not self.optional: + return + + if self.verbose_warn_install: + print("You should run:") + print(f"\n\tsudo apt-get install {self.install}") + + def give_redhat_hints(self): + progs = { + "Pod::Usage": "perl-Pod-Usage", + "convert": "ImageMagick", + "dot": "graphviz", + "python-sphinx": "python3-sphinx", + "rsvg-convert": "librsvg2-tools", + "virtualenv": "python3-virtualenv", + "xelatex": "texlive-xetex-bin", + "yaml": "python3-pyyaml", + } + + fedora26_opt_pkgs = [ + "graphviz-gd", # Fedora 26: needed for PDF support + ] + + fedora_tex_pkgs = [ + "dejavu-sans-fonts", + "dejavu-sans-mono-fonts", + "dejavu-serif-fonts", + "texlive-collection-fontsrecommended", + "texlive-collection-latex", + "texlive-xecjk", + ] + + old = 0 + rel = None + pkg_manager = "dnf" + + match = re.search(r"(release|Linux)\s+(\d+)", self.system_release) + if match: + rel = int(match.group(2)) + + if not rel: + print("Couldn't identify release number") + old = 1 + self.pdf = False + elif re.search("Fedora", self.system_release): + # Fedora 38 and upper use this CJK font + + noto_sans_redhat = "google-noto-sans-cjk-fonts" + else: + # Almalinux, CentOS, RHEL, ... + + # at least up to version 9 (and Fedora < 38), that's the CJK font + noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts" + + progs["virtualenv"] = "python-virtualenv" + + if rel and rel < 8: + old = 1 + self.pdf = False + + # RHEL 7 is in ELS, currently up to Jun, 2026 + + print("Note: texlive packages on RHEL/CENTOS <= 7 are incomplete. Can't support PDF output") + print("If you want to build PDF, please read:") + print("\thttps://www.systutorials.com/241660/how-to-install-tex-live-on-centos-7-linux/") + + if self.pdf: + pdf_pkgs = [ + "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc", + ] + + self.check_missing_file(pdf_pkgs, noto_sans_redhat, 2) + + if not old: + self.check_rpm_missing(fedora26_opt_pkgs, 2) + self.check_rpm_missing(fedora_tex_pkgs, 2) + + self.check_missing_tex(2) + + self.check_missing(progs) + + if not self.need and not self.optional: + return + + if self.verbose_warn_install: + print("You should run:") + + if old: + # dnf is there since Fedora 18+ and RHEL 8 + pkg_manager = "yum" + + print(f"\n\tsudo {pkg_manager} install -y {self.install}") + + def give_opensuse_hints(self): + progs = { + "Pod::Usage": "perl-Pod-Usage", + "convert": "ImageMagick", + "dot": "graphviz", + "python-sphinx": "python3-sphinx", + "virtualenv": "python3-virtualenv", + "xelatex": "texlive-xetex-bin", + "yaml": "python3-pyyaml", + } + + # On Tumbleweed, this package is also named rsvg-convert + if not re.search(r"Tumbleweed", self.system_release): + progs["rsvg-convert"] = "rsvg-view" + + suse_tex_pkgs = [ + "texlive-babel-english", + "texlive-caption", + "texlive-colortbl", + "texlive-courier", + "texlive-dvips", + "texlive-helvetic", + "texlive-makeindex", + "texlive-metafont", + "texlive-metapost", + "texlive-palatino", + "texlive-preview", + "texlive-times", + "texlive-zapfchan", + "texlive-zapfding", + ] + + progs["latexmk"] = "texlive-latexmk-bin" + + # FIXME: add support for installing CJK fonts + # + # I tried hard, but was unable to find a way to install + # "Noto Sans CJK SC" on openSUSE + + if self.pdf: + self.check_rpm_missing(suse_tex_pkgs, 2) + if self.pdf: + self.check_missing_tex(2) + self.check_missing(progs) + + if not self.need and not self.optional: + return + + if self.verbose_warn_install: + print("You should run:") + print(f"\n\tsudo zypper install --no-recommends {self.install}") + + def give_mageia_hints(self): + progs = { + "Pod::Usage": "perl-Pod-Usage", + "convert": "ImageMagick", + "dot": "graphviz", + "python-sphinx": "python3-sphinx", + "rsvg-convert": "librsvg2", + "virtualenv": "python3-virtualenv", + "xelatex": "texlive", + "yaml": "python3-yaml", + } + + tex_pkgs = [ + "texlive-fontsextra", + ] + + if re.search(r"OpenMandriva", self.system_release): + packager_cmd = "dnf install" + noto_sans = "noto-sans-cjk-fonts" + tex_pkgs = ["texlive-collection-fontsextra"] + else: + packager_cmd = "urpmi" + noto_sans = "google-noto-sans-cjk-ttc-fonts" + + progs["latexmk"] = "texlive-collection-basic" + + if self.pdf: + pdf_pkgs = [ + "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/TTF/NotoSans-Regular.ttf", + ] + + self.check_missing_file(pdf_pkgs, noto_sans, 2) + self.check_rpm_missing(tex_pkgs, 2) + + self.check_missing(progs) + + if not self.need and not self.optional: + return + if self.verbose_warn_install: + print("You should run:") + print(f"\n\tsudo {packager_cmd} {self.install}") + + def give_arch_linux_hints(self): + progs = { + "convert": "imagemagick", + "dot": "graphviz", + "latexmk": "texlive-core", + "rsvg-convert": "extra/librsvg", + "virtualenv": "python-virtualenv", + "xelatex": "texlive-xetex", + "yaml": "python-yaml", + } + + archlinux_tex_pkgs = [ + "texlive-core", + "texlive-latexextra", + "ttf-dejavu", + ] + + if self.pdf: + self.check_pacman_missing(archlinux_tex_pkgs, 2) + + self.check_missing_file( + ["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], + "noto-fonts-cjk", + 2, + ) + + self.check_missing(progs) + + if not self.need and not self.optional: + return + if self.verbose_warn_install: + print("You should run:") + print(f"\n\tsudo pacman -S {self.install}") + + def give_gentoo_hints(self): + progs = { + "convert": "media-gfx/imagemagick", + "dot": "media-gfx/graphviz", + "rsvg-convert": "gnome-base/librsvg", + "virtualenv": "dev-python/virtualenv", + "xelatex": "dev-texlive/texlive-xetex media-fonts/dejavu", + "yaml": "dev-python/pyyaml", + } + + if self.pdf: + pdf_pkgs = { + "media-fonts/dejavu": [ + "/usr/share/fonts/dejavu/DejaVuSans.ttf", + ], + "media-fonts/noto-cjk": [ + "/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf", + "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc", + ], + } + for package, files in pdf_pkgs.items(): + self.check_missing_file(files, package, 2) + + self.check_missing(progs) + + if not self.need and not self.optional: + return + + if self.verbose_warn_install: + print("You should run:") + print("\n") + + imagemagick = "media-gfx/imagemagick svg png" + cairo = "media-gfx/graphviz cairo pdf" + portage_imagemagick = "/etc/portage/package.use/imagemagick" + portage_cairo = "/etc/portage/package.use/graphviz" + + result = self.run(["grep", "imagemagick", portage_imagemagick], + stdout=subprocess.PIPE, text=True) + if not result.stdout.strip(): + print(f"\tsudo su -c 'echo \"{imagemagick}\" > {portage_imagemagick}'") + + result = self.run(["grep", "graphviz", portage_cairo], + stdout=subprocess.PIPE, text=True) + + if not result.stdout.strip(): + print(f"\tsudo su -c 'echo \"{cairo}\" > {portage_cairo}'") + + print(f"\tsudo emerge --ask {self.install}") + + # + # Dispatch the check to an os_specific hinter + # + + def check_distros(self): + # OS-specific hints logic + os_hints = { + re.compile("Red Hat Enterprise Linux"): self.give_redhat_hints, + re.compile("Fedora"): self.give_redhat_hints, + re.compile("AlmaLinux"): self.give_redhat_hints, + re.compile("Amazon Linux"): self.give_redhat_hints, + re.compile("CentOS"): self.give_redhat_hints, + re.compile("openEuler"): self.give_redhat_hints, + re.compile("Oracle Linux Server"): self.give_redhat_hints, + re.compile("Rocky Linux"): self.give_redhat_hints, + re.compile("Scientific Linux"): self.give_redhat_hints, + re.compile("Springdale Open Enterprise"): self.give_redhat_hints, + + re.compile("Ubuntu"): self.give_debian_hints, + re.compile("Debian"): self.give_debian_hints, + re.compile("Devuan"): self.give_debian_hints, + re.compile("Kali"): self.give_debian_hints, + re.compile("Mint"): self.give_debian_hints, + + re.compile("openSUSE"): self.give_opensuse_hints, + + re.compile("Mageia"): self.give_mageia_hints, + re.compile("OpenMandriva"): self.give_mageia_hints, + + re.compile("Arch Linux"): self.give_arch_linux_hints, + re.compile("Gentoo"): self.give_gentoo_hints, + } + + # If the OS is detected, use per-OS hint logic + for regex, os_hint in os_hints.items(): + if regex.search(self.system_release): + os_hint() + + return + + # + # Fall-back to generic hint code for other distros + # That's far from ideal, specially for LaTeX dependencies. + # + progs = {"sphinx-build": "sphinx"} + if self.pdf: + self.check_missing_tex(2) + + self.check_missing(progs) + + print(f"I don't know distro {self.system_release}.") + print("So, I can't provide you a hint with the install procedure.") + print("There are likely missing dependencies.") + + # + # Common dependencies + # + def deactivate_help(self): + print("\n If you want to exit the virtualenv, you can use:") + print("\tdeactivate") + + def get_virtenv(self): + cwd = os.getcwd() + + activates = [] + + # Add all sphinx prefixes with possible version numbers + for p in self.virtenv_prefix: + activates += glob(f"{cwd}/{p}[0-9]*/bin/activate") + + activates.sort(reverse=True, key=str.lower) + + # Place sphinx_latest first, if it exists + for p in self.virtenv_prefix: + activates = glob(f"{cwd}/{p}*latest/bin/activate") + activates + + ver = (0, 0, 0) + for f in activates: + # Discard too old Sphinx virtual environments + match = re.search(r"(\d+)\.(\d+)\.(\d+)", f) + if match: + ver = (int(match.group(1)), int(match.group(2)), int(match.group(3))) + + if ver < self.min_version: + continue + + sphinx_cmd = f.replace("activate", "sphinx-build") + if not os.path.isfile(sphinx_cmd): + continue + + ver = self.get_sphinx_version(sphinx_cmd) + + if not ver: + venv_dir = f.replace("/bin/activate", "") + print(f"Warning: virtual environment {venv_dir} is not working.\n" \ + "Python version upgrade? Remove it with:\n\n" \ + "\trm -rf {venv_dir}\n\n") + else: + if self.need_sphinx and ver >= self.min_version: + return (f, ver) + elif parse_version(ver) > self.cur_version: + return (f, ver) + + return ("", ver) + + def recommend_sphinx_upgrade(self): + # Avoid running sphinx-builds from venv if cur_version is good + if self.cur_version and self.cur_version >= RECOMMENDED_VERSION: + self.latest_avail_ver = self.cur_version + return None + + # Get the highest version from sphinx_*/bin/sphinx-build and the + # corresponding command to activate the venv/virtenv + self.activate_cmd, self.venv_ver = self.get_virtenv() + + # Store the highest version from Sphinx existing virtualenvs + if self.activate_cmd and self.venv_ver > self.cur_version: + self.latest_avail_ver = self.venv_ver + else: + if self.cur_version: + self.latest_avail_ver = self.cur_version + else: + self.latest_avail_ver = (0, 0, 0) + + # As we don't know package version of Sphinx, and there's no + # virtual environments, don't check if upgrades are needed + if not self.virtualenv: + if not self.latest_avail_ver: + return None + + return self.latest_avail_ver + + # Either there are already a virtual env or a new one should be created + self.need_pip = 1 + + if not self.latest_avail_ver: + return None + + # Return if the reason is due to an upgrade or not + if self.latest_avail_ver != (0, 0, 0): + if self.latest_avail_ver < RECOMMENDED_VERSION: + self.rec_sphinx_upgrade = 1 + + return self.latest_avail_ver + + def recommend_sphinx_version(self, virtualenv_cmd): + # The logic here is complex, as it have to deal with different versions: + # - minimal supported version; + # - minimal PDF version; + # - recommended version. + # It also needs to work fine with both distro's package and venv/virtualenv + + # Version is OK. Nothing to do. + if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION: + return + + if not self.need_sphinx: + # sphinx-build is present and its version is >= $min_version + + # only recommend enabling a newer virtenv version if makes sense. + if self.latest_avail_ver and self.latest_avail_ver > self.cur_version: + print("\nYou may also use the newer Sphinx version {self.latest_avail_ver} with:") + if f"{self.virtenv_prefix}" in os.getcwd(): + print("\tdeactivate") + print(f"\t. {self.activate_cmd}") + self.deactivate_help() + return + + if self.latest_avail_ver and self.latest_avail_ver >= RECOMMENDED_VERSION: + return + + if not self.virtualenv: + # No sphinx either via package or via virtenv. As we can't + # Compare the versions here, just return, recommending the + # user to install it from the package distro. + if not self.latest_avail_ver or self.latest_avail_ver == (0, 0, 0): + return + + # User doesn't want a virtenv recommendation, but he already + # installed one via virtenv with a newer version. + # So, print commands to enable it + if self.latest_avail_ver > self.cur_version: + print("\nYou may also use the Sphinx virtualenv version {self.latest_avail_ver} with:") + if f"{self.virtenv_prefix}" in os.getcwd(): + print("\tdeactivate") + print(f"\t. {self.activate_cmd}") + self.deactivate_help() + return + print("\n") + else: + if self.need_sphinx: + self.need += 1 + + # Suggest newer versions if current ones are too old + if self.latest_avail_ver and self.latest_avail_ver >= self.min_version: + if self.latest_avail_ver >= RECOMMENDED_VERSION: + print("\nNeed to activate Sphinx (version {self.latest_avail_ver}) on virtualenv with:") + print(f"\t. {self.activate_cmd}") + self.deactivate_help() + return + + # Version is above the minimal required one, but may be + # below the recommended one. So, print warnings/notes + if self.latest_avail_ver < RECOMMENDED_VERSION: + print(f"Warning: It is recommended at least Sphinx version {RECOMMENDED_VERSION}.") + + # At this point, either it needs Sphinx or upgrade is recommended, + # both via pip + + if self.rec_sphinx_upgrade: + if not self.virtualenv: + print("Instead of install/upgrade Python Sphinx pkg, you could use pip/pypi with:\n\n") + else: + print("To upgrade Sphinx, use:\n\n") + else: + print("\nSphinx needs to be installed either:\n1) via pip/pypi with:\n") + + self.python_cmd = self.find_python_no_venv() + + print(f"\t{virtualenv_cmd} {self.virtenv_dir}") + print(f"\t. {self.virtenv_dir}/bin/activate") + print(f"\tpip install -r {self.requirement_file}") + self.deactivate_help() + + print("\n2) As a package with:") + + old_need = self.need + old_optional = self.optional + self.missing = {} + self.pdf = False + self.optional = 0 + self.install = "" + old_verbose = self.verbose_warn_install + self.verbose_warn_install = 0 + + self.add_package("python-sphinx", 0) + + self.check_distros() + + self.need = old_need + self.optional = old_optional + self.verbose_warn_install = old_verbose + + print("\n" \ + " Please note that Sphinx >= 3.0 will currently produce false-positive\n" \ + " warning when the same name is used for more than one type (functions,\n" \ + " structs, enums,...). This is known Sphinx bug. For more details, see:\n" \ + "\thttps://github.com/sphinx-doc/sphinx/pull/8313") + + def check_needs(self): + self.get_system_release() + + # Check if Sphinx is already accessible from current environment + self.check_sphinx() + + if self.system_release: + print(f"Detected OS: {self.system_release}.") + else: + print("Unknown OS") + if self.cur_version != (0, 0, 0): + ver = ver_str(self.cur_version) + print(f"Sphinx version: {ver}\n") + + # FIXME: Check python command line, trying first python3 + self.python_cmd = self.which("python3") + if not self.python_cmd: + self.python_cmd = self.check_program("python", 0) + + # Check the type of virtual env, depending on Python version + if self.python_cmd: + if self.virtualenv: + try: + result = self.run( + [self.python_cmd, "--version"], + capture_output=True, + text=True, + check=True, + ) + + output = result.stdout + result.stderr + + match = re.search(r"(\d+)\.(\d+)\.", output) + if match: + major = int(match.group(1)) + minor = int(match.group(2)) + + if major < 3: + sys.exit("Python 3 is required to build the kernel docs") + if major == 3 and minor < 3: + self.need_virtualenv = True + else: + sys.exit(f"Warning: couldn't identify {self.python_cmd} version!") + + except subprocess.CalledProcessError as e: + sys.exit(f"Error checking Python version: {e}") + else: + self.add_package("python-sphinx", 0) + + self.venv_ver = self.recommend_sphinx_upgrade() + + virtualenv_cmd = "" + + if self.need_pip: + # Set virtualenv command line, if python < 3.3 + # FIXME: can be removed as we're now with an upper min requirement + # but then we need to check python version + if self.need_virtualenv: + virtualenv_cmd = self.which("virtualenv-3") + if not virtualenv_cmd: + virtualenv_cmd = self.which("virtualenv-3.5") + if not virtualenv_cmd: + self.check_program("virtualenv", 0) + virtualenv_cmd = "virtualenv" + else: + virtualenv_cmd = f"{self.python_cmd} -m venv" + self.check_python_module("ensurepip", 0) + + # Check for needed programs/tools + self.check_perl_module("Pod::Usage", 0) + self.check_python_module("yaml", 0) + self.check_program("make", 0) + self.check_program("gcc", 0) + self.check_program("dot", 1) + self.check_program("convert", 1) + + if self.pdf: + # Extra PDF files - should use 2 for LaTeX is_optional + self.check_program("xelatex", 2) + self.check_program("rsvg-convert", 2) + self.check_program("latexmk", 2) + + # Do distro-specific checks and output distro-install commands + self.check_distros() + + if not self.python_cmd: + if self.need == 1: + sys.exit("Can't build as 1 mandatory dependency is missing") + elif self.need: + sys.exit(f"Can't build as {self.need} mandatory dependencies are missing") + + # Check if sphinx-build is called sphinx-build-3 + if self.need_symlink: + sphinx_path = self.which("sphinx-build-3") + if sphinx_path: + print(f"\tsudo ln -sf {sphinx_path} /usr/bin/sphinx-build\n") + + self.recommend_sphinx_version(virtualenv_cmd) + print("") + + if not self.optional: + print("All optional dependencies are met.") + + if self.need == 1: + sys.exit("Can't build as 1 mandatory dependency is missing") + elif self.need: + sys.exit(f"Can't build as {self.need} mandatory dependencies are missing") + + print("Needed package dependencies are met.") + + def get_system_release(self): + """ + Determine the system type. There's no unique way that would work + with all distros with a minimal package install. So, several + methods are used here. + + By default, it will use lsb_release function. If not available, it will + fail back to reading the known different places where the distro name + is stored. + + Several modern distros now have /etc/os-release, which usually have + a decent coverage. + """ + + if self.which("lsb_release"): + result = self.run(["lsb_release", "-d"], capture_output=True, text=True) + self.system_release = result.stdout.replace("Description:", "").strip() + + release_files = [ + "/etc/system-release", + "/etc/redhat-release", + "/etc/lsb-release", + "/etc/gentoo-release", + ] + + if not self.system_release: + for f in release_files: + self.system_release = self.catcheck(f) + if self.system_release: + break + + # This seems more common than LSB these days + if not self.system_release: + os_var = {} + try: + with open("/etc/os-release", "r", encoding="utf-8") as f: + for line in f: + match = re.match(r"^([\w\d\_]+)=\"?([^\"]*)\"?\n", line) + if match: + os_var[match.group(1)] = match.group(2) + + self.system_release = os_var.get("NAME", "") + if "VERSION_ID" in os_var: + self.system_release += " " + os_var["VERSION_ID"] + elif "VERSION" in os_var: + self.system_release += " " + os_var["VERSION"] + except IOError: + pass + + if not self.system_release: + self.system_release = self.catcheck("/etc/issue") + + self.system_release = self.system_release.strip() + +DESCRIPTION = """ +Process some flags related to Sphinx installation and documentation build. +""" + + +def main(): + parser = argparse.ArgumentParser(description=DESCRIPTION) + + parser.add_argument( + "--no-virtualenv", + action="store_false", + dest="virtualenv", + help="Recommend installing Sphinx instead of using a virtualenv", + ) + + parser.add_argument( + "--no-pdf", + action="store_false", + dest="pdf", + help="Don't check for dependencies required to build PDF docs", + ) + + parser.add_argument( + "--version-check", + action="store_true", + dest="version_check", + help="If version is compatible, don't check for missing dependencies", + ) + + args = parser.parse_args() + + checker = SphinxDependencyChecker(args) + + checker.check_needs() + + +if __name__ == "__main__": + main() -- cgit v1.2.3 From 56a8767751c4bd2b93ba8840a78eccae7018d57c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:21 +0200 Subject: scripts: sphinx-pre-install: Make it compatible with Python 3.6 The minimal version requirements we have is 3.9. Yet, the script which detects it is this one. So, let's try supporting an old version here, as we may want to suggest to upgrade Python version to build the docs. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/39d6e27a047bc3cc8208ac5e11fe6ba44faff9c4.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index dcee2181b72f..71d86b230b22 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -115,7 +115,8 @@ class SphinxDependencyChecker: def find_python_no_venv(): # FIXME: does it makes sense now that this script is in Python? - result = subprocess.run(["pwd"], capture_output=True, text=True) + result = SphinxDependencyChecker.run(["pwd"], capture_output=True, + text=True) cur_dir = result.stdout.strip() python_names = ["python3", "python"] @@ -135,12 +136,23 @@ class SphinxDependencyChecker: def run(*args, **kwargs): """Excecute a command, hiding its output by default""" - if not kwargs.get('capture_output', False): + capture_output = kwargs.pop('capture_output', False) + + if capture_output: + if 'stdout' not in kwargs: + kwargs['stdout'] = subprocess.PIPE + if 'stderr' not in kwargs: + kwargs['stderr'] = subprocess.PIPE + else: if 'stdout' not in kwargs: kwargs['stdout'] = subprocess.DEVNULL if 'stderr' not in kwargs: kwargs['stderr'] = subprocess.DEVNULL + # Don't break with older Python versions + if 'text' in kwargs and sys.version_info < (3, 7): + kwargs['universal_newlines'] = kwargs.pop('text') + return subprocess.run(*args, **kwargs) # -- cgit v1.2.3 From 728648b6a5faa1d6be1e90c04a71481ba33fd08d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:22 +0200 Subject: scripts: sphinx-pre-install: run on a supported version The scripts/sphinx-pre-install is used to detect problems at the system environment and adjust it to build the Kernel documentation. If the version is too old, it won't run, though. Check if the version which started the script is valid. If not, seek for a new one that is compatible with documentation build. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/76627055a7f82f6a79296ddbd873fa5ac8f82a1d.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 65 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 71d86b230b22..3912359d2bae 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -5,6 +5,9 @@ # pylint: disable=C0103,C0114,C0115,C0116,C0301 # pylint: disable=R0902,R0904,R0912,R0915,R1705,R1710,E1121 +# Note: this script requires at least Python 3.6 to run. +# Don't add changes not compatible with it, it is meant to report +# incompatible python versions. import argparse import os @@ -16,7 +19,6 @@ from glob import glob def parse_version(version): """Convert a major.minor.patch version into a tuple""" -# return tuple(int(x) for x in version.split(".")) @@ -27,6 +29,7 @@ def ver_str(version): RECOMMENDED_VERSION = parse_version("3.4.3") +MIN_PYTHON_VERSION = parse_version("3.7") class SphinxDependencyChecker: @@ -132,6 +135,65 @@ class SphinxDependencyChecker: # Python not found at the PATH return python_names[-1] + @staticmethod + def get_python_version(cmd): + + result = SphinxDependencyChecker.run([cmd, "--version"], + capture_output=True, text=True) + version = result.stdout.strip() + + match = re.search(r"(\d+\.\d+\.\d+)", version) + if match: + return parse_version(match.group(1)) + + print(f"Can't parse version {version}") + return (0, 0, 0) + + @staticmethod + def find_python(): + + patterns = [ + "python3.[0-9]", + "python3.[0-9][0-9]", + ] + + new_python_cmd = None + + # Seek for a python binary newer than MIN_PYTHON_VERSION + for path in os.getenv("PATH", "").split(":"): + for pattern in patterns: + for cmd in glob(os.path.join(path, pattern)): + if os.path.isfile(cmd) and os.access(cmd, os.X_OK): + version = SphinxDependencyChecker.get_python_version(cmd) + if version >= MIN_PYTHON_VERSION: + return(cmd) + + @staticmethod + def check_python(): + + cur_ver = sys.version_info[:3] + if cur_ver >= MIN_PYTHON_VERSION: + return + + python_ver = ver_str(cur_ver) + + new_python_cmd = SphinxDependencyChecker.find_python() + if not new_python_cmd: + print(f"ERROR: Python version {python_ver} is not spported anymore") + print(f" Can't find a new version. This script may fail") + return + + # Restart script using the newer version + script_path = os.path.abspath(sys.argv[0]) + args = [new_python_cmd, script_path] + sys.argv[1:] + + print(f"Python {python_ver} not supported. Changing to {new_python_cmd}") + + try: + os.execv(new_python_cmd, args) + except OSError as e: + sys.exit(f"Failed to restart with {new_python_cmd}: {e}") + @staticmethod def run(*args, **kwargs): """Excecute a command, hiding its output by default""" @@ -1107,6 +1169,7 @@ def main(): checker = SphinxDependencyChecker(args) + checker.check_python() checker.check_needs() -- cgit v1.2.3 From ea5dd67722c1ac8ae65ae5a9390452931890f52e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:23 +0200 Subject: scripts: sphinx-pre-install: drop obsolete routines Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/afb97854f05b0852f4c6c1fcee102e9697c14cd8.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 3912359d2bae..b639acd455cc 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -114,27 +114,6 @@ class SphinxDependencyChecker: return None - @staticmethod - def find_python_no_venv(): - # FIXME: does it makes sense now that this script is in Python? - - result = SphinxDependencyChecker.run(["pwd"], capture_output=True, - text=True) - cur_dir = result.stdout.strip() - - python_names = ["python3", "python"] - - for d in os.environ.get("PATH", "").split(":"): - if f"{cur_dir}/sphinx" in d: - continue - - for p in python_names: - if os.access(os.path.join(d, p), os.X_OK): - return os.path.join(d, p) - - # Python not found at the PATH - return python_names[-1] - @staticmethod def get_python_version(cmd): @@ -940,7 +919,7 @@ class SphinxDependencyChecker: else: print("\nSphinx needs to be installed either:\n1) via pip/pypi with:\n") - self.python_cmd = self.find_python_no_venv() + self.python_cmd = os.path.abspath(sys.argv[0]) print(f"\t{virtualenv_cmd} {self.virtenv_dir}") print(f"\t. {self.virtenv_dir}/bin/activate") -- cgit v1.2.3 From f25bf12afc03ce88ca82fd2ed67fbee5c1ed0cd8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:24 +0200 Subject: scripts: sphinx-pre-install: drop support for old virtualenv Up to Python 3.2, the virtual environment were created via virtualenv binary. As we dropped support for such old version, clean up the code. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/31afe394bcfd8f7e450263c1922d2c73b91d36d8.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 71 ++++++++++++------------------------------- 1 file changed, 19 insertions(+), 52 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index b639acd455cc..0a73b1b33842 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -80,7 +80,6 @@ class SphinxDependencyChecker: self.need_symlink = 0 self.need_sphinx = 0 self.need_pip = 0 - self.need_virtualenv = 0 self.rec_sphinx_upgrade = 0 self.verbose_warn_install = 1 @@ -919,12 +918,14 @@ class SphinxDependencyChecker: else: print("\nSphinx needs to be installed either:\n1) via pip/pypi with:\n") - self.python_cmd = os.path.abspath(sys.argv[0]) - - print(f"\t{virtualenv_cmd} {self.virtenv_dir}") - print(f"\t. {self.virtenv_dir}/bin/activate") - print(f"\tpip install -r {self.requirement_file}") - self.deactivate_help() + if not virtualenv_cmd: + print(" Currently not possible.\n") + print(" Please upgrade Python to a newer version and run this script again") + else: + print(f"\t{virtualenv_cmd} {self.virtenv_dir}") + print(f"\t. {self.virtenv_dir}/bin/activate") + print(f"\tpip install -r {self.requirement_file}") + self.deactivate_help() print("\n2) As a package with:") @@ -953,6 +954,7 @@ class SphinxDependencyChecker: def check_needs(self): self.get_system_release() + self.python_cmd = sys.executable # Check if Sphinx is already accessible from current environment self.check_sphinx() @@ -965,56 +967,21 @@ class SphinxDependencyChecker: ver = ver_str(self.cur_version) print(f"Sphinx version: {ver}\n") - # FIXME: Check python command line, trying first python3 - self.python_cmd = self.which("python3") - if not self.python_cmd: - self.python_cmd = self.check_program("python", 0) - # Check the type of virtual env, depending on Python version - if self.python_cmd: - if self.virtualenv: - try: - result = self.run( - [self.python_cmd, "--version"], - capture_output=True, - text=True, - check=True, - ) - - output = result.stdout + result.stderr - - match = re.search(r"(\d+)\.(\d+)\.", output) - if match: - major = int(match.group(1)) - minor = int(match.group(2)) - - if major < 3: - sys.exit("Python 3 is required to build the kernel docs") - if major == 3 and minor < 3: - self.need_virtualenv = True - else: - sys.exit(f"Warning: couldn't identify {self.python_cmd} version!") - - except subprocess.CalledProcessError as e: - sys.exit(f"Error checking Python version: {e}") - else: - self.add_package("python-sphinx", 0) + virtualenv_cmd = None - self.venv_ver = self.recommend_sphinx_upgrade() + if sys.version_info < MIN_PYTHON_VERSION: + min_ver = ver_str(MIN_PYTHON_VERSION) + print(f"ERROR: at least python {min_ver} is required to build the kernel docs") + self.need_sphinx = 1 - virtualenv_cmd = "" + self.venv_ver = self.recommend_sphinx_upgrade() if self.need_pip: - # Set virtualenv command line, if python < 3.3 - # FIXME: can be removed as we're now with an upper min requirement - # but then we need to check python version - if self.need_virtualenv: - virtualenv_cmd = self.which("virtualenv-3") - if not virtualenv_cmd: - virtualenv_cmd = self.which("virtualenv-3.5") - if not virtualenv_cmd: - self.check_program("virtualenv", 0) - virtualenv_cmd = "virtualenv" + if sys.version_info < MIN_PYTHON_VERSION: + self.need_pip = False + print("Warning: python version is not supported.") + else: virtualenv_cmd = f"{self.python_cmd} -m venv" self.check_python_module("ensurepip", 0) -- cgit v1.2.3 From 2cb4877b74349e33be9e209fbcce6114c9a38b9f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:25 +0200 Subject: scripts: sphinx-pre-install: Address issues with OpenSUSE Leap 15.x On openSUSE Leap 15.6, which is the current LTS version, has two Sphinx packages. The normal one requires Python 3.6, which we don't support anymore. However, it also has Python 3.11 with a newer Sphinx version (7.2.6). Suggest the newer version: Detected OS: openSUSE Leap 15.6. ERROR: at least python 3.7 is required to build the kernel docs Warning: python version is not supported. Warning: better to also install "convert". Warning: better to also install "dot". ERROR: please install "yaml", otherwise, build won't work. You should run: sudo zypper install --no-recommends ImageMagick graphviz python311-pyyaml Sphinx needs to be installed either: 1) via pip/pypi with: Currently not possible. Please upgrade Python to a newer version and run this script again 2) As a package with: sudo zypper install --no-recommends python311-Sphinx Please note that Sphinx >= 3.0 will currently produce false-positive warning when the same name is used for more than one type (functions, structs, enums,...). This is known Sphinx bug. For more details, see: https://github.com/sphinx-doc/sphinx/pull/8313 Can't build as 2 mandatory dependencies are missing Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/a1600e292b63f96f40163e350238812158ebd6c2.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 0a73b1b33842..eca42d90ed01 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -556,6 +556,22 @@ class SphinxDependencyChecker: progs["latexmk"] = "texlive-latexmk-bin" + match = re.search(r"(Leap)\s+(\d+).(\d)", self.system_release) + if match: + rel = int(match.group(2)) + + # Leap 15.x uses Python 3.6, which is not compatible with + # the build system anymore. Suggest Python 3.11 + if rel == 15: + if not self.which(self.python_cmd): + self.add_package(self.python_cmd, 0) + + progs.update({ + "python-sphinx": "python311-Sphinx", + "virtualenv": "python311-virtualenv", + "yaml": "python311-pyyaml", + }) + # FIXME: add support for installing CJK fonts # # I tried hard, but was unable to find a way to install -- cgit v1.2.3 From c5ffae0fa965884d09458002e3d990c039602809 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:26 +0200 Subject: scripts: sphinx-pre-install: fix opensuse Leap hint for PyYAML On Leap, the name of the package is python311-PyYAML. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/0266c5e28e136c62de33d2d7f7d5e69b273e0d01.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index eca42d90ed01..65438c198674 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -569,7 +569,7 @@ class SphinxDependencyChecker: progs.update({ "python-sphinx": "python311-Sphinx", "virtualenv": "python311-virtualenv", - "yaml": "python311-pyyaml", + "yaml": "python311-PyYAML", }) # FIXME: add support for installing CJK fonts -- cgit v1.2.3 From 582b0f95c92e0a0ec79d23ed434786251bd4378b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:27 +0200 Subject: scripts: sphinx-pre-install: fix support for gentoo Some gentoo packages have changes. Fix it. While here, improve emerge portage use logic. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/283987633aae7d99e0eff41e24eb6285c1b6b335.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 65438c198674..b793796329c8 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -669,12 +669,13 @@ class SphinxDependencyChecker: def give_gentoo_hints(self): progs = { - "convert": "media-gfx/imagemagick", - "dot": "media-gfx/graphviz", - "rsvg-convert": "gnome-base/librsvg", - "virtualenv": "dev-python/virtualenv", - "xelatex": "dev-texlive/texlive-xetex media-fonts/dejavu", - "yaml": "dev-python/pyyaml", + "convert": "media-gfx/imagemagick", + "dot": "media-gfx/graphviz", + "rsvg-convert": "gnome-base/librsvg", + "virtualenv": "dev-python/virtualenv", + "xelatex": "dev-texlive/texlive-xetex media-fonts/dejavu", + "yaml": "dev-python/pyyaml", + "python-sphinx": "dev-python/sphinx", } if self.pdf: @@ -699,21 +700,17 @@ class SphinxDependencyChecker: print("You should run:") print("\n") - imagemagick = "media-gfx/imagemagick svg png" - cairo = "media-gfx/graphviz cairo pdf" - portage_imagemagick = "/etc/portage/package.use/imagemagick" - portage_cairo = "/etc/portage/package.use/graphviz" - result = self.run(["grep", "imagemagick", portage_imagemagick], - stdout=subprocess.PIPE, text=True) - if not result.stdout.strip(): - print(f"\tsudo su -c 'echo \"{imagemagick}\" > {portage_imagemagick}'") - - result = self.run(["grep", "graphviz", portage_cairo], - stdout=subprocess.PIPE, text=True) + portages = [ + "media-gfx/imagemagick", + "media-gfx/graphviz", + ] - if not result.stdout.strip(): - print(f"\tsudo su -c 'echo \"{cairo}\" > {portage_cairo}'") + for p in portages: + result = self.run(["grep", p, "/etc/portage/package.use/*"], + stdout=subprocess.PIPE, text=True) + if not result.stdout.strip(): + print(f"\tsudo emerge -av1 {p}") print(f"\tsudo emerge --ask {self.install}") -- cgit v1.2.3 From 94a161d998a103346088d97da6d1cfec4df97ddc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:28 +0200 Subject: scripts: sphinx-pre-install: Address issues with OpenSUSE Tumbleweed On Tumbleweed, package names are named after python-313*, as it also has older python versions on it. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/fe0b5f7c18d7b32e0229c0890371e1441ffea294.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index b793796329c8..0e165ad05fdb 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -571,6 +571,14 @@ class SphinxDependencyChecker: "virtualenv": "python311-virtualenv", "yaml": "python311-PyYAML", }) + else: + # Tumbleweed defaults to Python 3.11 + + progs.update({ + "python-sphinx": "python313-Sphinx", + "virtualenv": "python313-virtualenv", + "yaml": "python313-PyYAML", + }) # FIXME: add support for installing CJK fonts # -- cgit v1.2.3 From 12bdcf898977172a93fc30668f8918da83c51f86 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:29 +0200 Subject: scripts: sphinx-pre-install: only show portage hints once On gentoo, doesn't repeat instructions about how to enable portage. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/d4e7ef1d5e1f0412eb9f9ae4913dc64c0821e002.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 0e165ad05fdb..808d31bfa790 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -89,6 +89,9 @@ class SphinxDependencyChecker: self.python_cmd = "" self.activate_cmd = "" + # Certain hints are meant to be shown only once + self.first_hint = True + self.min_version = (0, 0, 0) self.cur_version = (0, 0, 0) self.latest_avail_ver = (0, 0, 0) @@ -714,11 +717,14 @@ class SphinxDependencyChecker: "media-gfx/graphviz", ] - for p in portages: - result = self.run(["grep", p, "/etc/portage/package.use/*"], - stdout=subprocess.PIPE, text=True) - if not result.stdout.strip(): - print(f"\tsudo emerge -av1 {p}") + if self.first_hint: + for p in portages: + result = self.run(["grep", p, "/etc/portage/package.use/*"], + stdout=subprocess.PIPE, text=True) + if not result.stdout.strip(): + print(f"\tsudo emerge -av1 {p}") + + self.first_hint = False print(f"\tsudo emerge --ask {self.install}") -- cgit v1.2.3 From 8f6f54c464d7fccfb8da46ca0afcb0860e6d73bb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:30 +0200 Subject: scripts: sphinx-pre-install: cleanup rhel support Rhel < 8.0 is not supported anymore. Drop support for it. Rhel 8 is problematic: at least on the tests I did with a docker repo, it didn't work, but it could be due to the issue that it is actually different than a real One. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/62fe8ab243ad39f4964f1f74b965e43dc8f10e23.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 808d31bfa790..42f55e67256d 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -487,15 +487,31 @@ class SphinxDependencyChecker: progs["virtualenv"] = "python-virtualenv" - if rel and rel < 8: + if not rel or rel < 8: old = 1 self.pdf = False - # RHEL 7 is in ELS, currently up to Jun, 2026 + print("ERROR: Distro not supported. Too old?") + return + + # TODO: check if RHEL8 still works. + # On my tests with docker "redhat/ubi8" image, there's no + # python3-sphinx (or similar) package. It comes with Python 3.6, + # but there are other python packages over there, so it may be + # possible to work with venv. + + if self.first_hint: + print("Note: RHEL-based distros typically require extra repositories.\n" \ + "For most, enabling epel and crb are enough:\n" \ + "\tsudo dnf install -y epel-release", \ + "\tsudo dnf config-manager --set-enabled crb\n" \ + "Yet, some may have other required repositories. Those commands could be useful:" \ + "\tsudo dnf repolist all" \ + "\tsudo repoquery --available --info ", + "\tsudo dnf config-manager --set-enabled '*' # enable all - probably not what you want") + + self.first_hint = False - print("Note: texlive packages on RHEL/CENTOS <= 7 are incomplete. Can't support PDF output") - print("If you want to build PDF, please read:") - print("\thttps://www.systutorials.com/241660/how-to-install-tex-live-on-centos-7-linux/") if self.pdf: pdf_pkgs = [ -- cgit v1.2.3 From 637fa6b38113fddca7da2f0507f709f46a44047a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:31 +0200 Subject: scripts: sphinx-pre-install: output Python and docutils version Specially when debugging issues, knowing the versions is important. Add it to the script output. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/96142ec916b9064c859b200639c3595115080322.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 42f55e67256d..de5bcfd052b5 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -154,6 +154,20 @@ class SphinxDependencyChecker: cur_ver = sys.version_info[:3] if cur_ver >= MIN_PYTHON_VERSION: + ver = ver_str(cur_ver) + print(f"Python version: {ver}") + + # This could be useful for debugging purposes + if SphinxDependencyChecker.which("docutils"): + result = SphinxDependencyChecker.run(["docutils", "--version"], + capture_output=True, text=True) + ver = result.stdout.strip() + match = re.search(r"(\d+\.\d+\.\d+)", ver) + if match: + ver = match.group(1) + + print(f"Docutils version: {ver}") + return python_ver = ver_str(cur_ver) -- cgit v1.2.3 From cccc5389811a34eccbd0f531c89d284f1cebdd70 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:32 +0200 Subject: scripts: sphinx-pre-install: add a missing f-string marker I forgot one f-string marker, with turned to be affecting 3 lines, because of cut-and-paste ;-) Use the proper f-string marker to print Sphinx version at the hint lines. Yet, we don't want to print as a tuple, so call ver_str() for it. Ideally, we would be placing it directly at the f-string, but Python 3.6 f-string support was pretty much limited. Only 3.12 (PEP 701) makes it similar to Perl, allowing expressions inside it. It sounds that function call itself was introduced on 3.7. As we explicitly want this one to run on 3.6, as latest Leap comes with it, we can't use function calls on f-string. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/b0ad1795446b17a00ba2dd83f366e784253668e6.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index de5bcfd052b5..6a244105f7ef 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -914,12 +914,15 @@ class SphinxDependencyChecker: if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION: return + if self.latest_avail_ver: + latest_avail_ver = ver_str(self.latest_avail_ver) + if not self.need_sphinx: # sphinx-build is present and its version is >= $min_version # only recommend enabling a newer virtenv version if makes sense. if self.latest_avail_ver and self.latest_avail_ver > self.cur_version: - print("\nYou may also use the newer Sphinx version {self.latest_avail_ver} with:") + print(f"\nYou may also use the newer Sphinx version {latest_avail_ver} with:") if f"{self.virtenv_prefix}" in os.getcwd(): print("\tdeactivate") print(f"\t. {self.activate_cmd}") @@ -940,7 +943,7 @@ class SphinxDependencyChecker: # installed one via virtenv with a newer version. # So, print commands to enable it if self.latest_avail_ver > self.cur_version: - print("\nYou may also use the Sphinx virtualenv version {self.latest_avail_ver} with:") + print(f"\nYou may also use the Sphinx virtualenv version {latest_avail_ver} with:") if f"{self.virtenv_prefix}" in os.getcwd(): print("\tdeactivate") print(f"\t. {self.activate_cmd}") @@ -954,7 +957,7 @@ class SphinxDependencyChecker: # Suggest newer versions if current ones are too old if self.latest_avail_ver and self.latest_avail_ver >= self.min_version: if self.latest_avail_ver >= RECOMMENDED_VERSION: - print("\nNeed to activate Sphinx (version {self.latest_avail_ver}) on virtualenv with:") + print(f"\nNeed to activate Sphinx (version {latest_avail_ver}) on virtualenv with:") print(f"\t. {self.activate_cmd}") self.deactivate_help() return -- cgit v1.2.3 From 8b18e86f6c0063e33096bf84ffcd9fb9ced4844d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:33 +0200 Subject: scripts: sphinx-pre-install: fix Leap support for rsvg-convert There is a test logic meant to be for Leap, renaming rsvg-convert package. Well, at least on latest Leap releases, this is wrong, causing install to fail. Drop it. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/6fcc94533d860e2f6f4f2c7d6ceb6cca70f48bb6.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 6a244105f7ef..6b5f9eda91bb 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -566,10 +566,6 @@ class SphinxDependencyChecker: "yaml": "python3-pyyaml", } - # On Tumbleweed, this package is also named rsvg-convert - if not re.search(r"Tumbleweed", self.system_release): - progs["rsvg-convert"] = "rsvg-view" - suse_tex_pkgs = [ "texlive-babel-english", "texlive-caption", -- cgit v1.2.3 From e53e6d395fe036bb2c24dc79dc728e9a82ee0b82 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:34 +0200 Subject: scripts: sphinx-pre-install: fix rhel recomendations Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/393a299a850ba9d94c6a8965e78db4da2dbf7e37.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 6b5f9eda91bb..1f64909953b9 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -519,9 +519,9 @@ class SphinxDependencyChecker: "For most, enabling epel and crb are enough:\n" \ "\tsudo dnf install -y epel-release", \ "\tsudo dnf config-manager --set-enabled crb\n" \ - "Yet, some may have other required repositories. Those commands could be useful:" \ - "\tsudo dnf repolist all" \ - "\tsudo repoquery --available --info ", + "Yet, some may have other required repositories. Those commands could be useful:\n" \ + "\tsudo dnf repolist all\n" \ + "\tsudo dnf repoquery --available --info \n", "\tsudo dnf config-manager --set-enabled '*' # enable all - probably not what you want") self.first_hint = False -- cgit v1.2.3 From 42e3f9f360e70bea4ee7d4e895f042d86ee11500 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:35 +0200 Subject: scripts: sphinx-pre-install: remove Scientific Linux According with its website: https://scientificlinux.org/ Scientific Linux reached end of life in June 30, 2024. Also, it was based on RHEL 7, which is not compatible with our build system anymore. So, drop support for it. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/dde5e0c95017022840f8a522ce44759e51f52aa1.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 1f64909953b9..cd9c83b17971 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -769,7 +769,6 @@ class SphinxDependencyChecker: re.compile("openEuler"): self.give_redhat_hints, re.compile("Oracle Linux Server"): self.give_redhat_hints, re.compile("Rocky Linux"): self.give_redhat_hints, - re.compile("Scientific Linux"): self.give_redhat_hints, re.compile("Springdale Open Enterprise"): self.give_redhat_hints, re.compile("Ubuntu"): self.give_debian_hints, -- cgit v1.2.3 From 1a7da749f18335d7996df3004225ca7418a1052d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:36 +0200 Subject: scripts: sphinx-pre-install: improve Gentoo package deps logic It took me a lot of time, but I guess understand now what it takes to install a package on Gentoo. Handling dependencies is a nightmare, as Gentoo refuses to emerge some packages if there's no package.use file describing them. To make it worse, compilation flags shall also be present there for some packages. If USE is not perfect, error/warning messages like those are shown: gnome-base/librsvg dev-texlive/texlive-xetex media-fonts/dejavu dev-python/pyyaml ... !!! The following binary packages have been ignored due to non matching USE: =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_13 qt6 svg =media-gfx/graphviz-12.2.1-r1 X pdf python_single_target_python3_12 -python_single_target_python3_13 qt6 svg =media-gfx/graphviz-12.2.1-r1 X pdf qt6 svg =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 qt6 svg =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 python_single_target_python3_12 -python_single_target_python3_13 qt6 svg =media-fonts/noto-cjk-20190416 X =app-text/texlive-core-2024-r1 X cjk -xetex =app-text/texlive-core-2024-r1 X -xetex =app-text/texlive-core-2024-r1 -xetex =dev-libs/zziplib-0.13.79-r1 sdl If emerge is allowed, it will simply ignore the above packages, creating an incomplete installation, which will later fail when one tries to build docs with images or build PDFs. After the fix, command line commands to produce the needed USE chain will be emitted, if they don't exist yet. sudo su -c 'echo "media-gfx/graphviz" > /etc/portage/package.use/graphviz' sudo su -c 'echo "media-gfx/imagemagick" > /etc/portage/package.use/imagemagick' sudo su -c 'echo "media-libs/harfbuzz icu" > /etc/portage/package.use/media-libs' sudo su -c 'echo "media-fonts/noto-cjk" > /etc/portage/package.use/media-fonts' sudo su -c 'echo "app-text/texlive-core xetex" > /etc/portage/package.use/texlive' sudo su -c 'echo "dev-libs/zziplib sdl" > /etc/portage/package.use/zziblib' The new logic tries to be smart enough to detect for missing files and missing arguments. Yet, as Gentoo seems to require users to manage those package.use files by hand, the logic isn't perfect: users may still need to verify for conflicts on different use files. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/365fe5e7d568da932dcffde65f48f2c1256cb773.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 88 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index cd9c83b17971..94f3d2e32fd6 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -737,22 +737,88 @@ class SphinxDependencyChecker: print("You should run:") print("\n") - - portages = [ - "media-gfx/imagemagick", - "media-gfx/graphviz", - ] + # Handling dependencies is a nightmare, as Gentoo refuses to emerge + # some packages if there's no package.use file describing them. + # To make it worse, compilation flags shall also be present there + # for some packages. If USE is not perfect, error/warning messages + # like those are shown: + # + # !!! The following binary packages have been ignored due to non matching USE: + # + # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_13 qt6 svg + # =media-gfx/graphviz-12.2.1-r1 X pdf python_single_target_python3_12 -python_single_target_python3_13 qt6 svg + # =media-gfx/graphviz-12.2.1-r1 X pdf qt6 svg + # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 qt6 svg + # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 python_single_target_python3_12 -python_single_target_python3_13 qt6 svg + # =media-fonts/noto-cjk-20190416 X + # =app-text/texlive-core-2024-r1 X cjk -xetex + # =app-text/texlive-core-2024-r1 X -xetex + # =app-text/texlive-core-2024-r1 -xetex + # =dev-libs/zziplib-0.13.79-r1 sdl + # + # And will ignore such packages, installing the remaining ones. That + # affects mostly the image extension and PDF generation. + + # Package dependencies and the minimal needed args: + portages = { + "graphviz": "media-gfx/graphviz", + "imagemagick": "media-gfx/imagemagick", + "media-libs": "media-libs/harfbuzz icu", + "media-fonts": "media-fonts/noto-cjk", + "texlive": "app-text/texlive-core xetex", + "zziblib": "dev-libs/zziplib sdl", + } if self.first_hint: - for p in portages: - result = self.run(["grep", p, "/etc/portage/package.use/*"], - stdout=subprocess.PIPE, text=True) - if not result.stdout.strip(): - print(f"\tsudo emerge -av1 {p}") + use_base = "/etc/portage/package.use" + files = glob(f"{use_base}/*") + + for fname, portage in portages.items(): + install = False + + while install == False: + if not files: + # No files under package.usage. Install all + install = True + break + + args = portage.split(" ") + + name = args.pop(0) + + cmd = ["grep", "-l", "-E", rf"^{name}\b" ] + files + result = self.run(cmd, stdout=subprocess.PIPE, text=True) + if result.returncode or not result.stdout.strip(): + # File containing portage name not found + install = True + break + + # Ensure that needed USE flags are present + if args: + match_fname = result.stdout.strip() + with open(match_fname, 'r', encoding='utf8', + errors='backslashreplace') as fp: + for line in fp: + for arg in args: + if arg.startswith("-"): + continue + + if not re.search(rf"\s*{arg}\b", line): + # Needed file argument not found + install = True + break + + # Everything looks ok, don't install + break + + # emit a code to setup missing USE + if install: + print(f"\tsudo su -c 'echo \"{portage}\" > {use_base}/{fname}'") self.first_hint = False - print(f"\tsudo emerge --ask {self.install}") + # Now, we can use emerge and let it respect USE + print(f"\tsudo emerge --ask --changed-use --binpkg-respect-use=y {self.install}") # # Dispatch the check to an os_specific hinter -- cgit v1.2.3 From fb08659be07e646f8103449e02d5e58b32c284d9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:37 +0200 Subject: scripts: sphinx-pre-install: fix OpenMandriva support OpenMandriva Lx 4.3 has different package names for ImageMagick and yaml. Fix them to ensure that system setup will pass. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/0b4e7aa88c96e6a5b8f2e6f381b3e21124680d33.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 94f3d2e32fd6..2f6036eadc94 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -647,6 +647,11 @@ class SphinxDependencyChecker: packager_cmd = "dnf install" noto_sans = "noto-sans-cjk-fonts" tex_pkgs = ["texlive-collection-fontsextra"] + + # Tested on OpenMandriva Lx 4.3 + progs["convert"] = "imagemagick" + progs["yaml"] = "python-pyyaml" + else: packager_cmd = "urpmi" noto_sans = "google-noto-sans-cjk-ttc-fonts" -- cgit v1.2.3 From 1ad72e9dfa5ef24ca489b7cbaa5f6f85b1fe87d8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:38 +0200 Subject: scripts: sphinx-pre-install: move package instructions to a new func Currently, if Python < 3.7, package install will fail. That happens with OpenSuse Leap and RHEL-based ver 8 distros. OpenSuse allows installing Sphinx with Python 3.11, but RHEL-based distros don't. Prepare to recomend only venv on such cases. For now, just split the recomendation on a new function that will check for a paramtere to be called. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/4fb2181c960e89774309a833f80209a1a3ab10d2.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 44 +++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 2f6036eadc94..2360ca2ed21c 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -89,6 +89,9 @@ class SphinxDependencyChecker: self.python_cmd = "" self.activate_cmd = "" + # Some distros may not have a Sphinx shipped package compatible with + # our minimal requirements + self.package_supported = True # Certain hints are meant to be shown only once self.first_hint = True @@ -969,6 +972,27 @@ class SphinxDependencyChecker: return self.latest_avail_ver + def recommend_package(self): + + print("\n2) As a package with:") + + old_need = self.need + old_optional = self.optional + self.missing = {} + self.pdf = False + self.optional = 0 + self.install = "" + old_verbose = self.verbose_warn_install + self.verbose_warn_install = 0 + + self.add_package("python-sphinx", 0) + + self.check_distros() + + self.need = old_need + self.optional = old_optional + self.verbose_warn_install = old_verbose + def recommend_sphinx_version(self, virtualenv_cmd): # The logic here is complex, as it have to deal with different versions: # - minimal supported version; @@ -1053,24 +1077,8 @@ class SphinxDependencyChecker: print(f"\tpip install -r {self.requirement_file}") self.deactivate_help() - print("\n2) As a package with:") - - old_need = self.need - old_optional = self.optional - self.missing = {} - self.pdf = False - self.optional = 0 - self.install = "" - old_verbose = self.verbose_warn_install - self.verbose_warn_install = 0 - - self.add_package("python-sphinx", 0) - - self.check_distros() - - self.need = old_need - self.optional = old_optional - self.verbose_warn_install = old_verbose + if self.package_supported: + self.recommend_package() print("\n" \ " Please note that Sphinx >= 3.0 will currently produce false-positive\n" \ -- cgit v1.2.3 From 6db1d3977baf7ac895033624897d38fb60cbe3f7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:39 +0200 Subject: scripts: sphinx-pre-install: adjust a warning message There is one extra space at the first line. Also, as now we only support Python 3.4+, update the text. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/74a17edd70364ca623a54b62bd97a344bb474988.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 2360ca2ed21c..365590f81551 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -1081,8 +1081,8 @@ class SphinxDependencyChecker: self.recommend_package() print("\n" \ - " Please note that Sphinx >= 3.0 will currently produce false-positive\n" \ - " warning when the same name is used for more than one type (functions,\n" \ + " Please note that Sphinx currentlys produce false-positive\n" \ + " warnings when the same name is used for more than one type (functions,\n" \ " structs, enums,...). This is known Sphinx bug. For more details, see:\n" \ "\thttps://github.com/sphinx-doc/sphinx/pull/8313") -- cgit v1.2.3 From 8cd256524a920782268a0dd7e3e0404c34bfc65f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:40 +0200 Subject: scripts: sphinx-pre-install: better handle Python min version Don't do any recommendations about Sphinx install with too old python versions. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/013aeb848ecc3f6b69b4518cf3d335bd2353b6e1.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 365590f81551..a5c777e529ec 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -92,6 +92,10 @@ class SphinxDependencyChecker: # Some distros may not have a Sphinx shipped package compatible with # our minimal requirements self.package_supported = True + + # Recommend a new python version + self.recommend_python = None + # Certain hints are meant to be shown only once self.first_hint = True @@ -511,11 +515,11 @@ class SphinxDependencyChecker: print("ERROR: Distro not supported. Too old?") return - # TODO: check if RHEL8 still works. - # On my tests with docker "redhat/ubi8" image, there's no - # python3-sphinx (or similar) package. It comes with Python 3.6, - # but there are other python packages over there, so it may be - # possible to work with venv. + # RHEL 8 uses Python 3.6, which is not compatible with + # the build system anymore. Suggest Python 3.11 + if rel == 8: + self.add_package("python39", 0) + self.recommend_python = True if self.first_hint: print("Note: RHEL-based distros typically require extra repositories.\n" \ @@ -596,6 +600,7 @@ class SphinxDependencyChecker: # the build system anymore. Suggest Python 3.11 if rel == 15: if not self.which(self.python_cmd): + self.recommend_python = True self.add_package(self.python_cmd, 0) progs.update({ @@ -1000,6 +1005,11 @@ class SphinxDependencyChecker: # - recommended version. # It also needs to work fine with both distro's package and venv/virtualenv + if self.recommend_python: + print("\nPython version is incompatible with doc build.\n" \ + "Please upgrade it and re-run.\n") + return + # Version is OK. Nothing to do. if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION: return -- cgit v1.2.3 From 9ecda2e10101537e97bcb2b05049e58306ac4e1b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:41 +0200 Subject: scripts: sphinx-pre-install: convert is_optional to a class When is_optional was added in Perl, it was a boolean. With time, it ended becoming a sort of enum, which makes the module harder to maintain. Convert it to a enum-like class and add more options to it. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/42290a24f3b1dbea9ebe19747cf5622bb2f2cf5c.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 192 ++++++++++++++++++++++++++++-------------- 1 file changed, 130 insertions(+), 62 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index a5c777e529ec..0963da21c27b 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -32,6 +32,53 @@ RECOMMENDED_VERSION = parse_version("3.4.3") MIN_PYTHON_VERSION = parse_version("3.7") +class DepType: + + # Internal types of dependencies. + _SYS_TYPE = 0 + _PHY_TYPE = 1 + _PDF_TYPE = 2 + + # Let's define keys as a tuple with the type and mandatory/optional. + # This way, checking for optional or type is easy. + + SYSTEM_MANDATORY = (_SYS_TYPE, True) + PYTHON_MANDATORY = (_PHY_TYPE, True) + PDF_MANDATORY = (_PDF_TYPE, True) + + # Currently we're not using all optional types, but let's keep all + # combinations here, as we may end needing them in the future. Also, + # it allows a name() function that handles all possibilities. + SYSTEM_OPTIONAL = (_SYS_TYPE, False) + PYTHON_OPTIONAL = (_PHY_TYPE, False) + PDF_OPTIONAL = (_PDF_TYPE, True) + + @staticmethod + def name(dtype): + if dtype[0] == DepType._SYS_TYPE: + msg = "build" + elif dtype[0] == DepType._PHY_TYPE: + msg = "Python" + else: + msg = "PDF" + + if dtype[1]: + return f"ERROR: {msg} mandatory deps missing" + else: + out = f"Warning: {msg} optional deps missing" + + @staticmethod + def is_optional(dtype): + return not dtype[1] + + @staticmethod + def is_pdf(dtype): + if (dtype[0] == DepType._PDF_TYPE): + return True + + return False + + class SphinxDependencyChecker: # List of required texlive packages on Fedora and OpenSuse texlive = { @@ -223,56 +270,68 @@ class SphinxDependencyChecker: # Methods to check if a feature exists # - # Note: is_optional has 3 states: - # - 0: mandatory - # - 1: optional, but nice to have - # - 2: LaTeX optional - pdf builds without it, but may have visual impact - def check_missing(self, progs): - for prog, is_optional in sorted(self.missing.items()): + run = {} + + for prog, dtype in sorted(self.missing.items()): # At least on some LTS distros like CentOS 7, texlive doesn't # provide all packages we need. When such distros are # detected, we have to disable PDF output. # # So, we need to ignore the packages that distros would # need for LaTeX to work - if is_optional == 2 and not self.pdf: + if DepType.is_pdf(dtype) and not self.pdf: self.optional -= 1 continue + if not dtype in run: + run[dtype] = [] + + run[dtype].append(prog) + + output_msg = "" + + for dtype in sorted(run.keys()): + progs = " ".join(run[dtype]) + if self.verbose_warn_install: - if is_optional: - print(f'Warning: better to also install "{prog}".') - else: - print(f'ERROR: please install "{prog}", otherwise, build won\'t work.') + try: + name = DepType.name(dtype) + output_msg += f'{name}:\t{progs}\n' + except KeyError: + raise KeyError(f"ERROR!!!: invalid dtype for {progs}: {dtype}") - self.install += " " + progs.get(prog, prog) + self.install += " " + progs + + if output_msg: + print(f"\n{output_msg}\n") self.install = self.install.lstrip() - def add_package(self, package, is_optional): - self.missing[package] = is_optional + def add_package(self, package, dtype): + is_optional = DepType.is_optional(dtype) + self.missing[package] = dtype if is_optional: self.optional += 1 else: self.need += 1 - def check_missing_file(self, files, package, is_optional): + def check_missing_file(self, files, package, dtype): for f in files: if os.path.exists(f): return - self.add_package(package, is_optional) + self.add_package(package, dtype) - def check_program(self, prog, is_optional): + def check_program(self, prog, dtype): found = self.which(prog) if found: return found - self.add_package(prog, is_optional) + self.add_package(prog, dtype) return None - def check_perl_module(self, prog, is_optional): + def check_perl_module(self, prog, dtype): # While testing with lxc download template, one of the # distros (Oracle) didn't have perl - nor even an option to install # before installing oraclelinux-release-el9 package. @@ -281,46 +340,52 @@ class SphinxDependencyChecker: # add it as a mandatory package, as some parts of the doc builder # needs it. if not self.which("perl"): - self.add_package("perl", 0) - self.add_package(prog, is_optional) + self.add_package("perl", DepType.SYSTEM_MANDATORY) + self.add_package(prog, dtype) return try: self.run(["perl", f"-M{prog}", "-e", "1"], check=True) except subprocess.CalledProcessError: - self.add_package(prog, is_optional) + self.add_package(prog, dtype) - def check_python_module(self, module, is_optional): - # FIXME: is it needed at the Python version? Maybe due to venv? - if not self.python_cmd: - return + def check_python_module(self, module, is_optional=False): + if is_optional: + dtype = DepType.PYTHON_OPTIONAL + else: + dtype = DepType.PYTHON_MANDATORY try: self.run([self.python_cmd, "-c", f"import {module}"], check=True) except subprocess.CalledProcessError: - self.add_package(module, is_optional) + self.add_package(module, dtype) - def check_rpm_missing(self, pkgs, is_optional): + def check_rpm_missing(self, pkgs, dtype): for prog in pkgs: try: self.run(["rpm", "-q", prog], check=True) except subprocess.CalledProcessError: - self.add_package(prog, is_optional) + self.add_package(prog, dtype) - def check_pacman_missing(self, pkgs, is_optional): + def check_pacman_missing(self, pkgs, dtype): for prog in pkgs: try: self.run(["pacman", "-Q", prog], check=True) except subprocess.CalledProcessError: - self.add_package(prog, is_optional) + self.add_package(prog, dtype) + + def check_missing_tex(self, is_optional=False): + if is_optional: + dtype = DepType.PDF_OPTIONAL + else: + dtype = DepType.PDF_MANDATORY - def check_missing_tex(self, is_optional): kpsewhich = self.which("kpsewhich") for prog, package in self.texlive.items(): # If kpsewhich is not there, just add it to deps if not kpsewhich: - self.add_package(package, is_optional) + self.add_package(package, dtype) continue # Check if the package is needed @@ -331,11 +396,11 @@ class SphinxDependencyChecker: # Didn't find. Add it if not result.stdout.strip(): - self.add_package(package, is_optional) + self.add_package(package, dtype) except subprocess.CalledProcessError: # kpsewhich returned an error. Add it, just in case - self.add_package(package, is_optional) + self.add_package(package, dtype) def get_sphinx_fname(self): if "SPHINXBUILD" in os.environ: @@ -446,9 +511,9 @@ class SphinxDependencyChecker: } for package, files in pdf_pkgs.items(): - self.check_missing_file(files, package, 2) + self.check_missing_file(files, package, DepType.PDF_MANDATORY) - self.check_program("dvipng", 2) + self.check_program("dvipng", DepType.PDF_MANDATORY) self.check_missing(progs) @@ -518,7 +583,7 @@ class SphinxDependencyChecker: # RHEL 8 uses Python 3.6, which is not compatible with # the build system anymore. Suggest Python 3.11 if rel == 8: - self.add_package("python39", 0) + self.add_package("python39", DepType.SYSTEM_MANDATORY) self.recommend_python = True if self.first_hint: @@ -540,13 +605,13 @@ class SphinxDependencyChecker: "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc", ] - self.check_missing_file(pdf_pkgs, noto_sans_redhat, 2) + self.check_missing_file(pdf_pkgs, noto_sans_redhat, DepType.PDF_MANDATORY) if not old: - self.check_rpm_missing(fedora26_opt_pkgs, 2) - self.check_rpm_missing(fedora_tex_pkgs, 2) + self.check_rpm_missing(fedora26_opt_pkgs, DepType.PDF_MANDATORY) + self.check_rpm_missing(fedora_tex_pkgs, DepType.PDF_MANDATORY) - self.check_missing_tex(2) + self.check_missing_tex() self.check_missing(progs) @@ -601,7 +666,7 @@ class SphinxDependencyChecker: if rel == 15: if not self.which(self.python_cmd): self.recommend_python = True - self.add_package(self.python_cmd, 0) + self.add_package(self.python_cmd, DepType.SYSTEM_MANDATORY) progs.update({ "python-sphinx": "python311-Sphinx", @@ -623,9 +688,9 @@ class SphinxDependencyChecker: # "Noto Sans CJK SC" on openSUSE if self.pdf: - self.check_rpm_missing(suse_tex_pkgs, 2) + self.check_rpm_missing(suse_tex_pkgs, DepType.PDF_MANDATORY) if self.pdf: - self.check_missing_tex(2) + self.check_missing_tex() self.check_missing(progs) if not self.need and not self.optional: @@ -672,8 +737,8 @@ class SphinxDependencyChecker: "/usr/share/fonts/TTF/NotoSans-Regular.ttf", ] - self.check_missing_file(pdf_pkgs, noto_sans, 2) - self.check_rpm_missing(tex_pkgs, 2) + self.check_missing_file(pdf_pkgs, noto_sans, DepType.PDF_MANDATORY) + self.check_rpm_missing(tex_pkgs, DepType.PDF_MANDATORY) self.check_missing(progs) @@ -701,7 +766,7 @@ class SphinxDependencyChecker: ] if self.pdf: - self.check_pacman_missing(archlinux_tex_pkgs, 2) + self.check_pacman_missing(archlinux_tex_pkgs, DepType.PDF_MANDATORY) self.check_missing_file( ["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], @@ -739,7 +804,7 @@ class SphinxDependencyChecker: ], } for package, files in pdf_pkgs.items(): - self.check_missing_file(files, package, 2) + self.check_missing_file(files, package, DepType.PDF_MANDATORY) self.check_missing(progs) @@ -878,7 +943,7 @@ class SphinxDependencyChecker: # progs = {"sphinx-build": "sphinx"} if self.pdf: - self.check_missing_tex(2) + self.check_missing_tex() self.check_missing(progs) @@ -990,7 +1055,7 @@ class SphinxDependencyChecker: old_verbose = self.verbose_warn_install self.verbose_warn_install = 0 - self.add_package("python-sphinx", 0) + self.add_package("python-sphinx", DepType.PYTHON_MANDATORY) self.check_distros() @@ -1010,6 +1075,7 @@ class SphinxDependencyChecker: "Please upgrade it and re-run.\n") return + # Version is OK. Nothing to do. if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION: return @@ -1128,21 +1194,23 @@ class SphinxDependencyChecker: else: virtualenv_cmd = f"{self.python_cmd} -m venv" - self.check_python_module("ensurepip", 0) + self.check_python_module("ensurepip") # Check for needed programs/tools - self.check_perl_module("Pod::Usage", 0) - self.check_python_module("yaml", 0) - self.check_program("make", 0) - self.check_program("gcc", 0) - self.check_program("dot", 1) - self.check_program("convert", 1) + self.check_perl_module("Pod::Usage", DepType.SYSTEM_MANDATORY) + + self.check_program("make", DepType.SYSTEM_MANDATORY) + self.check_program("gcc", DepType.SYSTEM_MANDATORY) + + self.check_program("dot", DepType.SYSTEM_OPTIONAL) + self.check_program("convert", DepType.SYSTEM_OPTIONAL) + + self.check_python_module("yaml") if self.pdf: - # Extra PDF files - should use 2 for LaTeX is_optional - self.check_program("xelatex", 2) - self.check_program("rsvg-convert", 2) - self.check_program("latexmk", 2) + self.check_program("xelatex", DepType.PDF_MANDATORY) + self.check_program("rsvg-convert", DepType.PDF_MANDATORY) + self.check_program("latexmk", DepType.PDF_MANDATORY) # Do distro-specific checks and output distro-install commands self.check_distros() -- cgit v1.2.3 From 272f5e0390dd9b5f62b480eae482a67d2493630e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:42 +0200 Subject: scripts: sphinx-pre-install: better handle RHEL-based distros Better implement support for RHEL-based distros. While here, get rid of a Fedora 28 support which cause troubles with server distros. Also, get rid of yum, as RHEL8 already suppords dnf, and this is not the minimal version we may still support. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/4d1b27d3a381f011e150bb50176babba83af9e1a.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 0963da21c27b..592223fa686f 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -536,10 +536,6 @@ class SphinxDependencyChecker: "yaml": "python3-pyyaml", } - fedora26_opt_pkgs = [ - "graphviz-gd", # Fedora 26: needed for PDF support - ] - fedora_tex_pkgs = [ "dejavu-sans-fonts", "dejavu-sans-mono-fonts", @@ -549,9 +545,8 @@ class SphinxDependencyChecker: "texlive-xecjk", ] - old = 0 + fedora = False rel = None - pkg_manager = "dnf" match = re.search(r"(release|Linux)\s+(\d+)", self.system_release) if match: @@ -559,12 +554,12 @@ class SphinxDependencyChecker: if not rel: print("Couldn't identify release number") - old = 1 self.pdf = False elif re.search("Fedora", self.system_release): # Fedora 38 and upper use this CJK font noto_sans_redhat = "google-noto-sans-cjk-fonts" + fedora = True else: # Almalinux, CentOS, RHEL, ... @@ -574,9 +569,6 @@ class SphinxDependencyChecker: progs["virtualenv"] = "python-virtualenv" if not rel or rel < 8: - old = 1 - self.pdf = False - print("ERROR: Distro not supported. Too old?") return @@ -607,11 +599,16 @@ class SphinxDependencyChecker: self.check_missing_file(pdf_pkgs, noto_sans_redhat, DepType.PDF_MANDATORY) - if not old: - self.check_rpm_missing(fedora26_opt_pkgs, DepType.PDF_MANDATORY) - self.check_rpm_missing(fedora_tex_pkgs, DepType.PDF_MANDATORY) + self.check_rpm_missing(fedora_tex_pkgs, DepType.PDF_MANDATORY) + + self.check_missing_tex(DepType.PDF_MANDATORY) + + # There's no texlive-ctex on RHEL 8 repositories. This will + # likely affect CJK pdf build only. + if not fedora and rel == 8: + if "texlive-ctex" in self.missing: + del self.missing["texlive-ctex"] - self.check_missing_tex() self.check_missing(progs) @@ -621,11 +618,7 @@ class SphinxDependencyChecker: if self.verbose_warn_install: print("You should run:") - if old: - # dnf is there since Fedora 18+ and RHEL 8 - pkg_manager = "yum" - - print(f"\n\tsudo {pkg_manager} install -y {self.install}") + print(f"\n\tsudo dnf install -y {self.install}") def give_opensuse_hints(self): progs = { -- cgit v1.2.3 From 2cab00fb178a31bca45adc1ff5f0994679d116a4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:43 +0200 Subject: scripts: sphinx-pre-install: move missing logic to a separate class Better manage dependencies by placing them on a distro-independent class. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/f4f5bf276e07dc494f5dc83c4c2d087be7f790e6.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 251 +++++++++++++++++++++++------------------- 1 file changed, 138 insertions(+), 113 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 592223fa686f..47dce1fcddfb 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -32,7 +32,7 @@ RECOMMENDED_VERSION = parse_version("3.4.3") MIN_PYTHON_VERSION = parse_version("3.7") -class DepType: +class DepManager: # Internal types of dependencies. _SYS_TYPE = 0 @@ -53,11 +53,18 @@ class DepType: PYTHON_OPTIONAL = (_PHY_TYPE, False) PDF_OPTIONAL = (_PDF_TYPE, True) + def __init__(self, pdf): + self.missing = {} + self.run = {} + self.need = 0 + self.optional = 0 + self.pdf = pdf + @staticmethod def name(dtype): - if dtype[0] == DepType._SYS_TYPE: + if dtype[0] == DepManager._SYS_TYPE: msg = "build" - elif dtype[0] == DepType._PHY_TYPE: + elif dtype[0] == DepManager._PHY_TYPE: msg = "Python" else: msg = "PDF" @@ -73,11 +80,75 @@ class DepType: @staticmethod def is_pdf(dtype): - if (dtype[0] == DepType._PDF_TYPE): + if (dtype[0] == DepManager._PDF_TYPE): return True return False + def add_package(self, package, dtype): + is_optional = DepManager.is_optional(dtype) + self.missing[package] = dtype + if is_optional: + self.optional += 1 + else: + self.need += 1 + + def del_package(self, package): + if package in self.missing: + del self.missing[package] + + def clear_deps(self): + """ + Clear dependencies without changing needed/optional. + + This is an ackward way to have a separate section to recommend + a package after system main dependencies. + + TODO: rework the logic to prevent needing it + """ + + self.missing = {} + + def check_missing(self, progs): + self.run = {} + + for prog, dtype in sorted(self.missing.items()): + # At least on some LTS distros like CentOS 7, texlive doesn't + # provide all packages we need. When such distros are + # detected, we have to disable PDF output. + # + # So, we need to ignore the packages that distros would + # need for LaTeX to work + if DepManager.is_pdf(dtype) and not self.pdf: + self.optional -= 1 + continue + + if not dtype in self.run: + self.run[dtype] = [] + + self.run[dtype].append(progs.get(prog, prog)) + + install = [] + for dtype in self.run.keys(): + install += self.run[dtype] + + return " ".join(sorted(set(install))) + + def warn_install(self): + + output_msg = "" + + for dtype in sorted(self.run.keys()): + progs = " ".join(sorted(set(self.run[dtype]))) + + try: + name = DepManager.name(dtype) + output_msg += f'{name}:\t{progs}\n' + except KeyError: + raise KeyError(f"ERROR!!!: invalid dtype for {progs}: {dtype}") + + if output_msg: + print(f"\n{output_msg}\n") class SphinxDependencyChecker: # List of required texlive packages on Fedora and OpenSuse @@ -120,10 +191,8 @@ class SphinxDependencyChecker: self.virtualenv = args.virtualenv self.version_check = args.version_check - self.missing = {} + self.deps = DepManager(self.pdf) - self.need = 0 - self.optional = 0 self.need_symlink = 0 self.need_sphinx = 0 self.need_pip = 0 @@ -270,64 +339,18 @@ class SphinxDependencyChecker: # Methods to check if a feature exists # - def check_missing(self, progs): - run = {} - - for prog, dtype in sorted(self.missing.items()): - # At least on some LTS distros like CentOS 7, texlive doesn't - # provide all packages we need. When such distros are - # detected, we have to disable PDF output. - # - # So, we need to ignore the packages that distros would - # need for LaTeX to work - if DepType.is_pdf(dtype) and not self.pdf: - self.optional -= 1 - continue - - if not dtype in run: - run[dtype] = [] - - run[dtype].append(prog) - - output_msg = "" - - for dtype in sorted(run.keys()): - progs = " ".join(run[dtype]) - - if self.verbose_warn_install: - try: - name = DepType.name(dtype) - output_msg += f'{name}:\t{progs}\n' - except KeyError: - raise KeyError(f"ERROR!!!: invalid dtype for {progs}: {dtype}") - - self.install += " " + progs - - if output_msg: - print(f"\n{output_msg}\n") - - self.install = self.install.lstrip() - - def add_package(self, package, dtype): - is_optional = DepType.is_optional(dtype) - self.missing[package] = dtype - if is_optional: - self.optional += 1 - else: - self.need += 1 - def check_missing_file(self, files, package, dtype): for f in files: if os.path.exists(f): return - self.add_package(package, dtype) + self.deps.add_package(package, dtype) def check_program(self, prog, dtype): found = self.which(prog) if found: return found - self.add_package(prog, dtype) + self.deps.add_package(prog, dtype) return None @@ -340,52 +363,52 @@ class SphinxDependencyChecker: # add it as a mandatory package, as some parts of the doc builder # needs it. if not self.which("perl"): - self.add_package("perl", DepType.SYSTEM_MANDATORY) - self.add_package(prog, dtype) + self.deps.add_package("perl", DepManager.SYSTEM_MANDATORY) + self.deps.add_package(prog, dtype) return try: self.run(["perl", f"-M{prog}", "-e", "1"], check=True) except subprocess.CalledProcessError: - self.add_package(prog, dtype) + self.deps.add_package(prog, dtype) def check_python_module(self, module, is_optional=False): if is_optional: - dtype = DepType.PYTHON_OPTIONAL + dtype = DepManager.PYTHON_OPTIONAL else: - dtype = DepType.PYTHON_MANDATORY + dtype = DepManager.PYTHON_MANDATORY try: self.run([self.python_cmd, "-c", f"import {module}"], check=True) except subprocess.CalledProcessError: - self.add_package(module, dtype) + self.deps.add_package(module, dtype) def check_rpm_missing(self, pkgs, dtype): for prog in pkgs: try: self.run(["rpm", "-q", prog], check=True) except subprocess.CalledProcessError: - self.add_package(prog, dtype) + self.deps.add_package(prog, dtype) def check_pacman_missing(self, pkgs, dtype): for prog in pkgs: try: self.run(["pacman", "-Q", prog], check=True) except subprocess.CalledProcessError: - self.add_package(prog, dtype) + self.deps.add_package(prog, dtype) def check_missing_tex(self, is_optional=False): if is_optional: - dtype = DepType.PDF_OPTIONAL + dtype = DepManager.PDF_OPTIONAL else: - dtype = DepType.PDF_MANDATORY + dtype = DepManager.PDF_MANDATORY kpsewhich = self.which("kpsewhich") for prog, package in self.texlive.items(): # If kpsewhich is not there, just add it to deps if not kpsewhich: - self.add_package(package, dtype) + self.deps.add_package(package, dtype) continue # Check if the package is needed @@ -396,11 +419,11 @@ class SphinxDependencyChecker: # Didn't find. Add it if not result.stdout.strip(): - self.add_package(package, dtype) + self.deps.add_package(package, dtype) except subprocess.CalledProcessError: # kpsewhich returned an error. Add it, just in case - self.add_package(package, dtype) + self.deps.add_package(package, dtype) def get_sphinx_fname(self): if "SPHINXBUILD" in os.environ: @@ -478,6 +501,17 @@ class SphinxDependencyChecker: return f.read().strip() return "" + def check_missing(self, progs): + self.install += self.deps.check_missing(progs) + if self.verbose_warn_install: + self.deps.warn_install() + + if not self.deps.need and not self.deps.optional: + return False + + return True + + # # Distro-specific hints methods # @@ -511,13 +545,11 @@ class SphinxDependencyChecker: } for package, files in pdf_pkgs.items(): - self.check_missing_file(files, package, DepType.PDF_MANDATORY) - - self.check_program("dvipng", DepType.PDF_MANDATORY) + self.check_missing_file(files, package, DepManager.PDF_MANDATORY) - self.check_missing(progs) + self.check_program("dvipng", DepManager.PDF_MANDATORY) - if not self.need and not self.optional: + if self.check_missing(progs): return if self.verbose_warn_install: @@ -575,7 +607,7 @@ class SphinxDependencyChecker: # RHEL 8 uses Python 3.6, which is not compatible with # the build system anymore. Suggest Python 3.11 if rel == 8: - self.add_package("python39", DepType.SYSTEM_MANDATORY) + self.deps.add_package("python39", DepManager.SYSTEM_MANDATORY) self.recommend_python = True if self.first_hint: @@ -597,22 +629,18 @@ class SphinxDependencyChecker: "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc", ] - self.check_missing_file(pdf_pkgs, noto_sans_redhat, DepType.PDF_MANDATORY) + self.check_missing_file(pdf_pkgs, noto_sans_redhat, DepManager.PDF_MANDATORY) - self.check_rpm_missing(fedora_tex_pkgs, DepType.PDF_MANDATORY) + self.check_rpm_missing(fedora_tex_pkgs, DepManager.PDF_MANDATORY) - self.check_missing_tex(DepType.PDF_MANDATORY) + self.check_missing_tex(DepManager.PDF_MANDATORY) # There's no texlive-ctex on RHEL 8 repositories. This will # likely affect CJK pdf build only. if not fedora and rel == 8: - if "texlive-ctex" in self.missing: - del self.missing["texlive-ctex"] - - - self.check_missing(progs) + self.deps.del_package("texlive-ctex") - if not self.need and not self.optional: + if self.check_missing(progs): return if self.verbose_warn_install: @@ -659,7 +687,7 @@ class SphinxDependencyChecker: if rel == 15: if not self.which(self.python_cmd): self.recommend_python = True - self.add_package(self.python_cmd, DepType.SYSTEM_MANDATORY) + self.deps.add_package(self.python_cmd, DepManager.SYSTEM_MANDATORY) progs.update({ "python-sphinx": "python311-Sphinx", @@ -681,12 +709,11 @@ class SphinxDependencyChecker: # "Noto Sans CJK SC" on openSUSE if self.pdf: - self.check_rpm_missing(suse_tex_pkgs, DepType.PDF_MANDATORY) + self.check_rpm_missing(suse_tex_pkgs, DepManager.PDF_MANDATORY) if self.pdf: self.check_missing_tex() - self.check_missing(progs) - if not self.need and not self.optional: + if self.check_missing(progs): return if self.verbose_warn_install: @@ -730,13 +757,12 @@ class SphinxDependencyChecker: "/usr/share/fonts/TTF/NotoSans-Regular.ttf", ] - self.check_missing_file(pdf_pkgs, noto_sans, DepType.PDF_MANDATORY) - self.check_rpm_missing(tex_pkgs, DepType.PDF_MANDATORY) - - self.check_missing(progs) + self.check_missing_file(pdf_pkgs, noto_sans, DepManager.PDF_MANDATORY) + self.check_rpm_missing(tex_pkgs, DepManager.PDF_MANDATORY) - if not self.need and not self.optional: + if self.check_missing(progs): return + if self.verbose_warn_install: print("You should run:") print(f"\n\tsudo {packager_cmd} {self.install}") @@ -759,7 +785,7 @@ class SphinxDependencyChecker: ] if self.pdf: - self.check_pacman_missing(archlinux_tex_pkgs, DepType.PDF_MANDATORY) + self.check_pacman_missing(archlinux_tex_pkgs, DepManager.PDF_MANDATORY) self.check_missing_file( ["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], @@ -767,10 +793,9 @@ class SphinxDependencyChecker: 2, ) - self.check_missing(progs) - - if not self.need and not self.optional: + if self.check_missing(progs): return + if self.verbose_warn_install: print("You should run:") print(f"\n\tsudo pacman -S {self.install}") @@ -797,11 +822,9 @@ class SphinxDependencyChecker: ], } for package, files in pdf_pkgs.items(): - self.check_missing_file(files, package, DepType.PDF_MANDATORY) - - self.check_missing(progs) + self.check_missing_file(files, package, DepManager.PDF_MANDATORY) - if not self.need and not self.optional: + if self.check_missing(progs): return if self.verbose_warn_install: @@ -1039,16 +1062,18 @@ class SphinxDependencyChecker: print("\n2) As a package with:") - old_need = self.need - old_optional = self.optional - self.missing = {} + old_need = self.deps.need + old_optional = self.deps.optional + self.pdf = False self.optional = 0 self.install = "" old_verbose = self.verbose_warn_install self.verbose_warn_install = 0 - self.add_package("python-sphinx", DepType.PYTHON_MANDATORY) + self.deps.clear_deps() + + self.deps.add_package("python-sphinx", DepManager.PYTHON_MANDATORY) self.check_distros() @@ -1111,7 +1136,7 @@ class SphinxDependencyChecker: print("\n") else: if self.need_sphinx: - self.need += 1 + self.deps.need += 1 # Suggest newer versions if current ones are too old if self.latest_avail_ver and self.latest_avail_ver >= self.min_version: @@ -1190,20 +1215,20 @@ class SphinxDependencyChecker: self.check_python_module("ensurepip") # Check for needed programs/tools - self.check_perl_module("Pod::Usage", DepType.SYSTEM_MANDATORY) + self.check_perl_module("Pod::Usage", DepManager.SYSTEM_MANDATORY) - self.check_program("make", DepType.SYSTEM_MANDATORY) - self.check_program("gcc", DepType.SYSTEM_MANDATORY) + self.check_program("make", DepManager.SYSTEM_MANDATORY) + self.check_program("gcc", DepManager.SYSTEM_MANDATORY) - self.check_program("dot", DepType.SYSTEM_OPTIONAL) - self.check_program("convert", DepType.SYSTEM_OPTIONAL) + self.check_program("dot", DepManager.SYSTEM_OPTIONAL) + self.check_program("convert", DepManager.SYSTEM_OPTIONAL) self.check_python_module("yaml") if self.pdf: - self.check_program("xelatex", DepType.PDF_MANDATORY) - self.check_program("rsvg-convert", DepType.PDF_MANDATORY) - self.check_program("latexmk", DepType.PDF_MANDATORY) + self.check_program("xelatex", DepManager.PDF_MANDATORY) + self.check_program("rsvg-convert", DepManager.PDF_MANDATORY) + self.check_program("latexmk", DepManager.PDF_MANDATORY) # Do distro-specific checks and output distro-install commands self.check_distros() -- cgit v1.2.3 From 1e9ba3b6d4cef8b1b87d6226ab7026c9745cc1d7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:44 +0200 Subject: scripts: sphinx-pre-install: move ancillary checkers to a separate class The code there are just a bunch of static functions that are used by the main class. group them altogether to better organize the code. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/e2671eb14fae7a8510f5305ac44ad8063e237a5f.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 160 +++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 79 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 47dce1fcddfb..b00e50028f4d 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -150,85 +150,11 @@ class DepManager: if output_msg: print(f"\n{output_msg}\n") -class SphinxDependencyChecker: - # List of required texlive packages on Fedora and OpenSuse - texlive = { - "amsfonts.sty": "texlive-amsfonts", - "amsmath.sty": "texlive-amsmath", - "amssymb.sty": "texlive-amsfonts", - "amsthm.sty": "texlive-amscls", - "anyfontsize.sty": "texlive-anyfontsize", - "atbegshi.sty": "texlive-oberdiek", - "bm.sty": "texlive-tools", - "capt-of.sty": "texlive-capt-of", - "cmap.sty": "texlive-cmap", - "ctexhook.sty": "texlive-ctex", - "ecrm1000.tfm": "texlive-ec", - "eqparbox.sty": "texlive-eqparbox", - "eu1enc.def": "texlive-euenc", - "fancybox.sty": "texlive-fancybox", - "fancyvrb.sty": "texlive-fancyvrb", - "float.sty": "texlive-float", - "fncychap.sty": "texlive-fncychap", - "footnote.sty": "texlive-mdwtools", - "framed.sty": "texlive-framed", - "luatex85.sty": "texlive-luatex85", - "multirow.sty": "texlive-multirow", - "needspace.sty": "texlive-needspace", - "palatino.sty": "texlive-psnfss", - "parskip.sty": "texlive-parskip", - "polyglossia.sty": "texlive-polyglossia", - "tabulary.sty": "texlive-tabulary", - "threeparttable.sty": "texlive-threeparttable", - "titlesec.sty": "texlive-titlesec", - "ucs.sty": "texlive-ucs", - "upquote.sty": "texlive-upquote", - "wrapfig.sty": "texlive-wrapfig", - } - - def __init__(self, args): - self.pdf = args.pdf - self.virtualenv = args.virtualenv - self.version_check = args.version_check - - self.deps = DepManager(self.pdf) - - self.need_symlink = 0 - self.need_sphinx = 0 - self.need_pip = 0 - self.rec_sphinx_upgrade = 0 - self.verbose_warn_install = 1 - - self.system_release = "" - self.install = "" - self.virtenv_dir = "" - self.python_cmd = "" - self.activate_cmd = "" - - # Some distros may not have a Sphinx shipped package compatible with - # our minimal requirements - self.package_supported = True - - # Recommend a new python version - self.recommend_python = None - - # Certain hints are meant to be shown only once - self.first_hint = True - - self.min_version = (0, 0, 0) - self.cur_version = (0, 0, 0) - self.latest_avail_ver = (0, 0, 0) - self.venv_ver = (0, 0, 0) - - prefix = os.environ.get("srctree", ".") + "/" - - self.conf = prefix + "Documentation/conf.py" - self.requirement_file = prefix + "Documentation/sphinx/requirements.txt" - self.virtenv_prefix = ["sphinx_", "Sphinx_" ] - - # - # Ancillary methods that don't depend on self - # +class AncillaryCheckers: + """ + Ancillary methods that checks for missing dependencies for different + types of types, like binaries, python modules, rpm deps, etc. + """ @staticmethod def which(prog): @@ -335,6 +261,82 @@ class SphinxDependencyChecker: return subprocess.run(*args, **kwargs) +class SphinxDependencyChecker(AncillaryCheckers): + # List of required texlive packages on Fedora and OpenSuse + texlive = { + "amsfonts.sty": "texlive-amsfonts", + "amsmath.sty": "texlive-amsmath", + "amssymb.sty": "texlive-amsfonts", + "amsthm.sty": "texlive-amscls", + "anyfontsize.sty": "texlive-anyfontsize", + "atbegshi.sty": "texlive-oberdiek", + "bm.sty": "texlive-tools", + "capt-of.sty": "texlive-capt-of", + "cmap.sty": "texlive-cmap", + "ctexhook.sty": "texlive-ctex", + "ecrm1000.tfm": "texlive-ec", + "eqparbox.sty": "texlive-eqparbox", + "eu1enc.def": "texlive-euenc", + "fancybox.sty": "texlive-fancybox", + "fancyvrb.sty": "texlive-fancyvrb", + "float.sty": "texlive-float", + "fncychap.sty": "texlive-fncychap", + "footnote.sty": "texlive-mdwtools", + "framed.sty": "texlive-framed", + "luatex85.sty": "texlive-luatex85", + "multirow.sty": "texlive-multirow", + "needspace.sty": "texlive-needspace", + "palatino.sty": "texlive-psnfss", + "parskip.sty": "texlive-parskip", + "polyglossia.sty": "texlive-polyglossia", + "tabulary.sty": "texlive-tabulary", + "threeparttable.sty": "texlive-threeparttable", + "titlesec.sty": "texlive-titlesec", + "ucs.sty": "texlive-ucs", + "upquote.sty": "texlive-upquote", + "wrapfig.sty": "texlive-wrapfig", + } + + def __init__(self, args): + self.pdf = args.pdf + self.virtualenv = args.virtualenv + self.version_check = args.version_check + + self.deps = DepManager(self.pdf) + + self.need_symlink = 0 + self.need_sphinx = 0 + self.need_pip = 0 + self.rec_sphinx_upgrade = 0 + self.verbose_warn_install = 1 + + self.system_release = "" + self.install = "" + self.virtenv_dir = "" + self.python_cmd = "" + self.activate_cmd = "" + + # Some distros may not have a Sphinx shipped package compatible with + # our minimal requirements + self.package_supported = True + + # Recommend a new python version + self.recommend_python = None + + # Certain hints are meant to be shown only once + self.first_hint = True + + self.min_version = (0, 0, 0) + self.cur_version = (0, 0, 0) + self.latest_avail_ver = (0, 0, 0) + self.venv_ver = (0, 0, 0) + + prefix = os.environ.get("srctree", ".") + "/" + + self.conf = prefix + "Documentation/conf.py" + self.requirement_file = prefix + "Documentation/sphinx/requirements.txt" + self.virtenv_prefix = ["sphinx_", "Sphinx_" ] + # # Methods to check if a feature exists # -- cgit v1.2.3 From 9bb5f0dc18d037635f4c5075747de1e47493b538 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:45 +0200 Subject: scripts: sphinx-pre-install: add more generic checkers on a class Better organize the code by moving the more generic methods to MissingCheckers. Such class contain only binary and package dependent missing checkers, but no distro-specific data or code. All distro-specific data/code remains at SphinxDependencyChecker class. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/11a252fe816bd7c85583d26ade0666eb2b481bf0.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 142 ++++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 66 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index b00e50028f4d..9127487bd4d7 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -150,7 +150,7 @@ class DepManager: if output_msg: print(f"\n{output_msg}\n") -class AncillaryCheckers: +class AncillaryMethods: """ Ancillary methods that checks for missing dependencies for different types of types, like binaries, python modules, rpm deps, etc. @@ -261,81 +261,24 @@ class AncillaryCheckers: return subprocess.run(*args, **kwargs) -class SphinxDependencyChecker(AncillaryCheckers): - # List of required texlive packages on Fedora and OpenSuse - texlive = { - "amsfonts.sty": "texlive-amsfonts", - "amsmath.sty": "texlive-amsmath", - "amssymb.sty": "texlive-amsfonts", - "amsthm.sty": "texlive-amscls", - "anyfontsize.sty": "texlive-anyfontsize", - "atbegshi.sty": "texlive-oberdiek", - "bm.sty": "texlive-tools", - "capt-of.sty": "texlive-capt-of", - "cmap.sty": "texlive-cmap", - "ctexhook.sty": "texlive-ctex", - "ecrm1000.tfm": "texlive-ec", - "eqparbox.sty": "texlive-eqparbox", - "eu1enc.def": "texlive-euenc", - "fancybox.sty": "texlive-fancybox", - "fancyvrb.sty": "texlive-fancyvrb", - "float.sty": "texlive-float", - "fncychap.sty": "texlive-fncychap", - "footnote.sty": "texlive-mdwtools", - "framed.sty": "texlive-framed", - "luatex85.sty": "texlive-luatex85", - "multirow.sty": "texlive-multirow", - "needspace.sty": "texlive-needspace", - "palatino.sty": "texlive-psnfss", - "parskip.sty": "texlive-parskip", - "polyglossia.sty": "texlive-polyglossia", - "tabulary.sty": "texlive-tabulary", - "threeparttable.sty": "texlive-threeparttable", - "titlesec.sty": "texlive-titlesec", - "ucs.sty": "texlive-ucs", - "upquote.sty": "texlive-upquote", - "wrapfig.sty": "texlive-wrapfig", - } +class MissingCheckers(AncillaryMethods): - def __init__(self, args): + def __init__(self, args, texlive): self.pdf = args.pdf self.virtualenv = args.virtualenv self.version_check = args.version_check + self.texlive = texlive self.deps = DepManager(self.pdf) self.need_symlink = 0 self.need_sphinx = 0 - self.need_pip = 0 - self.rec_sphinx_upgrade = 0 + self.verbose_warn_install = 1 - self.system_release = "" - self.install = "" self.virtenv_dir = "" - self.python_cmd = "" - self.activate_cmd = "" - # Some distros may not have a Sphinx shipped package compatible with - # our minimal requirements - self.package_supported = True - - # Recommend a new python version - self.recommend_python = None - - # Certain hints are meant to be shown only once - self.first_hint = True - - self.min_version = (0, 0, 0) - self.cur_version = (0, 0, 0) - self.latest_avail_ver = (0, 0, 0) - self.venv_ver = (0, 0, 0) - - prefix = os.environ.get("srctree", ".") + "/" - - self.conf = prefix + "Documentation/conf.py" - self.requirement_file = prefix + "Documentation/sphinx/requirements.txt" - self.virtenv_prefix = ["sphinx_", "Sphinx_" ] + self.install = "" # # Methods to check if a feature exists @@ -460,9 +403,9 @@ class SphinxDependencyChecker(AncillaryCheckers): if match: return parse_version(match.group(1)) - def check_sphinx(self): + def check_sphinx(self, conf): try: - with open(self.conf, "r", encoding="utf-8") as f: + with open(conf, "r", encoding="utf-8") as f: for line in f: match = re.match(r"^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]", line) if match: @@ -513,6 +456,73 @@ class SphinxDependencyChecker(AncillaryCheckers): return True +class SphinxDependencyChecker(MissingCheckers): + + def __init__(self, args): + # List of required texlive packages on Fedora and OpenSuse + texlive = { + "amsfonts.sty": "texlive-amsfonts", + "amsmath.sty": "texlive-amsmath", + "amssymb.sty": "texlive-amsfonts", + "amsthm.sty": "texlive-amscls", + "anyfontsize.sty": "texlive-anyfontsize", + "atbegshi.sty": "texlive-oberdiek", + "bm.sty": "texlive-tools", + "capt-of.sty": "texlive-capt-of", + "cmap.sty": "texlive-cmap", + "ctexhook.sty": "texlive-ctex", + "ecrm1000.tfm": "texlive-ec", + "eqparbox.sty": "texlive-eqparbox", + "eu1enc.def": "texlive-euenc", + "fancybox.sty": "texlive-fancybox", + "fancyvrb.sty": "texlive-fancyvrb", + "float.sty": "texlive-float", + "fncychap.sty": "texlive-fncychap", + "footnote.sty": "texlive-mdwtools", + "framed.sty": "texlive-framed", + "luatex85.sty": "texlive-luatex85", + "multirow.sty": "texlive-multirow", + "needspace.sty": "texlive-needspace", + "palatino.sty": "texlive-psnfss", + "parskip.sty": "texlive-parskip", + "polyglossia.sty": "texlive-polyglossia", + "tabulary.sty": "texlive-tabulary", + "threeparttable.sty": "texlive-threeparttable", + "titlesec.sty": "texlive-titlesec", + "ucs.sty": "texlive-ucs", + "upquote.sty": "texlive-upquote", + "wrapfig.sty": "texlive-wrapfig", + } + + super().__init__(args, texlive) + + self.need_pip = 0 + self.rec_sphinx_upgrade = 0 + + self.system_release = "" + self.python_cmd = "" + self.activate_cmd = "" + + # Some distros may not have a Sphinx shipped package compatible with + # our minimal requirements + self.package_supported = True + + # Recommend a new python version + self.recommend_python = None + + # Certain hints are meant to be shown only once + self.first_hint = True + + self.min_version = (0, 0, 0) + self.cur_version = (0, 0, 0) + self.latest_avail_ver = (0, 0, 0) + self.venv_ver = (0, 0, 0) + + prefix = os.environ.get("srctree", ".") + "/" + + self.conf = prefix + "Documentation/conf.py" + self.requirement_file = prefix + "Documentation/sphinx/requirements.txt" + self.virtenv_prefix = ["sphinx_", "Sphinx_" ] # # Distro-specific hints methods @@ -1187,7 +1197,7 @@ class SphinxDependencyChecker(AncillaryCheckers): self.python_cmd = sys.executable # Check if Sphinx is already accessible from current environment - self.check_sphinx() + self.check_sphinx(self.conf) if self.system_release: print(f"Detected OS: {self.system_release}.") -- cgit v1.2.3 From fb22e438b23eabd828a0ca1076be5ecf9b0262db Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:46 +0200 Subject: scripts: sphinx-pre-install: move get_system_release() The code at get_system_release() is actually a helper function, independent from the actual Sphinx verification checker. Move it to MissingCheckers class, where other checkers are present. With that, the entire distro-specific handler logic, with all its complexity is confined at SphinxDependencyChecker class. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/4b42a85bbb6575bb34a58cf66019038c4afa1d5b.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 115 ++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 56 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 9127487bd4d7..593982f350b3 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -456,6 +456,64 @@ class MissingCheckers(AncillaryMethods): return True + def get_system_release(self): + """ + Determine the system type. There's no unique way that would work + with all distros with a minimal package install. So, several + methods are used here. + + By default, it will use lsb_release function. If not available, it will + fail back to reading the known different places where the distro name + is stored. + + Several modern distros now have /etc/os-release, which usually have + a decent coverage. + """ + + system_release = "" + + if self.which("lsb_release"): + result = self.run(["lsb_release", "-d"], capture_output=True, text=True) + system_release = result.stdout.replace("Description:", "").strip() + + release_files = [ + "/etc/system-release", + "/etc/redhat-release", + "/etc/lsb-release", + "/etc/gentoo-release", + ] + + if not system_release: + for f in release_files: + system_release = self.catcheck(f) + if system_release: + break + + # This seems more common than LSB these days + if not system_release: + os_var = {} + try: + with open("/etc/os-release", "r", encoding="utf-8") as f: + for line in f: + match = re.match(r"^([\w\d\_]+)=\"?([^\"]*)\"?\n", line) + if match: + os_var[match.group(1)] = match.group(2) + + system_release = os_var.get("NAME", "") + if "VERSION_ID" in os_var: + system_release += " " + os_var["VERSION_ID"] + elif "VERSION" in os_var: + system_release += " " + os_var["VERSION"] + except IOError: + pass + + if not system_release: + system_release = self.catcheck("/etc/issue") + + system_release = system_release.strip() + + return system_release + class SphinxDependencyChecker(MissingCheckers): def __init__(self, args): @@ -499,7 +557,7 @@ class SphinxDependencyChecker(MissingCheckers): self.need_pip = 0 self.rec_sphinx_upgrade = 0 - self.system_release = "" + self.system_release = self.get_system_release() self.python_cmd = "" self.activate_cmd = "" @@ -1193,7 +1251,6 @@ class SphinxDependencyChecker(MissingCheckers): "\thttps://github.com/sphinx-doc/sphinx/pull/8313") def check_needs(self): - self.get_system_release() self.python_cmd = sys.executable # Check if Sphinx is already accessible from current environment @@ -1270,60 +1327,6 @@ class SphinxDependencyChecker(MissingCheckers): print("Needed package dependencies are met.") - def get_system_release(self): - """ - Determine the system type. There's no unique way that would work - with all distros with a minimal package install. So, several - methods are used here. - - By default, it will use lsb_release function. If not available, it will - fail back to reading the known different places where the distro name - is stored. - - Several modern distros now have /etc/os-release, which usually have - a decent coverage. - """ - - if self.which("lsb_release"): - result = self.run(["lsb_release", "-d"], capture_output=True, text=True) - self.system_release = result.stdout.replace("Description:", "").strip() - - release_files = [ - "/etc/system-release", - "/etc/redhat-release", - "/etc/lsb-release", - "/etc/gentoo-release", - ] - - if not self.system_release: - for f in release_files: - self.system_release = self.catcheck(f) - if self.system_release: - break - - # This seems more common than LSB these days - if not self.system_release: - os_var = {} - try: - with open("/etc/os-release", "r", encoding="utf-8") as f: - for line in f: - match = re.match(r"^([\w\d\_]+)=\"?([^\"]*)\"?\n", line) - if match: - os_var[match.group(1)] = match.group(2) - - self.system_release = os_var.get("NAME", "") - if "VERSION_ID" in os_var: - self.system_release += " " + os_var["VERSION_ID"] - elif "VERSION" in os_var: - self.system_release += " " + os_var["VERSION"] - except IOError: - pass - - if not self.system_release: - self.system_release = self.catcheck("/etc/issue") - - self.system_release = self.system_release.strip() - DESCRIPTION = """ Process some flags related to Sphinx installation and documentation build. """ -- cgit v1.2.3 From f477c6d71d3947ab8f2e9e7d5fd8448f7a26c1ab Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:47 +0200 Subject: scripts: sphinx-pre-install: add documentation for the ancillary classes. While here, rename a parameter to have its usage better documented. No functional changes. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/7421112b14edf5c21cc4cf0f2ee320fcaf874b40.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 162 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 140 insertions(+), 22 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 593982f350b3..1dc3f19804ab 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -33,35 +33,62 @@ MIN_PYTHON_VERSION = parse_version("3.7") class DepManager: + """ + Manage package dependencies. There are three types of dependencies: + + - System: dependencies required for docs build; + - Python: python dependencies for a native distro Sphinx install; + - PDF: dependencies needed by PDF builds. + + Each dependency can be mandatory or optional. Not installing an optional + dependency won't break the build, but will cause degradation at the + docs output. + """ - # Internal types of dependencies. + # Internal types of dependencies. Don't use them outside DepManager class. _SYS_TYPE = 0 _PHY_TYPE = 1 _PDF_TYPE = 2 - # Let's define keys as a tuple with the type and mandatory/optional. - # This way, checking for optional or type is easy. + # Dependencies visible outside the class. + # The keys are tuple with: (type, is_mandatory flag). + # + # Currently we're not using all optional dep types. Yet, we'll keep all + # possible combinations here. They're not many, and that makes easier + # if later needed and for the name() method below SYSTEM_MANDATORY = (_SYS_TYPE, True) PYTHON_MANDATORY = (_PHY_TYPE, True) PDF_MANDATORY = (_PDF_TYPE, True) - # Currently we're not using all optional types, but let's keep all - # combinations here, as we may end needing them in the future. Also, - # it allows a name() function that handles all possibilities. SYSTEM_OPTIONAL = (_SYS_TYPE, False) PYTHON_OPTIONAL = (_PHY_TYPE, False) PDF_OPTIONAL = (_PDF_TYPE, True) def __init__(self, pdf): + """ + Initialize internal vars: + + - missing: missing dependencies list, containing a distro-independent + name for a missing dependency and its type. + - missing_pkg: ancillary dict containing missing dependencies in + distro namespace, organized by type. + - need: total number of needed dependencies. Never cleaned. + - optional: total number of optional dependencies. Never cleaned. + - pdf: PDF support is enabled? + """ self.missing = {} - self.run = {} + self.missing_pkg = {} self.need = 0 self.optional = 0 self.pdf = pdf @staticmethod def name(dtype): + """ + Ancillary routine to output a warn/error message reporting + missing dependencies. + """ if dtype[0] == DepManager._SYS_TYPE: msg = "build" elif dtype[0] == DepManager._PHY_TYPE: @@ -76,16 +103,22 @@ class DepManager: @staticmethod def is_optional(dtype): + """Ancillary routine to report if a dependency is optional""" return not dtype[1] @staticmethod def is_pdf(dtype): + """Ancillary routine to report if a dependency is for PDF generation""" if (dtype[0] == DepManager._PDF_TYPE): return True return False def add_package(self, package, dtype): + """ + Add a package at the self.missing() dictionary. + Doesn't update missing_pkg. + """ is_optional = DepManager.is_optional(dtype) self.missing[package] = dtype if is_optional: @@ -94,6 +127,10 @@ class DepManager: self.need += 1 def del_package(self, package): + """ + Remove a package at the self.missing() dictionary. + Doesn't update missing_pkg. + """ if package in self.missing: del self.missing[package] @@ -104,13 +141,22 @@ class DepManager: This is an ackward way to have a separate section to recommend a package after system main dependencies. - TODO: rework the logic to prevent needing it + TODO: rework the logic to prevent needing it. """ self.missing = {} + self.missing_pkg = {} def check_missing(self, progs): - self.run = {} + """ + Update self.missing_pkg, using progs dict to convert from the + agnostic package name to distro-specific one. + + Returns an string with the packages to be installed, sorted and + with eventual duplicates removed. + """ + + self.missing_pkg = {} for prog, dtype in sorted(self.missing.items()): # At least on some LTS distros like CentOS 7, texlive doesn't @@ -123,23 +169,26 @@ class DepManager: self.optional -= 1 continue - if not dtype in self.run: - self.run[dtype] = [] + if not dtype in self.missing_pkg: + self.missing_pkg[dtype] = [] - self.run[dtype].append(progs.get(prog, prog)) + self.missing_pkg[dtype].append(progs.get(prog, prog)) install = [] - for dtype in self.run.keys(): - install += self.run[dtype] + for dtype in self.missing_pkg.keys(): + install += self.missing_pkg[dtype] return " ".join(sorted(set(install))) def warn_install(self): + """ + Emit warnings/errors related to missing packages. + """ output_msg = "" - for dtype in sorted(self.run.keys()): - progs = " ".join(sorted(set(self.run[dtype]))) + for dtype in sorted(self.missing_pkg.keys()): + progs = " ".join(sorted(set(self.missing_pkg[dtype]))) try: name = DepManager.name(dtype) @@ -158,6 +207,11 @@ class AncillaryMethods: @staticmethod def which(prog): + """ + Our own implementation of which(). We could instead use + shutil.which(), but this function is simple enough. + Probably faster to use this implementation than to import shutil. + """ for path in os.environ.get("PATH", "").split(":"): full_path = os.path.join(path, prog) if os.access(full_path, os.X_OK): @@ -167,6 +221,10 @@ class AncillaryMethods: @staticmethod def get_python_version(cmd): + """ + Get python version from a Python binary. As we need to detect if + are out there newer python binaries, we can't rely on sys.release here. + """ result = SphinxDependencyChecker.run([cmd, "--version"], capture_output=True, text=True) @@ -181,7 +239,13 @@ class AncillaryMethods: @staticmethod def find_python(): + """ + Detect if are out there any python 3.xy version newer than the + current one. + Note: this routine is limited to up to 2 digits for python3. We + may need to update it one day, hopefully on a distant future. + """ patterns = [ "python3.[0-9]", "python3.[0-9][0-9]", @@ -200,7 +264,10 @@ class AncillaryMethods: @staticmethod def check_python(): - + """ + Check if the current python binary satisfies our minimal requirement + for Sphinx build. If not, re-run with a newer version if found. + """ cur_ver = sys.version_info[:3] if cur_ver >= MIN_PYTHON_VERSION: ver = ver_str(cur_ver) @@ -240,7 +307,10 @@ class AncillaryMethods: @staticmethod def run(*args, **kwargs): - """Excecute a command, hiding its output by default""" + """ + Excecute a command, hiding its output by default. + Preserve comatibility with older Python versions. + """ capture_output = kwargs.pop('capture_output', False) @@ -262,8 +332,15 @@ class AncillaryMethods: return subprocess.run(*args, **kwargs) class MissingCheckers(AncillaryMethods): + """ + Contains some ancillary checkers for different types of binaries and + package managers. + """ def __init__(self, args, texlive): + """ + Initialize its internal variables + """ self.pdf = args.pdf self.virtualenv = args.virtualenv self.version_check = args.version_check @@ -280,17 +357,20 @@ class MissingCheckers(AncillaryMethods): self.install = "" - # - # Methods to check if a feature exists - # - def check_missing_file(self, files, package, dtype): + """ + Does the file exists? If not, add it to missing dependencies. + """ for f in files: if os.path.exists(f): return self.deps.add_package(package, dtype) def check_program(self, prog, dtype): + """ + Does the program exists and it is at the PATH? + If not, add it to missing dependencies. + """ found = self.which(prog) if found: return found @@ -300,6 +380,18 @@ class MissingCheckers(AncillaryMethods): return None def check_perl_module(self, prog, dtype): + """ + Does perl have a dependency? Is it available? + If not, add it to missing dependencies. + + Right now, we still need Perl for doc build, as it is required + by some tools called at docs or kernel build time, like: + + scripts/documentation-file-ref-check + + Also, checkpatch is on Perl. + """ + # While testing with lxc download template, one of the # distros (Oracle) didn't have perl - nor even an option to install # before installing oraclelinux-release-el9 package. @@ -318,6 +410,10 @@ class MissingCheckers(AncillaryMethods): self.deps.add_package(prog, dtype) def check_python_module(self, module, is_optional=False): + """ + Does a python module exists outside venv? If not, add it to missing + dependencies. + """ if is_optional: dtype = DepManager.PYTHON_OPTIONAL else: @@ -329,6 +425,9 @@ class MissingCheckers(AncillaryMethods): self.deps.add_package(module, dtype) def check_rpm_missing(self, pkgs, dtype): + """ + Does a rpm package exists? If not, add it to missing dependencies. + """ for prog in pkgs: try: self.run(["rpm", "-q", prog], check=True) @@ -336,6 +435,9 @@ class MissingCheckers(AncillaryMethods): self.deps.add_package(prog, dtype) def check_pacman_missing(self, pkgs, dtype): + """ + Does a pacman package exists? If not, add it to missing dependencies. + """ for prog in pkgs: try: self.run(["pacman", "-Q", prog], check=True) @@ -343,6 +445,9 @@ class MissingCheckers(AncillaryMethods): self.deps.add_package(prog, dtype) def check_missing_tex(self, is_optional=False): + """ + Does a LaTeX package exists? If not, add it to missing dependencies. + """ if is_optional: dtype = DepManager.PDF_OPTIONAL else: @@ -371,6 +476,9 @@ class MissingCheckers(AncillaryMethods): self.deps.add_package(package, dtype) def get_sphinx_fname(self): + """ + Gets the binary filename for sphinx-build. + """ if "SPHINXBUILD" in os.environ: return os.environ["SPHINXBUILD"] @@ -386,6 +494,9 @@ class MissingCheckers(AncillaryMethods): return "" def get_sphinx_version(self, cmd): + """ + Gets sphinx-build version. + """ try: result = self.run([cmd, "--version"], stdout=subprocess.PIPE, @@ -404,6 +515,9 @@ class MissingCheckers(AncillaryMethods): return parse_version(match.group(1)) def check_sphinx(self, conf): + """ + Checks Sphinx minimal requirements + """ try: with open(conf, "r", encoding="utf-8") as f: for line in f: @@ -441,6 +555,10 @@ class MissingCheckers(AncillaryMethods): sys.exit(0) def catcheck(self, filename): + """ + Reads a file if it exists, returning as string. + If not found, returns an empty string. + """ if os.path.exists(filename): with open(filename, "r", encoding="utf-8") as f: return f.read().strip() -- cgit v1.2.3 From 6d5f4f3da1a82786650d38067bb4c5e4ed84dc61 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:48 +0200 Subject: scripts: sphinx-pre-install: add docstring documentation This program is somewhat complex. Add some docstring documentation, explaining what each function and class is supposed to do. Most of the focus here were to describe the ancillary functions used to detect dependency needs. The main SphinxDependencyChecker still requires a lot of care, and probably need to be reorganized to clearly split the 4 types of output it produces: - Need to upgrade Python binary; - System install needs; - Virtual env install needs; - Python install needs via system packages, to run Sphinx natively. Yet, for now, I'm happy of having it a lot better documented than its Perl version. - While here, rename a parameter to have its usage better documented. No functional changes. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/0cadab2cab3f78ae6d9f378e92a45125fbc5188f.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 110 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 102 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 1dc3f19804ab..1c96f6692e9a 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -9,6 +9,22 @@ # Don't add changes not compatible with it, it is meant to report # incompatible python versions. +""" +Dependency checker for Sphinx documentation Kernel build. + +This module provides tools to check for all required dependencies needed to +build documentation using Sphinx, including system packages, Python modules +and LaTeX packages for PDF generation. + +It detect packages for a subset of Linux distributions used by Kernel +maintainers, showing hints and missing dependencies. + +The main class SphinxDependencyChecker handles the dependency checking logic +and provides recommendations for installing missing packages. It supports both +system package installations and Python virtual environments. By default, +system pacage install is recommended. +""" + import argparse import os import re @@ -75,7 +91,7 @@ class DepManager: distro namespace, organized by type. - need: total number of needed dependencies. Never cleaned. - optional: total number of optional dependencies. Never cleaned. - - pdf: PDF support is enabled? + - pdf: Is PDF support enabled? """ self.missing = {} self.missing_pkg = {} @@ -565,6 +581,13 @@ class MissingCheckers(AncillaryMethods): return "" def check_missing(self, progs): + """ + Check for missing dependencies using the provided program mapping. + + The actual distro-specific programs are mapped via progs argument. + + Returns True if there are missing dependencies, False otherwise. + """ self.install += self.deps.check_missing(progs) if self.verbose_warn_install: self.deps.warn_install() @@ -633,8 +656,18 @@ class MissingCheckers(AncillaryMethods): return system_release class SphinxDependencyChecker(MissingCheckers): + """ + Main class for checking Sphinx documentation build dependencies. + - Check for missing system packages; + - Check for missing Python modules; + - Check for missing LaTeX packages needed by PDF generation; + - Propose Sphinx install via Python Virtual environment; + - Propose Sphinx install via distro-specific package install. + """ def __init__(self, args): + """Initialize checker variables""" + # List of required texlive packages on Fedora and OpenSuse texlive = { "amsfonts.sty": "texlive-amsfonts", @@ -705,6 +738,9 @@ class SphinxDependencyChecker(MissingCheckers): # def give_debian_hints(self): + """ + Provide package installation hints for Debian-based distros. + """ progs = { "Pod::Usage": "perl-modules", "convert": "imagemagick", @@ -745,6 +781,10 @@ class SphinxDependencyChecker(MissingCheckers): print(f"\n\tsudo apt-get install {self.install}") def give_redhat_hints(self): + """ + Provide package installation hints for RedHat-based distros + (Fedora, RHEL and RHEL-based variants). + """ progs = { "Pod::Usage": "perl-Pod-Usage", "convert": "ImageMagick", @@ -837,6 +877,10 @@ class SphinxDependencyChecker(MissingCheckers): print(f"\n\tsudo dnf install -y {self.install}") def give_opensuse_hints(self): + """ + Provide package installation hints for openSUSE-based distros + (Leap and Tumbleweed). + """ progs = { "Pod::Usage": "perl-Pod-Usage", "convert": "ImageMagick", @@ -909,6 +953,9 @@ class SphinxDependencyChecker(MissingCheckers): print(f"\n\tsudo zypper install --no-recommends {self.install}") def give_mageia_hints(self): + """ + Provide package installation hints for Mageia and OpenMandriva. + """ progs = { "Pod::Usage": "perl-Pod-Usage", "convert": "ImageMagick", @@ -956,6 +1003,9 @@ class SphinxDependencyChecker(MissingCheckers): print(f"\n\tsudo {packager_cmd} {self.install}") def give_arch_linux_hints(self): + """ + Provide package installation hints for ArchLinux. + """ progs = { "convert": "imagemagick", "dot": "graphviz", @@ -989,6 +1039,9 @@ class SphinxDependencyChecker(MissingCheckers): print(f"\n\tsudo pacman -S {self.install}") def give_gentoo_hints(self): + """ + Provide package installation hints for Gentoo. + """ progs = { "convert": "media-gfx/imagemagick", "dot": "media-gfx/graphviz", @@ -1107,7 +1160,12 @@ class SphinxDependencyChecker(MissingCheckers): # def check_distros(self): - # OS-specific hints logic + """ + OS-specific hints logic. Seeks for a hinter. If found, provide + package-manager-specific install commands. + + Otherwise, just lists the missing dependencies. + """ os_hints = { re.compile("Red Hat Enterprise Linux"): self.give_redhat_hints, re.compile("Fedora"): self.give_redhat_hints, @@ -1159,10 +1217,22 @@ class SphinxDependencyChecker(MissingCheckers): # Common dependencies # def deactivate_help(self): + """ + Print a helper message to disable a virtual environment. + """ + print("\n If you want to exit the virtualenv, you can use:") print("\tdeactivate") def get_virtenv(self): + """ + Give a hint about how to activate an already-existing virtual + environment containing sphinx-build. + + Returns a tuble with (activate_cmd_path, sphinx_version) with + the newest available virtual env. + """ + cwd = os.getcwd() activates = [] @@ -1207,6 +1277,14 @@ class SphinxDependencyChecker(MissingCheckers): return ("", ver) def recommend_sphinx_upgrade(self): + """ + Check if Sphinx needs to be upgraded. + + Returns a tuple with the higest available Sphinx version if found. + Otherwise, returns None to indicate either that no upgrade is needed + or no venv was found. + """ + # Avoid running sphinx-builds from venv if cur_version is good if self.cur_version and self.cur_version >= RECOMMENDED_VERSION: self.latest_avail_ver = self.cur_version @@ -1247,6 +1325,9 @@ class SphinxDependencyChecker(MissingCheckers): return self.latest_avail_ver def recommend_package(self): + """ + Recommend installing Sphinx as a distro-specific package. + """ print("\n2) As a package with:") @@ -1270,11 +1351,19 @@ class SphinxDependencyChecker(MissingCheckers): self.verbose_warn_install = old_verbose def recommend_sphinx_version(self, virtualenv_cmd): - # The logic here is complex, as it have to deal with different versions: - # - minimal supported version; - # - minimal PDF version; - # - recommended version. - # It also needs to work fine with both distro's package and venv/virtualenv + """ + Provide recommendations for installing or upgrading Sphinx based + on current version. + + The logic here is complex, as it have to deal with different versions: + + - minimal supported version; + - minimal PDF version; + - recommended version. + + It also needs to work fine with both distro's package and + venv/virtualenv + """ if self.recommend_python: print("\nPython version is incompatible with doc build.\n" \ @@ -1369,6 +1458,10 @@ class SphinxDependencyChecker(MissingCheckers): "\thttps://github.com/sphinx-doc/sphinx/pull/8313") def check_needs(self): + """ + Main method that checks needed dependencies and provides + recommendations. + """ self.python_cmd = sys.executable # Check if Sphinx is already accessible from current environment @@ -1451,6 +1544,7 @@ Process some flags related to Sphinx installation and documentation build. def main(): + """Main function""" parser = argparse.ArgumentParser(description=DESCRIPTION) parser.add_argument( @@ -1481,6 +1575,6 @@ def main(): checker.check_python() checker.check_needs() - +# Call main if not used as module if __name__ == "__main__": main() -- cgit v1.2.3 From 8b45effaa2ce2790e3b391e1ac4bb668e0d37560 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:49 +0200 Subject: scripts: sphinx-pre-install: fix several codingstyle issues Address most pylint issues. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/5139b18535e1436e4b1773706224a9ec3a386697.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 52 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 1c96f6692e9a..1b11162da9fb 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -2,8 +2,8 @@ # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2017-2025 Mauro Carvalho Chehab # -# pylint: disable=C0103,C0114,C0115,C0116,C0301 -# pylint: disable=R0902,R0904,R0912,R0915,R1705,R1710,E1121 +# pylint: disable=C0103,C0114,C0115,C0116,C0301,C0302 +# pylint: disable=R0902,R0904,R0911,R0912,R0914,R0915,R1705,R1710,E1121 # Note: this script requires at least Python 3.6 to run. # Don't add changes not compatible with it, it is meant to report @@ -115,7 +115,7 @@ class DepManager: if dtype[1]: return f"ERROR: {msg} mandatory deps missing" else: - out = f"Warning: {msg} optional deps missing" + return f"Warning: {msg} optional deps missing" @staticmethod def is_optional(dtype): @@ -125,7 +125,7 @@ class DepManager: @staticmethod def is_pdf(dtype): """Ancillary routine to report if a dependency is for PDF generation""" - if (dtype[0] == DepManager._PDF_TYPE): + if dtype[0] == DepManager._PDF_TYPE: return True return False @@ -191,8 +191,8 @@ class DepManager: self.missing_pkg[dtype].append(progs.get(prog, prog)) install = [] - for dtype in self.missing_pkg.keys(): - install += self.missing_pkg[dtype] + for dtype, pkgs in self.missing_pkg.items(): + install += pkgs return " ".join(sorted(set(install))) @@ -267,8 +267,6 @@ class AncillaryMethods: "python3.[0-9][0-9]", ] - new_python_cmd = None - # Seek for a python binary newer than MIN_PYTHON_VERSION for path in os.getenv("PATH", "").split(":"): for pattern in patterns: @@ -276,7 +274,7 @@ class AncillaryMethods: if os.path.isfile(cmd) and os.access(cmd, os.X_OK): version = SphinxDependencyChecker.get_python_version(cmd) if version >= MIN_PYTHON_VERSION: - return(cmd) + return cmd @staticmethod def check_python(): @@ -306,8 +304,8 @@ class AncillaryMethods: new_python_cmd = SphinxDependencyChecker.find_python() if not new_python_cmd: - print(f"ERROR: Python version {python_ver} is not spported anymore") - print(f" Can't find a new version. This script may fail") + print(f"ERROR: Python version {python_ver} is not spported anymore\n") + print(" Can't find a new version. This script may fail") return # Restart script using the newer version @@ -362,6 +360,9 @@ class MissingCheckers(AncillaryMethods): self.version_check = args.version_check self.texlive = texlive + self.min_version = (0, 0, 0) + self.cur_version = (0, 0, 0) + self.deps = DepManager(self.pdf) self.need_symlink = 0 @@ -370,8 +371,10 @@ class MissingCheckers(AncillaryMethods): self.verbose_warn_install = 1 self.virtenv_dir = "" - self.install = "" + self.python_cmd = "" + + self.virtenv_prefix = ["sphinx_", "Sphinx_" ] def check_missing_file(self, files, package, dtype): """ @@ -542,10 +545,10 @@ class MissingCheckers(AncillaryMethods): self.min_version = parse_version(match.group(1)) break except IOError: - sys.exit(f"Can't open {self.conf}") + sys.exit(f"Can't open {conf}") if not self.min_version: - sys.exit(f"Can't get needs_sphinx version from {self.conf}") + sys.exit(f"Can't get needs_sphinx version from {conf}") self.virtenv_dir = self.virtenv_prefix[0] + "latest" @@ -709,7 +712,6 @@ class SphinxDependencyChecker(MissingCheckers): self.rec_sphinx_upgrade = 0 self.system_release = self.get_system_release() - self.python_cmd = "" self.activate_cmd = "" # Some distros may not have a Sphinx shipped package compatible with @@ -722,8 +724,6 @@ class SphinxDependencyChecker(MissingCheckers): # Certain hints are meant to be shown only once self.first_hint = True - self.min_version = (0, 0, 0) - self.cur_version = (0, 0, 0) self.latest_avail_ver = (0, 0, 0) self.venv_ver = (0, 0, 0) @@ -731,7 +731,6 @@ class SphinxDependencyChecker(MissingCheckers): self.conf = prefix + "Documentation/conf.py" self.requirement_file = prefix + "Documentation/sphinx/requirements.txt" - self.virtenv_prefix = ["sphinx_", "Sphinx_" ] # # Distro-specific hints methods @@ -814,6 +813,7 @@ class SphinxDependencyChecker(MissingCheckers): if not rel: print("Couldn't identify release number") + noto_sans_redhat = None self.pdf = False elif re.search("Fedora", self.system_release): # Fedora 38 and upper use this CJK font @@ -1111,7 +1111,7 @@ class SphinxDependencyChecker(MissingCheckers): for fname, portage in portages.items(): install = False - while install == False: + while install is False: if not files: # No files under package.usage. Install all install = True @@ -1335,7 +1335,7 @@ class SphinxDependencyChecker(MissingCheckers): old_optional = self.deps.optional self.pdf = False - self.optional = 0 + self.deps.optional = 0 self.install = "" old_verbose = self.verbose_warn_install self.verbose_warn_install = 0 @@ -1346,8 +1346,8 @@ class SphinxDependencyChecker(MissingCheckers): self.check_distros() - self.need = old_need - self.optional = old_optional + self.deps.need = old_need + self.deps.optional = old_optional self.verbose_warn_install = old_verbose def recommend_sphinx_version(self, virtualenv_cmd): @@ -1528,13 +1528,13 @@ class SphinxDependencyChecker(MissingCheckers): self.recommend_sphinx_version(virtualenv_cmd) print("") - if not self.optional: + if not self.deps.optional: print("All optional dependencies are met.") - if self.need == 1: + if self.deps.need == 1: sys.exit("Can't build as 1 mandatory dependency is missing") - elif self.need: - sys.exit(f"Can't build as {self.need} mandatory dependencies are missing") + elif self.deps.need: + sys.exit(f"Can't build as {self.deps.need} mandatory dependencies are missing") print("Needed package dependencies are met.") -- cgit v1.2.3 From 24a34b3b453dc8fff0366a28c472b603f98a5c20 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:50 +0200 Subject: scripts: sphinx-pre-install: rework install command logic Cleanup the code to remove some redundancy and to let it be clearer about the command install instructions. Ensure that special instructions will be shown only once, before the actual install command. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/a6120449d9cc14346e867d1ef8944ae28ddbf3f6.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.py | 179 +++++++++++++++++++----------------------- 1 file changed, 82 insertions(+), 97 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py index 1b11162da9fb..7dfe5c2a6cc2 100755 --- a/scripts/sphinx-pre-install.py +++ b/scripts/sphinx-pre-install.py @@ -213,7 +213,7 @@ class DepManager: raise KeyError(f"ERROR!!!: invalid dtype for {progs}: {dtype}") if output_msg: - print(f"\n{output_msg}\n") + print(f"\n{output_msg}") class AncillaryMethods: """ @@ -583,23 +583,6 @@ class MissingCheckers(AncillaryMethods): return f.read().strip() return "" - def check_missing(self, progs): - """ - Check for missing dependencies using the provided program mapping. - - The actual distro-specific programs are mapped via progs argument. - - Returns True if there are missing dependencies, False otherwise. - """ - self.install += self.deps.check_missing(progs) - if self.verbose_warn_install: - self.deps.warn_install() - - if not self.deps.need and not self.deps.optional: - return False - - return True - def get_system_release(self): """ Determine the system type. There's no unique way that would work @@ -722,7 +705,7 @@ class SphinxDependencyChecker(MissingCheckers): self.recommend_python = None # Certain hints are meant to be shown only once - self.first_hint = True + self.distro_msg = None self.latest_avail_ver = (0, 0, 0) self.venv_ver = (0, 0, 0) @@ -732,6 +715,33 @@ class SphinxDependencyChecker(MissingCheckers): self.conf = prefix + "Documentation/conf.py" self.requirement_file = prefix + "Documentation/sphinx/requirements.txt" + def get_install_progs(self, progs, cmd, extra=None): + """ + Check for missing dependencies using the provided program mapping. + + The actual distro-specific programs are mapped via progs argument. + """ + install = self.deps.check_missing(progs) + + if self.verbose_warn_install: + self.deps.warn_install() + + if not install: + return + + if cmd: + if self.verbose_warn_install: + msg = "You should run:" + else: + msg = "" + + if extra: + msg += "\n\t" + extra.replace("\n", "\n\t") + + return(msg + "\n\tsudo " + cmd + " " + install) + + return None + # # Distro-specific hints methods # @@ -772,12 +782,7 @@ class SphinxDependencyChecker(MissingCheckers): self.check_program("dvipng", DepManager.PDF_MANDATORY) - if self.check_missing(progs): - return - - if self.verbose_warn_install: - print("You should run:") - print(f"\n\tsudo apt-get install {self.install}") + return self.get_install_progs(progs, "apt-get install") def give_redhat_hints(self): """ @@ -838,18 +843,16 @@ class SphinxDependencyChecker(MissingCheckers): self.deps.add_package("python39", DepManager.SYSTEM_MANDATORY) self.recommend_python = True - if self.first_hint: - print("Note: RHEL-based distros typically require extra repositories.\n" \ - "For most, enabling epel and crb are enough:\n" \ - "\tsudo dnf install -y epel-release", \ - "\tsudo dnf config-manager --set-enabled crb\n" \ - "Yet, some may have other required repositories. Those commands could be useful:\n" \ - "\tsudo dnf repolist all\n" \ - "\tsudo dnf repoquery --available --info \n", - "\tsudo dnf config-manager --set-enabled '*' # enable all - probably not what you want") - - self.first_hint = False - + if not self.distro_msg: + self.distro_msg = \ + "Note: RHEL-based distros typically require extra repositories.\n" \ + "For most, enabling epel and crb are enough:\n" \ + "\tsudo dnf install -y epel-release\n" \ + "\tsudo dnf config-manager --set-enabled crb\n" \ + "Yet, some may have other required repositories. Those commands could be useful:\n" \ + "\tsudo dnf repolist all\n" \ + "\tsudo dnf repoquery --available --info \n" \ + "\tsudo dnf config-manager --set-enabled '*' # enable all - probably not what you want" if self.pdf: pdf_pkgs = [ @@ -868,13 +871,7 @@ class SphinxDependencyChecker(MissingCheckers): if not fedora and rel == 8: self.deps.del_package("texlive-ctex") - if self.check_missing(progs): - return - - if self.verbose_warn_install: - print("You should run:") - - print(f"\n\tsudo dnf install -y {self.install}") + return self.get_install_progs(progs, "dnf install") def give_opensuse_hints(self): """ @@ -945,12 +942,7 @@ class SphinxDependencyChecker(MissingCheckers): if self.pdf: self.check_missing_tex() - if self.check_missing(progs): - return - - if self.verbose_warn_install: - print("You should run:") - print(f"\n\tsudo zypper install --no-recommends {self.install}") + return self.get_install_progs(progs, "zypper install --no-recommends") def give_mageia_hints(self): """ @@ -995,12 +987,7 @@ class SphinxDependencyChecker(MissingCheckers): self.check_missing_file(pdf_pkgs, noto_sans, DepManager.PDF_MANDATORY) self.check_rpm_missing(tex_pkgs, DepManager.PDF_MANDATORY) - if self.check_missing(progs): - return - - if self.verbose_warn_install: - print("You should run:") - print(f"\n\tsudo {packager_cmd} {self.install}") + return self.get_install_progs(progs, packager_cmd) def give_arch_linux_hints(self): """ @@ -1023,20 +1010,15 @@ class SphinxDependencyChecker(MissingCheckers): ] if self.pdf: - self.check_pacman_missing(archlinux_tex_pkgs, DepManager.PDF_MANDATORY) + self.check_pacman_missing(archlinux_tex_pkgs, + DepManager.PDF_MANDATORY) - self.check_missing_file( - ["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], - "noto-fonts-cjk", - 2, - ) + self.check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], + "noto-fonts-cjk", + DepManager.PDF_MANDATORY) - if self.check_missing(progs): - return - if self.verbose_warn_install: - print("You should run:") - print(f"\n\tsudo pacman -S {self.install}") + return self.get_install_progs(progs, "pacman -S") def give_gentoo_hints(self): """ @@ -1065,13 +1047,6 @@ class SphinxDependencyChecker(MissingCheckers): for package, files in pdf_pkgs.items(): self.check_missing_file(files, package, DepManager.PDF_MANDATORY) - if self.check_missing(progs): - return - - if self.verbose_warn_install: - print("You should run:") - print("\n") - # Handling dependencies is a nightmare, as Gentoo refuses to emerge # some packages if there's no package.use file describing them. # To make it worse, compilation flags shall also be present there @@ -1104,7 +1079,10 @@ class SphinxDependencyChecker(MissingCheckers): "zziblib": "dev-libs/zziplib sdl", } - if self.first_hint: + extra_cmds = "" + if not self.distro_msg: + self.distro_msg = "Note: Gentoo requires package.use to be adjusted before emerging packages" + use_base = "/etc/portage/package.use" files = glob(f"{use_base}/*") @@ -1148,23 +1126,23 @@ class SphinxDependencyChecker(MissingCheckers): # emit a code to setup missing USE if install: - print(f"\tsudo su -c 'echo \"{portage}\" > {use_base}/{fname}'") - - self.first_hint = False + extra_cmds += (f"sudo su -c 'echo \"{portage}\" > {use_base}/{fname}'\n") # Now, we can use emerge and let it respect USE - print(f"\tsudo emerge --ask --changed-use --binpkg-respect-use=y {self.install}") + return self.get_install_progs(progs, + "emerge --ask --changed-use --binpkg-respect-use=y", + extra_cmds) - # - # Dispatch the check to an os_specific hinter - # - - def check_distros(self): + def get_install(self): """ - OS-specific hints logic. Seeks for a hinter. If found, provide - package-manager-specific install commands. + OS-specific hints logic. Seeks for a hinter. If found, use it to + provide package-manager specific install commands. + + Otherwise, outputs install instructions for the meta-packages. - Otherwise, just lists the missing dependencies. + Returns a string with the command to be executed to install the + the needed packages, if distro found. Otherwise, return just a + list of packages that require installation. """ os_hints = { re.compile("Red Hat Enterprise Linux"): self.give_redhat_hints, @@ -1195,9 +1173,7 @@ class SphinxDependencyChecker(MissingCheckers): # If the OS is detected, use per-OS hint logic for regex, os_hint in os_hints.items(): if regex.search(self.system_release): - os_hint() - - return + return os_hint() # # Fall-back to generic hint code for other distros @@ -1207,11 +1183,12 @@ class SphinxDependencyChecker(MissingCheckers): if self.pdf: self.check_missing_tex() - self.check_missing(progs) + self.distro_msg = \ + f"I don't know distro {self.system_release}.\n" \ + "So, I can't provide you a hint with the install procedure.\n" \ + "There are likely missing dependencies.\n" - print(f"I don't know distro {self.system_release}.") - print("So, I can't provide you a hint with the install procedure.") - print("There are likely missing dependencies.") + return self.get_install_progs(progs, None) # # Common dependencies @@ -1336,7 +1313,6 @@ class SphinxDependencyChecker(MissingCheckers): self.pdf = False self.deps.optional = 0 - self.install = "" old_verbose = self.verbose_warn_install self.verbose_warn_install = 0 @@ -1344,7 +1320,9 @@ class SphinxDependencyChecker(MissingCheckers): self.deps.add_package("python-sphinx", DepManager.PYTHON_MANDATORY) - self.check_distros() + cmd = self.get_install() + if cmd: + print(cmd) self.deps.need = old_need self.deps.optional = old_optional @@ -1511,7 +1489,14 @@ class SphinxDependencyChecker(MissingCheckers): self.check_program("latexmk", DepManager.PDF_MANDATORY) # Do distro-specific checks and output distro-install commands - self.check_distros() + cmd = self.get_install() + if cmd: + print(cmd) + + # If distro requires some special instructions, print here. + # Please notice that get_install() needs to be called first. + if self.distro_msg: + print("\n" + self.distro_msg) if not self.python_cmd: if self.need == 1: -- cgit v1.2.3 From d43cd965f3a646d22851192c659b787dba311d87 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:51 +0200 Subject: docs: Makefile: switch to the new scripts/sphinx-pre-install.py Now that we have a better, improved Python script, use it when checking for documentation build dependencies. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/79508fb071512c33e807f5411bbff1904751b5d3.1754992972.git.mchehab+huawei@kernel.org --- Documentation/Makefile | 14 +- scripts/sphinx-pre-install | 1565 +++++++++++++++++++++++++++++++++++++++++ scripts/sphinx-pre-install.py | 1565 ----------------------------------------- 3 files changed, 1572 insertions(+), 1572 deletions(-) create mode 100755 scripts/sphinx-pre-install delete mode 100755 scripts/sphinx-pre-install.py (limited to 'scripts') diff --git a/Documentation/Makefile b/Documentation/Makefile index c486fe3cc5e1..b98477df5ddf 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -46,7 +46,7 @@ ifeq ($(HAVE_SPHINX),0) .DEFAULT: $(warning The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed and in PATH, or set the SPHINXBUILD make variable to point to the full path of the '$(SPHINXBUILD)' executable.) @echo - @$(srctree)/scripts/sphinx-pre-install.pl + @$(srctree)/scripts/sphinx-pre-install @echo " SKIP Sphinx $@ target." else # HAVE_SPHINX @@ -121,7 +121,7 @@ $(YNL_RST_DIR)/%.rst: $(YNL_YAML_DIR)/%.yaml $(YNL_TOOL) htmldocs texinfodocs latexdocs epubdocs xmldocs: $(YNL_INDEX) htmldocs: - @$(srctree)/scripts/sphinx-pre-install.pl --version-check + @$(srctree)/scripts/sphinx-pre-install --version-check @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,html,$(var),,$(var))) # If Rust support is available and .config exists, add rustdoc generated contents. @@ -135,7 +135,7 @@ endif endif texinfodocs: - @$(srctree)/scripts/sphinx-pre-install.pl --version-check + @$(srctree)/scripts/sphinx-pre-install --version-check @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,texinfo,$(var),texinfo,$(var))) # Note: the 'info' Make target is generated by sphinx itself when @@ -147,7 +147,7 @@ linkcheckdocs: @$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,linkcheck,$(var),,$(var))) latexdocs: - @$(srctree)/scripts/sphinx-pre-install.pl --version-check + @$(srctree)/scripts/sphinx-pre-install --version-check @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,latex,$(var),latex,$(var))) ifeq ($(HAVE_PDFLATEX),0) @@ -160,7 +160,7 @@ else # HAVE_PDFLATEX pdfdocs: DENY_VF = XDG_CONFIG_HOME=$(FONTS_CONF_DENY_VF) pdfdocs: latexdocs - @$(srctree)/scripts/sphinx-pre-install.pl --version-check + @$(srctree)/scripts/sphinx-pre-install --version-check $(foreach var,$(SPHINXDIRS), \ $(MAKE) PDFLATEX="$(PDFLATEX)" LATEXOPTS="$(LATEXOPTS)" $(DENY_VF) -C $(BUILDDIR)/$(var)/latex || sh $(srctree)/scripts/check-variable-fonts.sh || exit; \ mkdir -p $(BUILDDIR)/$(var)/pdf; \ @@ -170,11 +170,11 @@ pdfdocs: latexdocs endif # HAVE_PDFLATEX epubdocs: - @$(srctree)/scripts/sphinx-pre-install.pl --version-check + @$(srctree)/scripts/sphinx-pre-install --version-check @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,epub,$(var),epub,$(var))) xmldocs: - @$(srctree)/scripts/sphinx-pre-install.pl --version-check + @$(srctree)/scripts/sphinx-pre-install --version-check @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,xml,$(var),xml,$(var))) endif # HAVE_SPHINX diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install new file mode 100755 index 000000000000..7dfe5c2a6cc2 --- /dev/null +++ b/scripts/sphinx-pre-install @@ -0,0 +1,1565 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2017-2025 Mauro Carvalho Chehab +# +# pylint: disable=C0103,C0114,C0115,C0116,C0301,C0302 +# pylint: disable=R0902,R0904,R0911,R0912,R0914,R0915,R1705,R1710,E1121 + +# Note: this script requires at least Python 3.6 to run. +# Don't add changes not compatible with it, it is meant to report +# incompatible python versions. + +""" +Dependency checker for Sphinx documentation Kernel build. + +This module provides tools to check for all required dependencies needed to +build documentation using Sphinx, including system packages, Python modules +and LaTeX packages for PDF generation. + +It detect packages for a subset of Linux distributions used by Kernel +maintainers, showing hints and missing dependencies. + +The main class SphinxDependencyChecker handles the dependency checking logic +and provides recommendations for installing missing packages. It supports both +system package installations and Python virtual environments. By default, +system pacage install is recommended. +""" + +import argparse +import os +import re +import subprocess +import sys +from glob import glob + + +def parse_version(version): + """Convert a major.minor.patch version into a tuple""" + return tuple(int(x) for x in version.split(".")) + + +def ver_str(version): + """Returns a version tuple as major.minor.patch""" + + return ".".join([str(x) for x in version]) + + +RECOMMENDED_VERSION = parse_version("3.4.3") +MIN_PYTHON_VERSION = parse_version("3.7") + + +class DepManager: + """ + Manage package dependencies. There are three types of dependencies: + + - System: dependencies required for docs build; + - Python: python dependencies for a native distro Sphinx install; + - PDF: dependencies needed by PDF builds. + + Each dependency can be mandatory or optional. Not installing an optional + dependency won't break the build, but will cause degradation at the + docs output. + """ + + # Internal types of dependencies. Don't use them outside DepManager class. + _SYS_TYPE = 0 + _PHY_TYPE = 1 + _PDF_TYPE = 2 + + # Dependencies visible outside the class. + # The keys are tuple with: (type, is_mandatory flag). + # + # Currently we're not using all optional dep types. Yet, we'll keep all + # possible combinations here. They're not many, and that makes easier + # if later needed and for the name() method below + + SYSTEM_MANDATORY = (_SYS_TYPE, True) + PYTHON_MANDATORY = (_PHY_TYPE, True) + PDF_MANDATORY = (_PDF_TYPE, True) + + SYSTEM_OPTIONAL = (_SYS_TYPE, False) + PYTHON_OPTIONAL = (_PHY_TYPE, False) + PDF_OPTIONAL = (_PDF_TYPE, True) + + def __init__(self, pdf): + """ + Initialize internal vars: + + - missing: missing dependencies list, containing a distro-independent + name for a missing dependency and its type. + - missing_pkg: ancillary dict containing missing dependencies in + distro namespace, organized by type. + - need: total number of needed dependencies. Never cleaned. + - optional: total number of optional dependencies. Never cleaned. + - pdf: Is PDF support enabled? + """ + self.missing = {} + self.missing_pkg = {} + self.need = 0 + self.optional = 0 + self.pdf = pdf + + @staticmethod + def name(dtype): + """ + Ancillary routine to output a warn/error message reporting + missing dependencies. + """ + if dtype[0] == DepManager._SYS_TYPE: + msg = "build" + elif dtype[0] == DepManager._PHY_TYPE: + msg = "Python" + else: + msg = "PDF" + + if dtype[1]: + return f"ERROR: {msg} mandatory deps missing" + else: + return f"Warning: {msg} optional deps missing" + + @staticmethod + def is_optional(dtype): + """Ancillary routine to report if a dependency is optional""" + return not dtype[1] + + @staticmethod + def is_pdf(dtype): + """Ancillary routine to report if a dependency is for PDF generation""" + if dtype[0] == DepManager._PDF_TYPE: + return True + + return False + + def add_package(self, package, dtype): + """ + Add a package at the self.missing() dictionary. + Doesn't update missing_pkg. + """ + is_optional = DepManager.is_optional(dtype) + self.missing[package] = dtype + if is_optional: + self.optional += 1 + else: + self.need += 1 + + def del_package(self, package): + """ + Remove a package at the self.missing() dictionary. + Doesn't update missing_pkg. + """ + if package in self.missing: + del self.missing[package] + + def clear_deps(self): + """ + Clear dependencies without changing needed/optional. + + This is an ackward way to have a separate section to recommend + a package after system main dependencies. + + TODO: rework the logic to prevent needing it. + """ + + self.missing = {} + self.missing_pkg = {} + + def check_missing(self, progs): + """ + Update self.missing_pkg, using progs dict to convert from the + agnostic package name to distro-specific one. + + Returns an string with the packages to be installed, sorted and + with eventual duplicates removed. + """ + + self.missing_pkg = {} + + for prog, dtype in sorted(self.missing.items()): + # At least on some LTS distros like CentOS 7, texlive doesn't + # provide all packages we need. When such distros are + # detected, we have to disable PDF output. + # + # So, we need to ignore the packages that distros would + # need for LaTeX to work + if DepManager.is_pdf(dtype) and not self.pdf: + self.optional -= 1 + continue + + if not dtype in self.missing_pkg: + self.missing_pkg[dtype] = [] + + self.missing_pkg[dtype].append(progs.get(prog, prog)) + + install = [] + for dtype, pkgs in self.missing_pkg.items(): + install += pkgs + + return " ".join(sorted(set(install))) + + def warn_install(self): + """ + Emit warnings/errors related to missing packages. + """ + + output_msg = "" + + for dtype in sorted(self.missing_pkg.keys()): + progs = " ".join(sorted(set(self.missing_pkg[dtype]))) + + try: + name = DepManager.name(dtype) + output_msg += f'{name}:\t{progs}\n' + except KeyError: + raise KeyError(f"ERROR!!!: invalid dtype for {progs}: {dtype}") + + if output_msg: + print(f"\n{output_msg}") + +class AncillaryMethods: + """ + Ancillary methods that checks for missing dependencies for different + types of types, like binaries, python modules, rpm deps, etc. + """ + + @staticmethod + def which(prog): + """ + Our own implementation of which(). We could instead use + shutil.which(), but this function is simple enough. + Probably faster to use this implementation than to import shutil. + """ + for path in os.environ.get("PATH", "").split(":"): + full_path = os.path.join(path, prog) + if os.access(full_path, os.X_OK): + return full_path + + return None + + @staticmethod + def get_python_version(cmd): + """ + Get python version from a Python binary. As we need to detect if + are out there newer python binaries, we can't rely on sys.release here. + """ + + result = SphinxDependencyChecker.run([cmd, "--version"], + capture_output=True, text=True) + version = result.stdout.strip() + + match = re.search(r"(\d+\.\d+\.\d+)", version) + if match: + return parse_version(match.group(1)) + + print(f"Can't parse version {version}") + return (0, 0, 0) + + @staticmethod + def find_python(): + """ + Detect if are out there any python 3.xy version newer than the + current one. + + Note: this routine is limited to up to 2 digits for python3. We + may need to update it one day, hopefully on a distant future. + """ + patterns = [ + "python3.[0-9]", + "python3.[0-9][0-9]", + ] + + # Seek for a python binary newer than MIN_PYTHON_VERSION + for path in os.getenv("PATH", "").split(":"): + for pattern in patterns: + for cmd in glob(os.path.join(path, pattern)): + if os.path.isfile(cmd) and os.access(cmd, os.X_OK): + version = SphinxDependencyChecker.get_python_version(cmd) + if version >= MIN_PYTHON_VERSION: + return cmd + + @staticmethod + def check_python(): + """ + Check if the current python binary satisfies our minimal requirement + for Sphinx build. If not, re-run with a newer version if found. + """ + cur_ver = sys.version_info[:3] + if cur_ver >= MIN_PYTHON_VERSION: + ver = ver_str(cur_ver) + print(f"Python version: {ver}") + + # This could be useful for debugging purposes + if SphinxDependencyChecker.which("docutils"): + result = SphinxDependencyChecker.run(["docutils", "--version"], + capture_output=True, text=True) + ver = result.stdout.strip() + match = re.search(r"(\d+\.\d+\.\d+)", ver) + if match: + ver = match.group(1) + + print(f"Docutils version: {ver}") + + return + + python_ver = ver_str(cur_ver) + + new_python_cmd = SphinxDependencyChecker.find_python() + if not new_python_cmd: + print(f"ERROR: Python version {python_ver} is not spported anymore\n") + print(" Can't find a new version. This script may fail") + return + + # Restart script using the newer version + script_path = os.path.abspath(sys.argv[0]) + args = [new_python_cmd, script_path] + sys.argv[1:] + + print(f"Python {python_ver} not supported. Changing to {new_python_cmd}") + + try: + os.execv(new_python_cmd, args) + except OSError as e: + sys.exit(f"Failed to restart with {new_python_cmd}: {e}") + + @staticmethod + def run(*args, **kwargs): + """ + Excecute a command, hiding its output by default. + Preserve comatibility with older Python versions. + """ + + capture_output = kwargs.pop('capture_output', False) + + if capture_output: + if 'stdout' not in kwargs: + kwargs['stdout'] = subprocess.PIPE + if 'stderr' not in kwargs: + kwargs['stderr'] = subprocess.PIPE + else: + if 'stdout' not in kwargs: + kwargs['stdout'] = subprocess.DEVNULL + if 'stderr' not in kwargs: + kwargs['stderr'] = subprocess.DEVNULL + + # Don't break with older Python versions + if 'text' in kwargs and sys.version_info < (3, 7): + kwargs['universal_newlines'] = kwargs.pop('text') + + return subprocess.run(*args, **kwargs) + +class MissingCheckers(AncillaryMethods): + """ + Contains some ancillary checkers for different types of binaries and + package managers. + """ + + def __init__(self, args, texlive): + """ + Initialize its internal variables + """ + self.pdf = args.pdf + self.virtualenv = args.virtualenv + self.version_check = args.version_check + self.texlive = texlive + + self.min_version = (0, 0, 0) + self.cur_version = (0, 0, 0) + + self.deps = DepManager(self.pdf) + + self.need_symlink = 0 + self.need_sphinx = 0 + + self.verbose_warn_install = 1 + + self.virtenv_dir = "" + self.install = "" + self.python_cmd = "" + + self.virtenv_prefix = ["sphinx_", "Sphinx_" ] + + def check_missing_file(self, files, package, dtype): + """ + Does the file exists? If not, add it to missing dependencies. + """ + for f in files: + if os.path.exists(f): + return + self.deps.add_package(package, dtype) + + def check_program(self, prog, dtype): + """ + Does the program exists and it is at the PATH? + If not, add it to missing dependencies. + """ + found = self.which(prog) + if found: + return found + + self.deps.add_package(prog, dtype) + + return None + + def check_perl_module(self, prog, dtype): + """ + Does perl have a dependency? Is it available? + If not, add it to missing dependencies. + + Right now, we still need Perl for doc build, as it is required + by some tools called at docs or kernel build time, like: + + scripts/documentation-file-ref-check + + Also, checkpatch is on Perl. + """ + + # While testing with lxc download template, one of the + # distros (Oracle) didn't have perl - nor even an option to install + # before installing oraclelinux-release-el9 package. + # + # Check it before running an error. If perl is not there, + # add it as a mandatory package, as some parts of the doc builder + # needs it. + if not self.which("perl"): + self.deps.add_package("perl", DepManager.SYSTEM_MANDATORY) + self.deps.add_package(prog, dtype) + return + + try: + self.run(["perl", f"-M{prog}", "-e", "1"], check=True) + except subprocess.CalledProcessError: + self.deps.add_package(prog, dtype) + + def check_python_module(self, module, is_optional=False): + """ + Does a python module exists outside venv? If not, add it to missing + dependencies. + """ + if is_optional: + dtype = DepManager.PYTHON_OPTIONAL + else: + dtype = DepManager.PYTHON_MANDATORY + + try: + self.run([self.python_cmd, "-c", f"import {module}"], check=True) + except subprocess.CalledProcessError: + self.deps.add_package(module, dtype) + + def check_rpm_missing(self, pkgs, dtype): + """ + Does a rpm package exists? If not, add it to missing dependencies. + """ + for prog in pkgs: + try: + self.run(["rpm", "-q", prog], check=True) + except subprocess.CalledProcessError: + self.deps.add_package(prog, dtype) + + def check_pacman_missing(self, pkgs, dtype): + """ + Does a pacman package exists? If not, add it to missing dependencies. + """ + for prog in pkgs: + try: + self.run(["pacman", "-Q", prog], check=True) + except subprocess.CalledProcessError: + self.deps.add_package(prog, dtype) + + def check_missing_tex(self, is_optional=False): + """ + Does a LaTeX package exists? If not, add it to missing dependencies. + """ + if is_optional: + dtype = DepManager.PDF_OPTIONAL + else: + dtype = DepManager.PDF_MANDATORY + + kpsewhich = self.which("kpsewhich") + for prog, package in self.texlive.items(): + + # If kpsewhich is not there, just add it to deps + if not kpsewhich: + self.deps.add_package(package, dtype) + continue + + # Check if the package is needed + try: + result = self.run( + [kpsewhich, prog], stdout=subprocess.PIPE, text=True, check=True + ) + + # Didn't find. Add it + if not result.stdout.strip(): + self.deps.add_package(package, dtype) + + except subprocess.CalledProcessError: + # kpsewhich returned an error. Add it, just in case + self.deps.add_package(package, dtype) + + def get_sphinx_fname(self): + """ + Gets the binary filename for sphinx-build. + """ + if "SPHINXBUILD" in os.environ: + return os.environ["SPHINXBUILD"] + + fname = "sphinx-build" + if self.which(fname): + return fname + + fname = "sphinx-build-3" + if self.which(fname): + self.need_symlink = 1 + return fname + + return "" + + def get_sphinx_version(self, cmd): + """ + Gets sphinx-build version. + """ + try: + result = self.run([cmd, "--version"], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, check=True) + except (subprocess.CalledProcessError, FileNotFoundError): + return None + + for line in result.stdout.split("\n"): + match = re.match(r"^sphinx-build\s+([\d\.]+)(?:\+(?:/[\da-f]+)|b\d+)?\s*$", line) + if match: + return parse_version(match.group(1)) + + match = re.match(r"^Sphinx.*\s+([\d\.]+)\s*$", line) + if match: + return parse_version(match.group(1)) + + def check_sphinx(self, conf): + """ + Checks Sphinx minimal requirements + """ + try: + with open(conf, "r", encoding="utf-8") as f: + for line in f: + match = re.match(r"^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]", line) + if match: + self.min_version = parse_version(match.group(1)) + break + except IOError: + sys.exit(f"Can't open {conf}") + + if not self.min_version: + sys.exit(f"Can't get needs_sphinx version from {conf}") + + self.virtenv_dir = self.virtenv_prefix[0] + "latest" + + sphinx = self.get_sphinx_fname() + if not sphinx: + self.need_sphinx = 1 + return + + self.cur_version = self.get_sphinx_version(sphinx) + if not self.cur_version: + sys.exit(f"{sphinx} didn't return its version") + + if self.cur_version < self.min_version: + curver = ver_str(self.cur_version) + minver = ver_str(self.min_version) + + print(f"ERROR: Sphinx version is {curver}. It should be >= {minver}") + self.need_sphinx = 1 + return + + # On version check mode, just assume Sphinx has all mandatory deps + if self.version_check and self.cur_version >= RECOMMENDED_VERSION: + sys.exit(0) + + def catcheck(self, filename): + """ + Reads a file if it exists, returning as string. + If not found, returns an empty string. + """ + if os.path.exists(filename): + with open(filename, "r", encoding="utf-8") as f: + return f.read().strip() + return "" + + def get_system_release(self): + """ + Determine the system type. There's no unique way that would work + with all distros with a minimal package install. So, several + methods are used here. + + By default, it will use lsb_release function. If not available, it will + fail back to reading the known different places where the distro name + is stored. + + Several modern distros now have /etc/os-release, which usually have + a decent coverage. + """ + + system_release = "" + + if self.which("lsb_release"): + result = self.run(["lsb_release", "-d"], capture_output=True, text=True) + system_release = result.stdout.replace("Description:", "").strip() + + release_files = [ + "/etc/system-release", + "/etc/redhat-release", + "/etc/lsb-release", + "/etc/gentoo-release", + ] + + if not system_release: + for f in release_files: + system_release = self.catcheck(f) + if system_release: + break + + # This seems more common than LSB these days + if not system_release: + os_var = {} + try: + with open("/etc/os-release", "r", encoding="utf-8") as f: + for line in f: + match = re.match(r"^([\w\d\_]+)=\"?([^\"]*)\"?\n", line) + if match: + os_var[match.group(1)] = match.group(2) + + system_release = os_var.get("NAME", "") + if "VERSION_ID" in os_var: + system_release += " " + os_var["VERSION_ID"] + elif "VERSION" in os_var: + system_release += " " + os_var["VERSION"] + except IOError: + pass + + if not system_release: + system_release = self.catcheck("/etc/issue") + + system_release = system_release.strip() + + return system_release + +class SphinxDependencyChecker(MissingCheckers): + """ + Main class for checking Sphinx documentation build dependencies. + + - Check for missing system packages; + - Check for missing Python modules; + - Check for missing LaTeX packages needed by PDF generation; + - Propose Sphinx install via Python Virtual environment; + - Propose Sphinx install via distro-specific package install. + """ + def __init__(self, args): + """Initialize checker variables""" + + # List of required texlive packages on Fedora and OpenSuse + texlive = { + "amsfonts.sty": "texlive-amsfonts", + "amsmath.sty": "texlive-amsmath", + "amssymb.sty": "texlive-amsfonts", + "amsthm.sty": "texlive-amscls", + "anyfontsize.sty": "texlive-anyfontsize", + "atbegshi.sty": "texlive-oberdiek", + "bm.sty": "texlive-tools", + "capt-of.sty": "texlive-capt-of", + "cmap.sty": "texlive-cmap", + "ctexhook.sty": "texlive-ctex", + "ecrm1000.tfm": "texlive-ec", + "eqparbox.sty": "texlive-eqparbox", + "eu1enc.def": "texlive-euenc", + "fancybox.sty": "texlive-fancybox", + "fancyvrb.sty": "texlive-fancyvrb", + "float.sty": "texlive-float", + "fncychap.sty": "texlive-fncychap", + "footnote.sty": "texlive-mdwtools", + "framed.sty": "texlive-framed", + "luatex85.sty": "texlive-luatex85", + "multirow.sty": "texlive-multirow", + "needspace.sty": "texlive-needspace", + "palatino.sty": "texlive-psnfss", + "parskip.sty": "texlive-parskip", + "polyglossia.sty": "texlive-polyglossia", + "tabulary.sty": "texlive-tabulary", + "threeparttable.sty": "texlive-threeparttable", + "titlesec.sty": "texlive-titlesec", + "ucs.sty": "texlive-ucs", + "upquote.sty": "texlive-upquote", + "wrapfig.sty": "texlive-wrapfig", + } + + super().__init__(args, texlive) + + self.need_pip = 0 + self.rec_sphinx_upgrade = 0 + + self.system_release = self.get_system_release() + self.activate_cmd = "" + + # Some distros may not have a Sphinx shipped package compatible with + # our minimal requirements + self.package_supported = True + + # Recommend a new python version + self.recommend_python = None + + # Certain hints are meant to be shown only once + self.distro_msg = None + + self.latest_avail_ver = (0, 0, 0) + self.venv_ver = (0, 0, 0) + + prefix = os.environ.get("srctree", ".") + "/" + + self.conf = prefix + "Documentation/conf.py" + self.requirement_file = prefix + "Documentation/sphinx/requirements.txt" + + def get_install_progs(self, progs, cmd, extra=None): + """ + Check for missing dependencies using the provided program mapping. + + The actual distro-specific programs are mapped via progs argument. + """ + install = self.deps.check_missing(progs) + + if self.verbose_warn_install: + self.deps.warn_install() + + if not install: + return + + if cmd: + if self.verbose_warn_install: + msg = "You should run:" + else: + msg = "" + + if extra: + msg += "\n\t" + extra.replace("\n", "\n\t") + + return(msg + "\n\tsudo " + cmd + " " + install) + + return None + + # + # Distro-specific hints methods + # + + def give_debian_hints(self): + """ + Provide package installation hints for Debian-based distros. + """ + progs = { + "Pod::Usage": "perl-modules", + "convert": "imagemagick", + "dot": "graphviz", + "ensurepip": "python3-venv", + "python-sphinx": "python3-sphinx", + "rsvg-convert": "librsvg2-bin", + "virtualenv": "virtualenv", + "xelatex": "texlive-xetex", + "yaml": "python3-yaml", + } + + if self.pdf: + pdf_pkgs = { + "texlive-lang-chinese": [ + "/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty", + ], + "fonts-dejavu": [ + "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", + ], + "fonts-noto-cjk": [ + "/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc", + ], + } + + for package, files in pdf_pkgs.items(): + self.check_missing_file(files, package, DepManager.PDF_MANDATORY) + + self.check_program("dvipng", DepManager.PDF_MANDATORY) + + return self.get_install_progs(progs, "apt-get install") + + def give_redhat_hints(self): + """ + Provide package installation hints for RedHat-based distros + (Fedora, RHEL and RHEL-based variants). + """ + progs = { + "Pod::Usage": "perl-Pod-Usage", + "convert": "ImageMagick", + "dot": "graphviz", + "python-sphinx": "python3-sphinx", + "rsvg-convert": "librsvg2-tools", + "virtualenv": "python3-virtualenv", + "xelatex": "texlive-xetex-bin", + "yaml": "python3-pyyaml", + } + + fedora_tex_pkgs = [ + "dejavu-sans-fonts", + "dejavu-sans-mono-fonts", + "dejavu-serif-fonts", + "texlive-collection-fontsrecommended", + "texlive-collection-latex", + "texlive-xecjk", + ] + + fedora = False + rel = None + + match = re.search(r"(release|Linux)\s+(\d+)", self.system_release) + if match: + rel = int(match.group(2)) + + if not rel: + print("Couldn't identify release number") + noto_sans_redhat = None + self.pdf = False + elif re.search("Fedora", self.system_release): + # Fedora 38 and upper use this CJK font + + noto_sans_redhat = "google-noto-sans-cjk-fonts" + fedora = True + else: + # Almalinux, CentOS, RHEL, ... + + # at least up to version 9 (and Fedora < 38), that's the CJK font + noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts" + + progs["virtualenv"] = "python-virtualenv" + + if not rel or rel < 8: + print("ERROR: Distro not supported. Too old?") + return + + # RHEL 8 uses Python 3.6, which is not compatible with + # the build system anymore. Suggest Python 3.11 + if rel == 8: + self.deps.add_package("python39", DepManager.SYSTEM_MANDATORY) + self.recommend_python = True + + if not self.distro_msg: + self.distro_msg = \ + "Note: RHEL-based distros typically require extra repositories.\n" \ + "For most, enabling epel and crb are enough:\n" \ + "\tsudo dnf install -y epel-release\n" \ + "\tsudo dnf config-manager --set-enabled crb\n" \ + "Yet, some may have other required repositories. Those commands could be useful:\n" \ + "\tsudo dnf repolist all\n" \ + "\tsudo dnf repoquery --available --info \n" \ + "\tsudo dnf config-manager --set-enabled '*' # enable all - probably not what you want" + + if self.pdf: + pdf_pkgs = [ + "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc", + ] + + self.check_missing_file(pdf_pkgs, noto_sans_redhat, DepManager.PDF_MANDATORY) + + self.check_rpm_missing(fedora_tex_pkgs, DepManager.PDF_MANDATORY) + + self.check_missing_tex(DepManager.PDF_MANDATORY) + + # There's no texlive-ctex on RHEL 8 repositories. This will + # likely affect CJK pdf build only. + if not fedora and rel == 8: + self.deps.del_package("texlive-ctex") + + return self.get_install_progs(progs, "dnf install") + + def give_opensuse_hints(self): + """ + Provide package installation hints for openSUSE-based distros + (Leap and Tumbleweed). + """ + progs = { + "Pod::Usage": "perl-Pod-Usage", + "convert": "ImageMagick", + "dot": "graphviz", + "python-sphinx": "python3-sphinx", + "virtualenv": "python3-virtualenv", + "xelatex": "texlive-xetex-bin", + "yaml": "python3-pyyaml", + } + + suse_tex_pkgs = [ + "texlive-babel-english", + "texlive-caption", + "texlive-colortbl", + "texlive-courier", + "texlive-dvips", + "texlive-helvetic", + "texlive-makeindex", + "texlive-metafont", + "texlive-metapost", + "texlive-palatino", + "texlive-preview", + "texlive-times", + "texlive-zapfchan", + "texlive-zapfding", + ] + + progs["latexmk"] = "texlive-latexmk-bin" + + match = re.search(r"(Leap)\s+(\d+).(\d)", self.system_release) + if match: + rel = int(match.group(2)) + + # Leap 15.x uses Python 3.6, which is not compatible with + # the build system anymore. Suggest Python 3.11 + if rel == 15: + if not self.which(self.python_cmd): + self.recommend_python = True + self.deps.add_package(self.python_cmd, DepManager.SYSTEM_MANDATORY) + + progs.update({ + "python-sphinx": "python311-Sphinx", + "virtualenv": "python311-virtualenv", + "yaml": "python311-PyYAML", + }) + else: + # Tumbleweed defaults to Python 3.11 + + progs.update({ + "python-sphinx": "python313-Sphinx", + "virtualenv": "python313-virtualenv", + "yaml": "python313-PyYAML", + }) + + # FIXME: add support for installing CJK fonts + # + # I tried hard, but was unable to find a way to install + # "Noto Sans CJK SC" on openSUSE + + if self.pdf: + self.check_rpm_missing(suse_tex_pkgs, DepManager.PDF_MANDATORY) + if self.pdf: + self.check_missing_tex() + + return self.get_install_progs(progs, "zypper install --no-recommends") + + def give_mageia_hints(self): + """ + Provide package installation hints for Mageia and OpenMandriva. + """ + progs = { + "Pod::Usage": "perl-Pod-Usage", + "convert": "ImageMagick", + "dot": "graphviz", + "python-sphinx": "python3-sphinx", + "rsvg-convert": "librsvg2", + "virtualenv": "python3-virtualenv", + "xelatex": "texlive", + "yaml": "python3-yaml", + } + + tex_pkgs = [ + "texlive-fontsextra", + ] + + if re.search(r"OpenMandriva", self.system_release): + packager_cmd = "dnf install" + noto_sans = "noto-sans-cjk-fonts" + tex_pkgs = ["texlive-collection-fontsextra"] + + # Tested on OpenMandriva Lx 4.3 + progs["convert"] = "imagemagick" + progs["yaml"] = "python-pyyaml" + + else: + packager_cmd = "urpmi" + noto_sans = "google-noto-sans-cjk-ttc-fonts" + + progs["latexmk"] = "texlive-collection-basic" + + if self.pdf: + pdf_pkgs = [ + "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/TTF/NotoSans-Regular.ttf", + ] + + self.check_missing_file(pdf_pkgs, noto_sans, DepManager.PDF_MANDATORY) + self.check_rpm_missing(tex_pkgs, DepManager.PDF_MANDATORY) + + return self.get_install_progs(progs, packager_cmd) + + def give_arch_linux_hints(self): + """ + Provide package installation hints for ArchLinux. + """ + progs = { + "convert": "imagemagick", + "dot": "graphviz", + "latexmk": "texlive-core", + "rsvg-convert": "extra/librsvg", + "virtualenv": "python-virtualenv", + "xelatex": "texlive-xetex", + "yaml": "python-yaml", + } + + archlinux_tex_pkgs = [ + "texlive-core", + "texlive-latexextra", + "ttf-dejavu", + ] + + if self.pdf: + self.check_pacman_missing(archlinux_tex_pkgs, + DepManager.PDF_MANDATORY) + + self.check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], + "noto-fonts-cjk", + DepManager.PDF_MANDATORY) + + + return self.get_install_progs(progs, "pacman -S") + + def give_gentoo_hints(self): + """ + Provide package installation hints for Gentoo. + """ + progs = { + "convert": "media-gfx/imagemagick", + "dot": "media-gfx/graphviz", + "rsvg-convert": "gnome-base/librsvg", + "virtualenv": "dev-python/virtualenv", + "xelatex": "dev-texlive/texlive-xetex media-fonts/dejavu", + "yaml": "dev-python/pyyaml", + "python-sphinx": "dev-python/sphinx", + } + + if self.pdf: + pdf_pkgs = { + "media-fonts/dejavu": [ + "/usr/share/fonts/dejavu/DejaVuSans.ttf", + ], + "media-fonts/noto-cjk": [ + "/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf", + "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc", + ], + } + for package, files in pdf_pkgs.items(): + self.check_missing_file(files, package, DepManager.PDF_MANDATORY) + + # Handling dependencies is a nightmare, as Gentoo refuses to emerge + # some packages if there's no package.use file describing them. + # To make it worse, compilation flags shall also be present there + # for some packages. If USE is not perfect, error/warning messages + # like those are shown: + # + # !!! The following binary packages have been ignored due to non matching USE: + # + # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_13 qt6 svg + # =media-gfx/graphviz-12.2.1-r1 X pdf python_single_target_python3_12 -python_single_target_python3_13 qt6 svg + # =media-gfx/graphviz-12.2.1-r1 X pdf qt6 svg + # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 qt6 svg + # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 python_single_target_python3_12 -python_single_target_python3_13 qt6 svg + # =media-fonts/noto-cjk-20190416 X + # =app-text/texlive-core-2024-r1 X cjk -xetex + # =app-text/texlive-core-2024-r1 X -xetex + # =app-text/texlive-core-2024-r1 -xetex + # =dev-libs/zziplib-0.13.79-r1 sdl + # + # And will ignore such packages, installing the remaining ones. That + # affects mostly the image extension and PDF generation. + + # Package dependencies and the minimal needed args: + portages = { + "graphviz": "media-gfx/graphviz", + "imagemagick": "media-gfx/imagemagick", + "media-libs": "media-libs/harfbuzz icu", + "media-fonts": "media-fonts/noto-cjk", + "texlive": "app-text/texlive-core xetex", + "zziblib": "dev-libs/zziplib sdl", + } + + extra_cmds = "" + if not self.distro_msg: + self.distro_msg = "Note: Gentoo requires package.use to be adjusted before emerging packages" + + use_base = "/etc/portage/package.use" + files = glob(f"{use_base}/*") + + for fname, portage in portages.items(): + install = False + + while install is False: + if not files: + # No files under package.usage. Install all + install = True + break + + args = portage.split(" ") + + name = args.pop(0) + + cmd = ["grep", "-l", "-E", rf"^{name}\b" ] + files + result = self.run(cmd, stdout=subprocess.PIPE, text=True) + if result.returncode or not result.stdout.strip(): + # File containing portage name not found + install = True + break + + # Ensure that needed USE flags are present + if args: + match_fname = result.stdout.strip() + with open(match_fname, 'r', encoding='utf8', + errors='backslashreplace') as fp: + for line in fp: + for arg in args: + if arg.startswith("-"): + continue + + if not re.search(rf"\s*{arg}\b", line): + # Needed file argument not found + install = True + break + + # Everything looks ok, don't install + break + + # emit a code to setup missing USE + if install: + extra_cmds += (f"sudo su -c 'echo \"{portage}\" > {use_base}/{fname}'\n") + + # Now, we can use emerge and let it respect USE + return self.get_install_progs(progs, + "emerge --ask --changed-use --binpkg-respect-use=y", + extra_cmds) + + def get_install(self): + """ + OS-specific hints logic. Seeks for a hinter. If found, use it to + provide package-manager specific install commands. + + Otherwise, outputs install instructions for the meta-packages. + + Returns a string with the command to be executed to install the + the needed packages, if distro found. Otherwise, return just a + list of packages that require installation. + """ + os_hints = { + re.compile("Red Hat Enterprise Linux"): self.give_redhat_hints, + re.compile("Fedora"): self.give_redhat_hints, + re.compile("AlmaLinux"): self.give_redhat_hints, + re.compile("Amazon Linux"): self.give_redhat_hints, + re.compile("CentOS"): self.give_redhat_hints, + re.compile("openEuler"): self.give_redhat_hints, + re.compile("Oracle Linux Server"): self.give_redhat_hints, + re.compile("Rocky Linux"): self.give_redhat_hints, + re.compile("Springdale Open Enterprise"): self.give_redhat_hints, + + re.compile("Ubuntu"): self.give_debian_hints, + re.compile("Debian"): self.give_debian_hints, + re.compile("Devuan"): self.give_debian_hints, + re.compile("Kali"): self.give_debian_hints, + re.compile("Mint"): self.give_debian_hints, + + re.compile("openSUSE"): self.give_opensuse_hints, + + re.compile("Mageia"): self.give_mageia_hints, + re.compile("OpenMandriva"): self.give_mageia_hints, + + re.compile("Arch Linux"): self.give_arch_linux_hints, + re.compile("Gentoo"): self.give_gentoo_hints, + } + + # If the OS is detected, use per-OS hint logic + for regex, os_hint in os_hints.items(): + if regex.search(self.system_release): + return os_hint() + + # + # Fall-back to generic hint code for other distros + # That's far from ideal, specially for LaTeX dependencies. + # + progs = {"sphinx-build": "sphinx"} + if self.pdf: + self.check_missing_tex() + + self.distro_msg = \ + f"I don't know distro {self.system_release}.\n" \ + "So, I can't provide you a hint with the install procedure.\n" \ + "There are likely missing dependencies.\n" + + return self.get_install_progs(progs, None) + + # + # Common dependencies + # + def deactivate_help(self): + """ + Print a helper message to disable a virtual environment. + """ + + print("\n If you want to exit the virtualenv, you can use:") + print("\tdeactivate") + + def get_virtenv(self): + """ + Give a hint about how to activate an already-existing virtual + environment containing sphinx-build. + + Returns a tuble with (activate_cmd_path, sphinx_version) with + the newest available virtual env. + """ + + cwd = os.getcwd() + + activates = [] + + # Add all sphinx prefixes with possible version numbers + for p in self.virtenv_prefix: + activates += glob(f"{cwd}/{p}[0-9]*/bin/activate") + + activates.sort(reverse=True, key=str.lower) + + # Place sphinx_latest first, if it exists + for p in self.virtenv_prefix: + activates = glob(f"{cwd}/{p}*latest/bin/activate") + activates + + ver = (0, 0, 0) + for f in activates: + # Discard too old Sphinx virtual environments + match = re.search(r"(\d+)\.(\d+)\.(\d+)", f) + if match: + ver = (int(match.group(1)), int(match.group(2)), int(match.group(3))) + + if ver < self.min_version: + continue + + sphinx_cmd = f.replace("activate", "sphinx-build") + if not os.path.isfile(sphinx_cmd): + continue + + ver = self.get_sphinx_version(sphinx_cmd) + + if not ver: + venv_dir = f.replace("/bin/activate", "") + print(f"Warning: virtual environment {venv_dir} is not working.\n" \ + "Python version upgrade? Remove it with:\n\n" \ + "\trm -rf {venv_dir}\n\n") + else: + if self.need_sphinx and ver >= self.min_version: + return (f, ver) + elif parse_version(ver) > self.cur_version: + return (f, ver) + + return ("", ver) + + def recommend_sphinx_upgrade(self): + """ + Check if Sphinx needs to be upgraded. + + Returns a tuple with the higest available Sphinx version if found. + Otherwise, returns None to indicate either that no upgrade is needed + or no venv was found. + """ + + # Avoid running sphinx-builds from venv if cur_version is good + if self.cur_version and self.cur_version >= RECOMMENDED_VERSION: + self.latest_avail_ver = self.cur_version + return None + + # Get the highest version from sphinx_*/bin/sphinx-build and the + # corresponding command to activate the venv/virtenv + self.activate_cmd, self.venv_ver = self.get_virtenv() + + # Store the highest version from Sphinx existing virtualenvs + if self.activate_cmd and self.venv_ver > self.cur_version: + self.latest_avail_ver = self.venv_ver + else: + if self.cur_version: + self.latest_avail_ver = self.cur_version + else: + self.latest_avail_ver = (0, 0, 0) + + # As we don't know package version of Sphinx, and there's no + # virtual environments, don't check if upgrades are needed + if not self.virtualenv: + if not self.latest_avail_ver: + return None + + return self.latest_avail_ver + + # Either there are already a virtual env or a new one should be created + self.need_pip = 1 + + if not self.latest_avail_ver: + return None + + # Return if the reason is due to an upgrade or not + if self.latest_avail_ver != (0, 0, 0): + if self.latest_avail_ver < RECOMMENDED_VERSION: + self.rec_sphinx_upgrade = 1 + + return self.latest_avail_ver + + def recommend_package(self): + """ + Recommend installing Sphinx as a distro-specific package. + """ + + print("\n2) As a package with:") + + old_need = self.deps.need + old_optional = self.deps.optional + + self.pdf = False + self.deps.optional = 0 + old_verbose = self.verbose_warn_install + self.verbose_warn_install = 0 + + self.deps.clear_deps() + + self.deps.add_package("python-sphinx", DepManager.PYTHON_MANDATORY) + + cmd = self.get_install() + if cmd: + print(cmd) + + self.deps.need = old_need + self.deps.optional = old_optional + self.verbose_warn_install = old_verbose + + def recommend_sphinx_version(self, virtualenv_cmd): + """ + Provide recommendations for installing or upgrading Sphinx based + on current version. + + The logic here is complex, as it have to deal with different versions: + + - minimal supported version; + - minimal PDF version; + - recommended version. + + It also needs to work fine with both distro's package and + venv/virtualenv + """ + + if self.recommend_python: + print("\nPython version is incompatible with doc build.\n" \ + "Please upgrade it and re-run.\n") + return + + + # Version is OK. Nothing to do. + if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION: + return + + if self.latest_avail_ver: + latest_avail_ver = ver_str(self.latest_avail_ver) + + if not self.need_sphinx: + # sphinx-build is present and its version is >= $min_version + + # only recommend enabling a newer virtenv version if makes sense. + if self.latest_avail_ver and self.latest_avail_ver > self.cur_version: + print(f"\nYou may also use the newer Sphinx version {latest_avail_ver} with:") + if f"{self.virtenv_prefix}" in os.getcwd(): + print("\tdeactivate") + print(f"\t. {self.activate_cmd}") + self.deactivate_help() + return + + if self.latest_avail_ver and self.latest_avail_ver >= RECOMMENDED_VERSION: + return + + if not self.virtualenv: + # No sphinx either via package or via virtenv. As we can't + # Compare the versions here, just return, recommending the + # user to install it from the package distro. + if not self.latest_avail_ver or self.latest_avail_ver == (0, 0, 0): + return + + # User doesn't want a virtenv recommendation, but he already + # installed one via virtenv with a newer version. + # So, print commands to enable it + if self.latest_avail_ver > self.cur_version: + print(f"\nYou may also use the Sphinx virtualenv version {latest_avail_ver} with:") + if f"{self.virtenv_prefix}" in os.getcwd(): + print("\tdeactivate") + print(f"\t. {self.activate_cmd}") + self.deactivate_help() + return + print("\n") + else: + if self.need_sphinx: + self.deps.need += 1 + + # Suggest newer versions if current ones are too old + if self.latest_avail_ver and self.latest_avail_ver >= self.min_version: + if self.latest_avail_ver >= RECOMMENDED_VERSION: + print(f"\nNeed to activate Sphinx (version {latest_avail_ver}) on virtualenv with:") + print(f"\t. {self.activate_cmd}") + self.deactivate_help() + return + + # Version is above the minimal required one, but may be + # below the recommended one. So, print warnings/notes + if self.latest_avail_ver < RECOMMENDED_VERSION: + print(f"Warning: It is recommended at least Sphinx version {RECOMMENDED_VERSION}.") + + # At this point, either it needs Sphinx or upgrade is recommended, + # both via pip + + if self.rec_sphinx_upgrade: + if not self.virtualenv: + print("Instead of install/upgrade Python Sphinx pkg, you could use pip/pypi with:\n\n") + else: + print("To upgrade Sphinx, use:\n\n") + else: + print("\nSphinx needs to be installed either:\n1) via pip/pypi with:\n") + + if not virtualenv_cmd: + print(" Currently not possible.\n") + print(" Please upgrade Python to a newer version and run this script again") + else: + print(f"\t{virtualenv_cmd} {self.virtenv_dir}") + print(f"\t. {self.virtenv_dir}/bin/activate") + print(f"\tpip install -r {self.requirement_file}") + self.deactivate_help() + + if self.package_supported: + self.recommend_package() + + print("\n" \ + " Please note that Sphinx currentlys produce false-positive\n" \ + " warnings when the same name is used for more than one type (functions,\n" \ + " structs, enums,...). This is known Sphinx bug. For more details, see:\n" \ + "\thttps://github.com/sphinx-doc/sphinx/pull/8313") + + def check_needs(self): + """ + Main method that checks needed dependencies and provides + recommendations. + """ + self.python_cmd = sys.executable + + # Check if Sphinx is already accessible from current environment + self.check_sphinx(self.conf) + + if self.system_release: + print(f"Detected OS: {self.system_release}.") + else: + print("Unknown OS") + if self.cur_version != (0, 0, 0): + ver = ver_str(self.cur_version) + print(f"Sphinx version: {ver}\n") + + # Check the type of virtual env, depending on Python version + virtualenv_cmd = None + + if sys.version_info < MIN_PYTHON_VERSION: + min_ver = ver_str(MIN_PYTHON_VERSION) + print(f"ERROR: at least python {min_ver} is required to build the kernel docs") + self.need_sphinx = 1 + + self.venv_ver = self.recommend_sphinx_upgrade() + + if self.need_pip: + if sys.version_info < MIN_PYTHON_VERSION: + self.need_pip = False + print("Warning: python version is not supported.") + + else: + virtualenv_cmd = f"{self.python_cmd} -m venv" + self.check_python_module("ensurepip") + + # Check for needed programs/tools + self.check_perl_module("Pod::Usage", DepManager.SYSTEM_MANDATORY) + + self.check_program("make", DepManager.SYSTEM_MANDATORY) + self.check_program("gcc", DepManager.SYSTEM_MANDATORY) + + self.check_program("dot", DepManager.SYSTEM_OPTIONAL) + self.check_program("convert", DepManager.SYSTEM_OPTIONAL) + + self.check_python_module("yaml") + + if self.pdf: + self.check_program("xelatex", DepManager.PDF_MANDATORY) + self.check_program("rsvg-convert", DepManager.PDF_MANDATORY) + self.check_program("latexmk", DepManager.PDF_MANDATORY) + + # Do distro-specific checks and output distro-install commands + cmd = self.get_install() + if cmd: + print(cmd) + + # If distro requires some special instructions, print here. + # Please notice that get_install() needs to be called first. + if self.distro_msg: + print("\n" + self.distro_msg) + + if not self.python_cmd: + if self.need == 1: + sys.exit("Can't build as 1 mandatory dependency is missing") + elif self.need: + sys.exit(f"Can't build as {self.need} mandatory dependencies are missing") + + # Check if sphinx-build is called sphinx-build-3 + if self.need_symlink: + sphinx_path = self.which("sphinx-build-3") + if sphinx_path: + print(f"\tsudo ln -sf {sphinx_path} /usr/bin/sphinx-build\n") + + self.recommend_sphinx_version(virtualenv_cmd) + print("") + + if not self.deps.optional: + print("All optional dependencies are met.") + + if self.deps.need == 1: + sys.exit("Can't build as 1 mandatory dependency is missing") + elif self.deps.need: + sys.exit(f"Can't build as {self.deps.need} mandatory dependencies are missing") + + print("Needed package dependencies are met.") + +DESCRIPTION = """ +Process some flags related to Sphinx installation and documentation build. +""" + + +def main(): + """Main function""" + parser = argparse.ArgumentParser(description=DESCRIPTION) + + parser.add_argument( + "--no-virtualenv", + action="store_false", + dest="virtualenv", + help="Recommend installing Sphinx instead of using a virtualenv", + ) + + parser.add_argument( + "--no-pdf", + action="store_false", + dest="pdf", + help="Don't check for dependencies required to build PDF docs", + ) + + parser.add_argument( + "--version-check", + action="store_true", + dest="version_check", + help="If version is compatible, don't check for missing dependencies", + ) + + args = parser.parse_args() + + checker = SphinxDependencyChecker(args) + + checker.check_python() + checker.check_needs() + +# Call main if not used as module +if __name__ == "__main__": + main() diff --git a/scripts/sphinx-pre-install.py b/scripts/sphinx-pre-install.py deleted file mode 100755 index 7dfe5c2a6cc2..000000000000 --- a/scripts/sphinx-pre-install.py +++ /dev/null @@ -1,1565 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (c) 2017-2025 Mauro Carvalho Chehab -# -# pylint: disable=C0103,C0114,C0115,C0116,C0301,C0302 -# pylint: disable=R0902,R0904,R0911,R0912,R0914,R0915,R1705,R1710,E1121 - -# Note: this script requires at least Python 3.6 to run. -# Don't add changes not compatible with it, it is meant to report -# incompatible python versions. - -""" -Dependency checker for Sphinx documentation Kernel build. - -This module provides tools to check for all required dependencies needed to -build documentation using Sphinx, including system packages, Python modules -and LaTeX packages for PDF generation. - -It detect packages for a subset of Linux distributions used by Kernel -maintainers, showing hints and missing dependencies. - -The main class SphinxDependencyChecker handles the dependency checking logic -and provides recommendations for installing missing packages. It supports both -system package installations and Python virtual environments. By default, -system pacage install is recommended. -""" - -import argparse -import os -import re -import subprocess -import sys -from glob import glob - - -def parse_version(version): - """Convert a major.minor.patch version into a tuple""" - return tuple(int(x) for x in version.split(".")) - - -def ver_str(version): - """Returns a version tuple as major.minor.patch""" - - return ".".join([str(x) for x in version]) - - -RECOMMENDED_VERSION = parse_version("3.4.3") -MIN_PYTHON_VERSION = parse_version("3.7") - - -class DepManager: - """ - Manage package dependencies. There are three types of dependencies: - - - System: dependencies required for docs build; - - Python: python dependencies for a native distro Sphinx install; - - PDF: dependencies needed by PDF builds. - - Each dependency can be mandatory or optional. Not installing an optional - dependency won't break the build, but will cause degradation at the - docs output. - """ - - # Internal types of dependencies. Don't use them outside DepManager class. - _SYS_TYPE = 0 - _PHY_TYPE = 1 - _PDF_TYPE = 2 - - # Dependencies visible outside the class. - # The keys are tuple with: (type, is_mandatory flag). - # - # Currently we're not using all optional dep types. Yet, we'll keep all - # possible combinations here. They're not many, and that makes easier - # if later needed and for the name() method below - - SYSTEM_MANDATORY = (_SYS_TYPE, True) - PYTHON_MANDATORY = (_PHY_TYPE, True) - PDF_MANDATORY = (_PDF_TYPE, True) - - SYSTEM_OPTIONAL = (_SYS_TYPE, False) - PYTHON_OPTIONAL = (_PHY_TYPE, False) - PDF_OPTIONAL = (_PDF_TYPE, True) - - def __init__(self, pdf): - """ - Initialize internal vars: - - - missing: missing dependencies list, containing a distro-independent - name for a missing dependency and its type. - - missing_pkg: ancillary dict containing missing dependencies in - distro namespace, organized by type. - - need: total number of needed dependencies. Never cleaned. - - optional: total number of optional dependencies. Never cleaned. - - pdf: Is PDF support enabled? - """ - self.missing = {} - self.missing_pkg = {} - self.need = 0 - self.optional = 0 - self.pdf = pdf - - @staticmethod - def name(dtype): - """ - Ancillary routine to output a warn/error message reporting - missing dependencies. - """ - if dtype[0] == DepManager._SYS_TYPE: - msg = "build" - elif dtype[0] == DepManager._PHY_TYPE: - msg = "Python" - else: - msg = "PDF" - - if dtype[1]: - return f"ERROR: {msg} mandatory deps missing" - else: - return f"Warning: {msg} optional deps missing" - - @staticmethod - def is_optional(dtype): - """Ancillary routine to report if a dependency is optional""" - return not dtype[1] - - @staticmethod - def is_pdf(dtype): - """Ancillary routine to report if a dependency is for PDF generation""" - if dtype[0] == DepManager._PDF_TYPE: - return True - - return False - - def add_package(self, package, dtype): - """ - Add a package at the self.missing() dictionary. - Doesn't update missing_pkg. - """ - is_optional = DepManager.is_optional(dtype) - self.missing[package] = dtype - if is_optional: - self.optional += 1 - else: - self.need += 1 - - def del_package(self, package): - """ - Remove a package at the self.missing() dictionary. - Doesn't update missing_pkg. - """ - if package in self.missing: - del self.missing[package] - - def clear_deps(self): - """ - Clear dependencies without changing needed/optional. - - This is an ackward way to have a separate section to recommend - a package after system main dependencies. - - TODO: rework the logic to prevent needing it. - """ - - self.missing = {} - self.missing_pkg = {} - - def check_missing(self, progs): - """ - Update self.missing_pkg, using progs dict to convert from the - agnostic package name to distro-specific one. - - Returns an string with the packages to be installed, sorted and - with eventual duplicates removed. - """ - - self.missing_pkg = {} - - for prog, dtype in sorted(self.missing.items()): - # At least on some LTS distros like CentOS 7, texlive doesn't - # provide all packages we need. When such distros are - # detected, we have to disable PDF output. - # - # So, we need to ignore the packages that distros would - # need for LaTeX to work - if DepManager.is_pdf(dtype) and not self.pdf: - self.optional -= 1 - continue - - if not dtype in self.missing_pkg: - self.missing_pkg[dtype] = [] - - self.missing_pkg[dtype].append(progs.get(prog, prog)) - - install = [] - for dtype, pkgs in self.missing_pkg.items(): - install += pkgs - - return " ".join(sorted(set(install))) - - def warn_install(self): - """ - Emit warnings/errors related to missing packages. - """ - - output_msg = "" - - for dtype in sorted(self.missing_pkg.keys()): - progs = " ".join(sorted(set(self.missing_pkg[dtype]))) - - try: - name = DepManager.name(dtype) - output_msg += f'{name}:\t{progs}\n' - except KeyError: - raise KeyError(f"ERROR!!!: invalid dtype for {progs}: {dtype}") - - if output_msg: - print(f"\n{output_msg}") - -class AncillaryMethods: - """ - Ancillary methods that checks for missing dependencies for different - types of types, like binaries, python modules, rpm deps, etc. - """ - - @staticmethod - def which(prog): - """ - Our own implementation of which(). We could instead use - shutil.which(), but this function is simple enough. - Probably faster to use this implementation than to import shutil. - """ - for path in os.environ.get("PATH", "").split(":"): - full_path = os.path.join(path, prog) - if os.access(full_path, os.X_OK): - return full_path - - return None - - @staticmethod - def get_python_version(cmd): - """ - Get python version from a Python binary. As we need to detect if - are out there newer python binaries, we can't rely on sys.release here. - """ - - result = SphinxDependencyChecker.run([cmd, "--version"], - capture_output=True, text=True) - version = result.stdout.strip() - - match = re.search(r"(\d+\.\d+\.\d+)", version) - if match: - return parse_version(match.group(1)) - - print(f"Can't parse version {version}") - return (0, 0, 0) - - @staticmethod - def find_python(): - """ - Detect if are out there any python 3.xy version newer than the - current one. - - Note: this routine is limited to up to 2 digits for python3. We - may need to update it one day, hopefully on a distant future. - """ - patterns = [ - "python3.[0-9]", - "python3.[0-9][0-9]", - ] - - # Seek for a python binary newer than MIN_PYTHON_VERSION - for path in os.getenv("PATH", "").split(":"): - for pattern in patterns: - for cmd in glob(os.path.join(path, pattern)): - if os.path.isfile(cmd) and os.access(cmd, os.X_OK): - version = SphinxDependencyChecker.get_python_version(cmd) - if version >= MIN_PYTHON_VERSION: - return cmd - - @staticmethod - def check_python(): - """ - Check if the current python binary satisfies our minimal requirement - for Sphinx build. If not, re-run with a newer version if found. - """ - cur_ver = sys.version_info[:3] - if cur_ver >= MIN_PYTHON_VERSION: - ver = ver_str(cur_ver) - print(f"Python version: {ver}") - - # This could be useful for debugging purposes - if SphinxDependencyChecker.which("docutils"): - result = SphinxDependencyChecker.run(["docutils", "--version"], - capture_output=True, text=True) - ver = result.stdout.strip() - match = re.search(r"(\d+\.\d+\.\d+)", ver) - if match: - ver = match.group(1) - - print(f"Docutils version: {ver}") - - return - - python_ver = ver_str(cur_ver) - - new_python_cmd = SphinxDependencyChecker.find_python() - if not new_python_cmd: - print(f"ERROR: Python version {python_ver} is not spported anymore\n") - print(" Can't find a new version. This script may fail") - return - - # Restart script using the newer version - script_path = os.path.abspath(sys.argv[0]) - args = [new_python_cmd, script_path] + sys.argv[1:] - - print(f"Python {python_ver} not supported. Changing to {new_python_cmd}") - - try: - os.execv(new_python_cmd, args) - except OSError as e: - sys.exit(f"Failed to restart with {new_python_cmd}: {e}") - - @staticmethod - def run(*args, **kwargs): - """ - Excecute a command, hiding its output by default. - Preserve comatibility with older Python versions. - """ - - capture_output = kwargs.pop('capture_output', False) - - if capture_output: - if 'stdout' not in kwargs: - kwargs['stdout'] = subprocess.PIPE - if 'stderr' not in kwargs: - kwargs['stderr'] = subprocess.PIPE - else: - if 'stdout' not in kwargs: - kwargs['stdout'] = subprocess.DEVNULL - if 'stderr' not in kwargs: - kwargs['stderr'] = subprocess.DEVNULL - - # Don't break with older Python versions - if 'text' in kwargs and sys.version_info < (3, 7): - kwargs['universal_newlines'] = kwargs.pop('text') - - return subprocess.run(*args, **kwargs) - -class MissingCheckers(AncillaryMethods): - """ - Contains some ancillary checkers for different types of binaries and - package managers. - """ - - def __init__(self, args, texlive): - """ - Initialize its internal variables - """ - self.pdf = args.pdf - self.virtualenv = args.virtualenv - self.version_check = args.version_check - self.texlive = texlive - - self.min_version = (0, 0, 0) - self.cur_version = (0, 0, 0) - - self.deps = DepManager(self.pdf) - - self.need_symlink = 0 - self.need_sphinx = 0 - - self.verbose_warn_install = 1 - - self.virtenv_dir = "" - self.install = "" - self.python_cmd = "" - - self.virtenv_prefix = ["sphinx_", "Sphinx_" ] - - def check_missing_file(self, files, package, dtype): - """ - Does the file exists? If not, add it to missing dependencies. - """ - for f in files: - if os.path.exists(f): - return - self.deps.add_package(package, dtype) - - def check_program(self, prog, dtype): - """ - Does the program exists and it is at the PATH? - If not, add it to missing dependencies. - """ - found = self.which(prog) - if found: - return found - - self.deps.add_package(prog, dtype) - - return None - - def check_perl_module(self, prog, dtype): - """ - Does perl have a dependency? Is it available? - If not, add it to missing dependencies. - - Right now, we still need Perl for doc build, as it is required - by some tools called at docs or kernel build time, like: - - scripts/documentation-file-ref-check - - Also, checkpatch is on Perl. - """ - - # While testing with lxc download template, one of the - # distros (Oracle) didn't have perl - nor even an option to install - # before installing oraclelinux-release-el9 package. - # - # Check it before running an error. If perl is not there, - # add it as a mandatory package, as some parts of the doc builder - # needs it. - if not self.which("perl"): - self.deps.add_package("perl", DepManager.SYSTEM_MANDATORY) - self.deps.add_package(prog, dtype) - return - - try: - self.run(["perl", f"-M{prog}", "-e", "1"], check=True) - except subprocess.CalledProcessError: - self.deps.add_package(prog, dtype) - - def check_python_module(self, module, is_optional=False): - """ - Does a python module exists outside venv? If not, add it to missing - dependencies. - """ - if is_optional: - dtype = DepManager.PYTHON_OPTIONAL - else: - dtype = DepManager.PYTHON_MANDATORY - - try: - self.run([self.python_cmd, "-c", f"import {module}"], check=True) - except subprocess.CalledProcessError: - self.deps.add_package(module, dtype) - - def check_rpm_missing(self, pkgs, dtype): - """ - Does a rpm package exists? If not, add it to missing dependencies. - """ - for prog in pkgs: - try: - self.run(["rpm", "-q", prog], check=True) - except subprocess.CalledProcessError: - self.deps.add_package(prog, dtype) - - def check_pacman_missing(self, pkgs, dtype): - """ - Does a pacman package exists? If not, add it to missing dependencies. - """ - for prog in pkgs: - try: - self.run(["pacman", "-Q", prog], check=True) - except subprocess.CalledProcessError: - self.deps.add_package(prog, dtype) - - def check_missing_tex(self, is_optional=False): - """ - Does a LaTeX package exists? If not, add it to missing dependencies. - """ - if is_optional: - dtype = DepManager.PDF_OPTIONAL - else: - dtype = DepManager.PDF_MANDATORY - - kpsewhich = self.which("kpsewhich") - for prog, package in self.texlive.items(): - - # If kpsewhich is not there, just add it to deps - if not kpsewhich: - self.deps.add_package(package, dtype) - continue - - # Check if the package is needed - try: - result = self.run( - [kpsewhich, prog], stdout=subprocess.PIPE, text=True, check=True - ) - - # Didn't find. Add it - if not result.stdout.strip(): - self.deps.add_package(package, dtype) - - except subprocess.CalledProcessError: - # kpsewhich returned an error. Add it, just in case - self.deps.add_package(package, dtype) - - def get_sphinx_fname(self): - """ - Gets the binary filename for sphinx-build. - """ - if "SPHINXBUILD" in os.environ: - return os.environ["SPHINXBUILD"] - - fname = "sphinx-build" - if self.which(fname): - return fname - - fname = "sphinx-build-3" - if self.which(fname): - self.need_symlink = 1 - return fname - - return "" - - def get_sphinx_version(self, cmd): - """ - Gets sphinx-build version. - """ - try: - result = self.run([cmd, "--version"], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, check=True) - except (subprocess.CalledProcessError, FileNotFoundError): - return None - - for line in result.stdout.split("\n"): - match = re.match(r"^sphinx-build\s+([\d\.]+)(?:\+(?:/[\da-f]+)|b\d+)?\s*$", line) - if match: - return parse_version(match.group(1)) - - match = re.match(r"^Sphinx.*\s+([\d\.]+)\s*$", line) - if match: - return parse_version(match.group(1)) - - def check_sphinx(self, conf): - """ - Checks Sphinx minimal requirements - """ - try: - with open(conf, "r", encoding="utf-8") as f: - for line in f: - match = re.match(r"^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]", line) - if match: - self.min_version = parse_version(match.group(1)) - break - except IOError: - sys.exit(f"Can't open {conf}") - - if not self.min_version: - sys.exit(f"Can't get needs_sphinx version from {conf}") - - self.virtenv_dir = self.virtenv_prefix[0] + "latest" - - sphinx = self.get_sphinx_fname() - if not sphinx: - self.need_sphinx = 1 - return - - self.cur_version = self.get_sphinx_version(sphinx) - if not self.cur_version: - sys.exit(f"{sphinx} didn't return its version") - - if self.cur_version < self.min_version: - curver = ver_str(self.cur_version) - minver = ver_str(self.min_version) - - print(f"ERROR: Sphinx version is {curver}. It should be >= {minver}") - self.need_sphinx = 1 - return - - # On version check mode, just assume Sphinx has all mandatory deps - if self.version_check and self.cur_version >= RECOMMENDED_VERSION: - sys.exit(0) - - def catcheck(self, filename): - """ - Reads a file if it exists, returning as string. - If not found, returns an empty string. - """ - if os.path.exists(filename): - with open(filename, "r", encoding="utf-8") as f: - return f.read().strip() - return "" - - def get_system_release(self): - """ - Determine the system type. There's no unique way that would work - with all distros with a minimal package install. So, several - methods are used here. - - By default, it will use lsb_release function. If not available, it will - fail back to reading the known different places where the distro name - is stored. - - Several modern distros now have /etc/os-release, which usually have - a decent coverage. - """ - - system_release = "" - - if self.which("lsb_release"): - result = self.run(["lsb_release", "-d"], capture_output=True, text=True) - system_release = result.stdout.replace("Description:", "").strip() - - release_files = [ - "/etc/system-release", - "/etc/redhat-release", - "/etc/lsb-release", - "/etc/gentoo-release", - ] - - if not system_release: - for f in release_files: - system_release = self.catcheck(f) - if system_release: - break - - # This seems more common than LSB these days - if not system_release: - os_var = {} - try: - with open("/etc/os-release", "r", encoding="utf-8") as f: - for line in f: - match = re.match(r"^([\w\d\_]+)=\"?([^\"]*)\"?\n", line) - if match: - os_var[match.group(1)] = match.group(2) - - system_release = os_var.get("NAME", "") - if "VERSION_ID" in os_var: - system_release += " " + os_var["VERSION_ID"] - elif "VERSION" in os_var: - system_release += " " + os_var["VERSION"] - except IOError: - pass - - if not system_release: - system_release = self.catcheck("/etc/issue") - - system_release = system_release.strip() - - return system_release - -class SphinxDependencyChecker(MissingCheckers): - """ - Main class for checking Sphinx documentation build dependencies. - - - Check for missing system packages; - - Check for missing Python modules; - - Check for missing LaTeX packages needed by PDF generation; - - Propose Sphinx install via Python Virtual environment; - - Propose Sphinx install via distro-specific package install. - """ - def __init__(self, args): - """Initialize checker variables""" - - # List of required texlive packages on Fedora and OpenSuse - texlive = { - "amsfonts.sty": "texlive-amsfonts", - "amsmath.sty": "texlive-amsmath", - "amssymb.sty": "texlive-amsfonts", - "amsthm.sty": "texlive-amscls", - "anyfontsize.sty": "texlive-anyfontsize", - "atbegshi.sty": "texlive-oberdiek", - "bm.sty": "texlive-tools", - "capt-of.sty": "texlive-capt-of", - "cmap.sty": "texlive-cmap", - "ctexhook.sty": "texlive-ctex", - "ecrm1000.tfm": "texlive-ec", - "eqparbox.sty": "texlive-eqparbox", - "eu1enc.def": "texlive-euenc", - "fancybox.sty": "texlive-fancybox", - "fancyvrb.sty": "texlive-fancyvrb", - "float.sty": "texlive-float", - "fncychap.sty": "texlive-fncychap", - "footnote.sty": "texlive-mdwtools", - "framed.sty": "texlive-framed", - "luatex85.sty": "texlive-luatex85", - "multirow.sty": "texlive-multirow", - "needspace.sty": "texlive-needspace", - "palatino.sty": "texlive-psnfss", - "parskip.sty": "texlive-parskip", - "polyglossia.sty": "texlive-polyglossia", - "tabulary.sty": "texlive-tabulary", - "threeparttable.sty": "texlive-threeparttable", - "titlesec.sty": "texlive-titlesec", - "ucs.sty": "texlive-ucs", - "upquote.sty": "texlive-upquote", - "wrapfig.sty": "texlive-wrapfig", - } - - super().__init__(args, texlive) - - self.need_pip = 0 - self.rec_sphinx_upgrade = 0 - - self.system_release = self.get_system_release() - self.activate_cmd = "" - - # Some distros may not have a Sphinx shipped package compatible with - # our minimal requirements - self.package_supported = True - - # Recommend a new python version - self.recommend_python = None - - # Certain hints are meant to be shown only once - self.distro_msg = None - - self.latest_avail_ver = (0, 0, 0) - self.venv_ver = (0, 0, 0) - - prefix = os.environ.get("srctree", ".") + "/" - - self.conf = prefix + "Documentation/conf.py" - self.requirement_file = prefix + "Documentation/sphinx/requirements.txt" - - def get_install_progs(self, progs, cmd, extra=None): - """ - Check for missing dependencies using the provided program mapping. - - The actual distro-specific programs are mapped via progs argument. - """ - install = self.deps.check_missing(progs) - - if self.verbose_warn_install: - self.deps.warn_install() - - if not install: - return - - if cmd: - if self.verbose_warn_install: - msg = "You should run:" - else: - msg = "" - - if extra: - msg += "\n\t" + extra.replace("\n", "\n\t") - - return(msg + "\n\tsudo " + cmd + " " + install) - - return None - - # - # Distro-specific hints methods - # - - def give_debian_hints(self): - """ - Provide package installation hints for Debian-based distros. - """ - progs = { - "Pod::Usage": "perl-modules", - "convert": "imagemagick", - "dot": "graphviz", - "ensurepip": "python3-venv", - "python-sphinx": "python3-sphinx", - "rsvg-convert": "librsvg2-bin", - "virtualenv": "virtualenv", - "xelatex": "texlive-xetex", - "yaml": "python3-yaml", - } - - if self.pdf: - pdf_pkgs = { - "texlive-lang-chinese": [ - "/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty", - ], - "fonts-dejavu": [ - "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", - ], - "fonts-noto-cjk": [ - "/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc", - ], - } - - for package, files in pdf_pkgs.items(): - self.check_missing_file(files, package, DepManager.PDF_MANDATORY) - - self.check_program("dvipng", DepManager.PDF_MANDATORY) - - return self.get_install_progs(progs, "apt-get install") - - def give_redhat_hints(self): - """ - Provide package installation hints for RedHat-based distros - (Fedora, RHEL and RHEL-based variants). - """ - progs = { - "Pod::Usage": "perl-Pod-Usage", - "convert": "ImageMagick", - "dot": "graphviz", - "python-sphinx": "python3-sphinx", - "rsvg-convert": "librsvg2-tools", - "virtualenv": "python3-virtualenv", - "xelatex": "texlive-xetex-bin", - "yaml": "python3-pyyaml", - } - - fedora_tex_pkgs = [ - "dejavu-sans-fonts", - "dejavu-sans-mono-fonts", - "dejavu-serif-fonts", - "texlive-collection-fontsrecommended", - "texlive-collection-latex", - "texlive-xecjk", - ] - - fedora = False - rel = None - - match = re.search(r"(release|Linux)\s+(\d+)", self.system_release) - if match: - rel = int(match.group(2)) - - if not rel: - print("Couldn't identify release number") - noto_sans_redhat = None - self.pdf = False - elif re.search("Fedora", self.system_release): - # Fedora 38 and upper use this CJK font - - noto_sans_redhat = "google-noto-sans-cjk-fonts" - fedora = True - else: - # Almalinux, CentOS, RHEL, ... - - # at least up to version 9 (and Fedora < 38), that's the CJK font - noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts" - - progs["virtualenv"] = "python-virtualenv" - - if not rel or rel < 8: - print("ERROR: Distro not supported. Too old?") - return - - # RHEL 8 uses Python 3.6, which is not compatible with - # the build system anymore. Suggest Python 3.11 - if rel == 8: - self.deps.add_package("python39", DepManager.SYSTEM_MANDATORY) - self.recommend_python = True - - if not self.distro_msg: - self.distro_msg = \ - "Note: RHEL-based distros typically require extra repositories.\n" \ - "For most, enabling epel and crb are enough:\n" \ - "\tsudo dnf install -y epel-release\n" \ - "\tsudo dnf config-manager --set-enabled crb\n" \ - "Yet, some may have other required repositories. Those commands could be useful:\n" \ - "\tsudo dnf repolist all\n" \ - "\tsudo dnf repoquery --available --info \n" \ - "\tsudo dnf config-manager --set-enabled '*' # enable all - probably not what you want" - - if self.pdf: - pdf_pkgs = [ - "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc", - ] - - self.check_missing_file(pdf_pkgs, noto_sans_redhat, DepManager.PDF_MANDATORY) - - self.check_rpm_missing(fedora_tex_pkgs, DepManager.PDF_MANDATORY) - - self.check_missing_tex(DepManager.PDF_MANDATORY) - - # There's no texlive-ctex on RHEL 8 repositories. This will - # likely affect CJK pdf build only. - if not fedora and rel == 8: - self.deps.del_package("texlive-ctex") - - return self.get_install_progs(progs, "dnf install") - - def give_opensuse_hints(self): - """ - Provide package installation hints for openSUSE-based distros - (Leap and Tumbleweed). - """ - progs = { - "Pod::Usage": "perl-Pod-Usage", - "convert": "ImageMagick", - "dot": "graphviz", - "python-sphinx": "python3-sphinx", - "virtualenv": "python3-virtualenv", - "xelatex": "texlive-xetex-bin", - "yaml": "python3-pyyaml", - } - - suse_tex_pkgs = [ - "texlive-babel-english", - "texlive-caption", - "texlive-colortbl", - "texlive-courier", - "texlive-dvips", - "texlive-helvetic", - "texlive-makeindex", - "texlive-metafont", - "texlive-metapost", - "texlive-palatino", - "texlive-preview", - "texlive-times", - "texlive-zapfchan", - "texlive-zapfding", - ] - - progs["latexmk"] = "texlive-latexmk-bin" - - match = re.search(r"(Leap)\s+(\d+).(\d)", self.system_release) - if match: - rel = int(match.group(2)) - - # Leap 15.x uses Python 3.6, which is not compatible with - # the build system anymore. Suggest Python 3.11 - if rel == 15: - if not self.which(self.python_cmd): - self.recommend_python = True - self.deps.add_package(self.python_cmd, DepManager.SYSTEM_MANDATORY) - - progs.update({ - "python-sphinx": "python311-Sphinx", - "virtualenv": "python311-virtualenv", - "yaml": "python311-PyYAML", - }) - else: - # Tumbleweed defaults to Python 3.11 - - progs.update({ - "python-sphinx": "python313-Sphinx", - "virtualenv": "python313-virtualenv", - "yaml": "python313-PyYAML", - }) - - # FIXME: add support for installing CJK fonts - # - # I tried hard, but was unable to find a way to install - # "Noto Sans CJK SC" on openSUSE - - if self.pdf: - self.check_rpm_missing(suse_tex_pkgs, DepManager.PDF_MANDATORY) - if self.pdf: - self.check_missing_tex() - - return self.get_install_progs(progs, "zypper install --no-recommends") - - def give_mageia_hints(self): - """ - Provide package installation hints for Mageia and OpenMandriva. - """ - progs = { - "Pod::Usage": "perl-Pod-Usage", - "convert": "ImageMagick", - "dot": "graphviz", - "python-sphinx": "python3-sphinx", - "rsvg-convert": "librsvg2", - "virtualenv": "python3-virtualenv", - "xelatex": "texlive", - "yaml": "python3-yaml", - } - - tex_pkgs = [ - "texlive-fontsextra", - ] - - if re.search(r"OpenMandriva", self.system_release): - packager_cmd = "dnf install" - noto_sans = "noto-sans-cjk-fonts" - tex_pkgs = ["texlive-collection-fontsextra"] - - # Tested on OpenMandriva Lx 4.3 - progs["convert"] = "imagemagick" - progs["yaml"] = "python-pyyaml" - - else: - packager_cmd = "urpmi" - noto_sans = "google-noto-sans-cjk-ttc-fonts" - - progs["latexmk"] = "texlive-collection-basic" - - if self.pdf: - pdf_pkgs = [ - "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/TTF/NotoSans-Regular.ttf", - ] - - self.check_missing_file(pdf_pkgs, noto_sans, DepManager.PDF_MANDATORY) - self.check_rpm_missing(tex_pkgs, DepManager.PDF_MANDATORY) - - return self.get_install_progs(progs, packager_cmd) - - def give_arch_linux_hints(self): - """ - Provide package installation hints for ArchLinux. - """ - progs = { - "convert": "imagemagick", - "dot": "graphviz", - "latexmk": "texlive-core", - "rsvg-convert": "extra/librsvg", - "virtualenv": "python-virtualenv", - "xelatex": "texlive-xetex", - "yaml": "python-yaml", - } - - archlinux_tex_pkgs = [ - "texlive-core", - "texlive-latexextra", - "ttf-dejavu", - ] - - if self.pdf: - self.check_pacman_missing(archlinux_tex_pkgs, - DepManager.PDF_MANDATORY) - - self.check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], - "noto-fonts-cjk", - DepManager.PDF_MANDATORY) - - - return self.get_install_progs(progs, "pacman -S") - - def give_gentoo_hints(self): - """ - Provide package installation hints for Gentoo. - """ - progs = { - "convert": "media-gfx/imagemagick", - "dot": "media-gfx/graphviz", - "rsvg-convert": "gnome-base/librsvg", - "virtualenv": "dev-python/virtualenv", - "xelatex": "dev-texlive/texlive-xetex media-fonts/dejavu", - "yaml": "dev-python/pyyaml", - "python-sphinx": "dev-python/sphinx", - } - - if self.pdf: - pdf_pkgs = { - "media-fonts/dejavu": [ - "/usr/share/fonts/dejavu/DejaVuSans.ttf", - ], - "media-fonts/noto-cjk": [ - "/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf", - "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc", - ], - } - for package, files in pdf_pkgs.items(): - self.check_missing_file(files, package, DepManager.PDF_MANDATORY) - - # Handling dependencies is a nightmare, as Gentoo refuses to emerge - # some packages if there's no package.use file describing them. - # To make it worse, compilation flags shall also be present there - # for some packages. If USE is not perfect, error/warning messages - # like those are shown: - # - # !!! The following binary packages have been ignored due to non matching USE: - # - # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_13 qt6 svg - # =media-gfx/graphviz-12.2.1-r1 X pdf python_single_target_python3_12 -python_single_target_python3_13 qt6 svg - # =media-gfx/graphviz-12.2.1-r1 X pdf qt6 svg - # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 qt6 svg - # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 python_single_target_python3_12 -python_single_target_python3_13 qt6 svg - # =media-fonts/noto-cjk-20190416 X - # =app-text/texlive-core-2024-r1 X cjk -xetex - # =app-text/texlive-core-2024-r1 X -xetex - # =app-text/texlive-core-2024-r1 -xetex - # =dev-libs/zziplib-0.13.79-r1 sdl - # - # And will ignore such packages, installing the remaining ones. That - # affects mostly the image extension and PDF generation. - - # Package dependencies and the minimal needed args: - portages = { - "graphviz": "media-gfx/graphviz", - "imagemagick": "media-gfx/imagemagick", - "media-libs": "media-libs/harfbuzz icu", - "media-fonts": "media-fonts/noto-cjk", - "texlive": "app-text/texlive-core xetex", - "zziblib": "dev-libs/zziplib sdl", - } - - extra_cmds = "" - if not self.distro_msg: - self.distro_msg = "Note: Gentoo requires package.use to be adjusted before emerging packages" - - use_base = "/etc/portage/package.use" - files = glob(f"{use_base}/*") - - for fname, portage in portages.items(): - install = False - - while install is False: - if not files: - # No files under package.usage. Install all - install = True - break - - args = portage.split(" ") - - name = args.pop(0) - - cmd = ["grep", "-l", "-E", rf"^{name}\b" ] + files - result = self.run(cmd, stdout=subprocess.PIPE, text=True) - if result.returncode or not result.stdout.strip(): - # File containing portage name not found - install = True - break - - # Ensure that needed USE flags are present - if args: - match_fname = result.stdout.strip() - with open(match_fname, 'r', encoding='utf8', - errors='backslashreplace') as fp: - for line in fp: - for arg in args: - if arg.startswith("-"): - continue - - if not re.search(rf"\s*{arg}\b", line): - # Needed file argument not found - install = True - break - - # Everything looks ok, don't install - break - - # emit a code to setup missing USE - if install: - extra_cmds += (f"sudo su -c 'echo \"{portage}\" > {use_base}/{fname}'\n") - - # Now, we can use emerge and let it respect USE - return self.get_install_progs(progs, - "emerge --ask --changed-use --binpkg-respect-use=y", - extra_cmds) - - def get_install(self): - """ - OS-specific hints logic. Seeks for a hinter. If found, use it to - provide package-manager specific install commands. - - Otherwise, outputs install instructions for the meta-packages. - - Returns a string with the command to be executed to install the - the needed packages, if distro found. Otherwise, return just a - list of packages that require installation. - """ - os_hints = { - re.compile("Red Hat Enterprise Linux"): self.give_redhat_hints, - re.compile("Fedora"): self.give_redhat_hints, - re.compile("AlmaLinux"): self.give_redhat_hints, - re.compile("Amazon Linux"): self.give_redhat_hints, - re.compile("CentOS"): self.give_redhat_hints, - re.compile("openEuler"): self.give_redhat_hints, - re.compile("Oracle Linux Server"): self.give_redhat_hints, - re.compile("Rocky Linux"): self.give_redhat_hints, - re.compile("Springdale Open Enterprise"): self.give_redhat_hints, - - re.compile("Ubuntu"): self.give_debian_hints, - re.compile("Debian"): self.give_debian_hints, - re.compile("Devuan"): self.give_debian_hints, - re.compile("Kali"): self.give_debian_hints, - re.compile("Mint"): self.give_debian_hints, - - re.compile("openSUSE"): self.give_opensuse_hints, - - re.compile("Mageia"): self.give_mageia_hints, - re.compile("OpenMandriva"): self.give_mageia_hints, - - re.compile("Arch Linux"): self.give_arch_linux_hints, - re.compile("Gentoo"): self.give_gentoo_hints, - } - - # If the OS is detected, use per-OS hint logic - for regex, os_hint in os_hints.items(): - if regex.search(self.system_release): - return os_hint() - - # - # Fall-back to generic hint code for other distros - # That's far from ideal, specially for LaTeX dependencies. - # - progs = {"sphinx-build": "sphinx"} - if self.pdf: - self.check_missing_tex() - - self.distro_msg = \ - f"I don't know distro {self.system_release}.\n" \ - "So, I can't provide you a hint with the install procedure.\n" \ - "There are likely missing dependencies.\n" - - return self.get_install_progs(progs, None) - - # - # Common dependencies - # - def deactivate_help(self): - """ - Print a helper message to disable a virtual environment. - """ - - print("\n If you want to exit the virtualenv, you can use:") - print("\tdeactivate") - - def get_virtenv(self): - """ - Give a hint about how to activate an already-existing virtual - environment containing sphinx-build. - - Returns a tuble with (activate_cmd_path, sphinx_version) with - the newest available virtual env. - """ - - cwd = os.getcwd() - - activates = [] - - # Add all sphinx prefixes with possible version numbers - for p in self.virtenv_prefix: - activates += glob(f"{cwd}/{p}[0-9]*/bin/activate") - - activates.sort(reverse=True, key=str.lower) - - # Place sphinx_latest first, if it exists - for p in self.virtenv_prefix: - activates = glob(f"{cwd}/{p}*latest/bin/activate") + activates - - ver = (0, 0, 0) - for f in activates: - # Discard too old Sphinx virtual environments - match = re.search(r"(\d+)\.(\d+)\.(\d+)", f) - if match: - ver = (int(match.group(1)), int(match.group(2)), int(match.group(3))) - - if ver < self.min_version: - continue - - sphinx_cmd = f.replace("activate", "sphinx-build") - if not os.path.isfile(sphinx_cmd): - continue - - ver = self.get_sphinx_version(sphinx_cmd) - - if not ver: - venv_dir = f.replace("/bin/activate", "") - print(f"Warning: virtual environment {venv_dir} is not working.\n" \ - "Python version upgrade? Remove it with:\n\n" \ - "\trm -rf {venv_dir}\n\n") - else: - if self.need_sphinx and ver >= self.min_version: - return (f, ver) - elif parse_version(ver) > self.cur_version: - return (f, ver) - - return ("", ver) - - def recommend_sphinx_upgrade(self): - """ - Check if Sphinx needs to be upgraded. - - Returns a tuple with the higest available Sphinx version if found. - Otherwise, returns None to indicate either that no upgrade is needed - or no venv was found. - """ - - # Avoid running sphinx-builds from venv if cur_version is good - if self.cur_version and self.cur_version >= RECOMMENDED_VERSION: - self.latest_avail_ver = self.cur_version - return None - - # Get the highest version from sphinx_*/bin/sphinx-build and the - # corresponding command to activate the venv/virtenv - self.activate_cmd, self.venv_ver = self.get_virtenv() - - # Store the highest version from Sphinx existing virtualenvs - if self.activate_cmd and self.venv_ver > self.cur_version: - self.latest_avail_ver = self.venv_ver - else: - if self.cur_version: - self.latest_avail_ver = self.cur_version - else: - self.latest_avail_ver = (0, 0, 0) - - # As we don't know package version of Sphinx, and there's no - # virtual environments, don't check if upgrades are needed - if not self.virtualenv: - if not self.latest_avail_ver: - return None - - return self.latest_avail_ver - - # Either there are already a virtual env or a new one should be created - self.need_pip = 1 - - if not self.latest_avail_ver: - return None - - # Return if the reason is due to an upgrade or not - if self.latest_avail_ver != (0, 0, 0): - if self.latest_avail_ver < RECOMMENDED_VERSION: - self.rec_sphinx_upgrade = 1 - - return self.latest_avail_ver - - def recommend_package(self): - """ - Recommend installing Sphinx as a distro-specific package. - """ - - print("\n2) As a package with:") - - old_need = self.deps.need - old_optional = self.deps.optional - - self.pdf = False - self.deps.optional = 0 - old_verbose = self.verbose_warn_install - self.verbose_warn_install = 0 - - self.deps.clear_deps() - - self.deps.add_package("python-sphinx", DepManager.PYTHON_MANDATORY) - - cmd = self.get_install() - if cmd: - print(cmd) - - self.deps.need = old_need - self.deps.optional = old_optional - self.verbose_warn_install = old_verbose - - def recommend_sphinx_version(self, virtualenv_cmd): - """ - Provide recommendations for installing or upgrading Sphinx based - on current version. - - The logic here is complex, as it have to deal with different versions: - - - minimal supported version; - - minimal PDF version; - - recommended version. - - It also needs to work fine with both distro's package and - venv/virtualenv - """ - - if self.recommend_python: - print("\nPython version is incompatible with doc build.\n" \ - "Please upgrade it and re-run.\n") - return - - - # Version is OK. Nothing to do. - if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION: - return - - if self.latest_avail_ver: - latest_avail_ver = ver_str(self.latest_avail_ver) - - if not self.need_sphinx: - # sphinx-build is present and its version is >= $min_version - - # only recommend enabling a newer virtenv version if makes sense. - if self.latest_avail_ver and self.latest_avail_ver > self.cur_version: - print(f"\nYou may also use the newer Sphinx version {latest_avail_ver} with:") - if f"{self.virtenv_prefix}" in os.getcwd(): - print("\tdeactivate") - print(f"\t. {self.activate_cmd}") - self.deactivate_help() - return - - if self.latest_avail_ver and self.latest_avail_ver >= RECOMMENDED_VERSION: - return - - if not self.virtualenv: - # No sphinx either via package or via virtenv. As we can't - # Compare the versions here, just return, recommending the - # user to install it from the package distro. - if not self.latest_avail_ver or self.latest_avail_ver == (0, 0, 0): - return - - # User doesn't want a virtenv recommendation, but he already - # installed one via virtenv with a newer version. - # So, print commands to enable it - if self.latest_avail_ver > self.cur_version: - print(f"\nYou may also use the Sphinx virtualenv version {latest_avail_ver} with:") - if f"{self.virtenv_prefix}" in os.getcwd(): - print("\tdeactivate") - print(f"\t. {self.activate_cmd}") - self.deactivate_help() - return - print("\n") - else: - if self.need_sphinx: - self.deps.need += 1 - - # Suggest newer versions if current ones are too old - if self.latest_avail_ver and self.latest_avail_ver >= self.min_version: - if self.latest_avail_ver >= RECOMMENDED_VERSION: - print(f"\nNeed to activate Sphinx (version {latest_avail_ver}) on virtualenv with:") - print(f"\t. {self.activate_cmd}") - self.deactivate_help() - return - - # Version is above the minimal required one, but may be - # below the recommended one. So, print warnings/notes - if self.latest_avail_ver < RECOMMENDED_VERSION: - print(f"Warning: It is recommended at least Sphinx version {RECOMMENDED_VERSION}.") - - # At this point, either it needs Sphinx or upgrade is recommended, - # both via pip - - if self.rec_sphinx_upgrade: - if not self.virtualenv: - print("Instead of install/upgrade Python Sphinx pkg, you could use pip/pypi with:\n\n") - else: - print("To upgrade Sphinx, use:\n\n") - else: - print("\nSphinx needs to be installed either:\n1) via pip/pypi with:\n") - - if not virtualenv_cmd: - print(" Currently not possible.\n") - print(" Please upgrade Python to a newer version and run this script again") - else: - print(f"\t{virtualenv_cmd} {self.virtenv_dir}") - print(f"\t. {self.virtenv_dir}/bin/activate") - print(f"\tpip install -r {self.requirement_file}") - self.deactivate_help() - - if self.package_supported: - self.recommend_package() - - print("\n" \ - " Please note that Sphinx currentlys produce false-positive\n" \ - " warnings when the same name is used for more than one type (functions,\n" \ - " structs, enums,...). This is known Sphinx bug. For more details, see:\n" \ - "\thttps://github.com/sphinx-doc/sphinx/pull/8313") - - def check_needs(self): - """ - Main method that checks needed dependencies and provides - recommendations. - """ - self.python_cmd = sys.executable - - # Check if Sphinx is already accessible from current environment - self.check_sphinx(self.conf) - - if self.system_release: - print(f"Detected OS: {self.system_release}.") - else: - print("Unknown OS") - if self.cur_version != (0, 0, 0): - ver = ver_str(self.cur_version) - print(f"Sphinx version: {ver}\n") - - # Check the type of virtual env, depending on Python version - virtualenv_cmd = None - - if sys.version_info < MIN_PYTHON_VERSION: - min_ver = ver_str(MIN_PYTHON_VERSION) - print(f"ERROR: at least python {min_ver} is required to build the kernel docs") - self.need_sphinx = 1 - - self.venv_ver = self.recommend_sphinx_upgrade() - - if self.need_pip: - if sys.version_info < MIN_PYTHON_VERSION: - self.need_pip = False - print("Warning: python version is not supported.") - - else: - virtualenv_cmd = f"{self.python_cmd} -m venv" - self.check_python_module("ensurepip") - - # Check for needed programs/tools - self.check_perl_module("Pod::Usage", DepManager.SYSTEM_MANDATORY) - - self.check_program("make", DepManager.SYSTEM_MANDATORY) - self.check_program("gcc", DepManager.SYSTEM_MANDATORY) - - self.check_program("dot", DepManager.SYSTEM_OPTIONAL) - self.check_program("convert", DepManager.SYSTEM_OPTIONAL) - - self.check_python_module("yaml") - - if self.pdf: - self.check_program("xelatex", DepManager.PDF_MANDATORY) - self.check_program("rsvg-convert", DepManager.PDF_MANDATORY) - self.check_program("latexmk", DepManager.PDF_MANDATORY) - - # Do distro-specific checks and output distro-install commands - cmd = self.get_install() - if cmd: - print(cmd) - - # If distro requires some special instructions, print here. - # Please notice that get_install() needs to be called first. - if self.distro_msg: - print("\n" + self.distro_msg) - - if not self.python_cmd: - if self.need == 1: - sys.exit("Can't build as 1 mandatory dependency is missing") - elif self.need: - sys.exit(f"Can't build as {self.need} mandatory dependencies are missing") - - # Check if sphinx-build is called sphinx-build-3 - if self.need_symlink: - sphinx_path = self.which("sphinx-build-3") - if sphinx_path: - print(f"\tsudo ln -sf {sphinx_path} /usr/bin/sphinx-build\n") - - self.recommend_sphinx_version(virtualenv_cmd) - print("") - - if not self.deps.optional: - print("All optional dependencies are met.") - - if self.deps.need == 1: - sys.exit("Can't build as 1 mandatory dependency is missing") - elif self.deps.need: - sys.exit(f"Can't build as {self.deps.need} mandatory dependencies are missing") - - print("Needed package dependencies are met.") - -DESCRIPTION = """ -Process some flags related to Sphinx installation and documentation build. -""" - - -def main(): - """Main function""" - parser = argparse.ArgumentParser(description=DESCRIPTION) - - parser.add_argument( - "--no-virtualenv", - action="store_false", - dest="virtualenv", - help="Recommend installing Sphinx instead of using a virtualenv", - ) - - parser.add_argument( - "--no-pdf", - action="store_false", - dest="pdf", - help="Don't check for dependencies required to build PDF docs", - ) - - parser.add_argument( - "--version-check", - action="store_true", - dest="version_check", - help="If version is compatible, don't check for missing dependencies", - ) - - args = parser.parse_args() - - checker = SphinxDependencyChecker(args) - - checker.check_python() - checker.check_needs() - -# Call main if not used as module -if __name__ == "__main__": - main() -- cgit v1.2.3 From 29e71d96837dd49ab2f35f3524a1ceda0ff21ea7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:52 +0200 Subject: scripts: sphinx-pre-install.pl: get rid of the old script All features were ported to the Python version. Plus, it supports more variants and contain fixes. So, drop the old version. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/6900872e6b89b7ff304e70f5d1c23cbb3c757d28.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install.pl | 1056 ----------------------------------------- 1 file changed, 1056 deletions(-) delete mode 100755 scripts/sphinx-pre-install.pl (limited to 'scripts') diff --git a/scripts/sphinx-pre-install.pl b/scripts/sphinx-pre-install.pl deleted file mode 100755 index 07234d482fa8..000000000000 --- a/scripts/sphinx-pre-install.pl +++ /dev/null @@ -1,1056 +0,0 @@ -#!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0-or-later -use strict; - -# Copyright (c) 2017-2020 Mauro Carvalho Chehab -# - -my $prefix = "./"; -$prefix = "$ENV{'srctree'}/" if ($ENV{'srctree'}); - -my $conf = $prefix . "Documentation/conf.py"; -my $requirement_file = $prefix . "Documentation/sphinx/requirements.txt"; -my $virtenv_prefix = "sphinx_"; - -# -# Static vars -# - -my %missing; -my $system_release; -my $need = 0; -my $optional = 0; -my $need_symlink = 0; -my $need_sphinx = 0; -my $need_pip = 0; -my $need_virtualenv = 0; -my $rec_sphinx_upgrade = 0; -my $verbose_warn_install = 1; -my $install = ""; -my $virtenv_dir = ""; -my $python_cmd = ""; -my $activate_cmd; -my $min_version; -my $cur_version; -my $rec_version = "3.4.3"; -my $latest_avail_ver; - -# -# Command line arguments -# - -my $pdf = 1; -my $virtualenv = 1; -my $version_check = 0; - -# -# List of required texlive packages on Fedora and OpenSuse -# - -my %texlive = ( - 'amsfonts.sty' => 'texlive-amsfonts', - 'amsmath.sty' => 'texlive-amsmath', - 'amssymb.sty' => 'texlive-amsfonts', - 'amsthm.sty' => 'texlive-amscls', - 'anyfontsize.sty' => 'texlive-anyfontsize', - 'atbegshi.sty' => 'texlive-oberdiek', - 'bm.sty' => 'texlive-tools', - 'capt-of.sty' => 'texlive-capt-of', - 'cmap.sty' => 'texlive-cmap', - 'ecrm1000.tfm' => 'texlive-ec', - 'eqparbox.sty' => 'texlive-eqparbox', - 'eu1enc.def' => 'texlive-euenc', - 'fancybox.sty' => 'texlive-fancybox', - 'fancyvrb.sty' => 'texlive-fancyvrb', - 'float.sty' => 'texlive-float', - 'fncychap.sty' => 'texlive-fncychap', - 'footnote.sty' => 'texlive-mdwtools', - 'framed.sty' => 'texlive-framed', - 'luatex85.sty' => 'texlive-luatex85', - 'multirow.sty' => 'texlive-multirow', - 'needspace.sty' => 'texlive-needspace', - 'palatino.sty' => 'texlive-psnfss', - 'parskip.sty' => 'texlive-parskip', - 'polyglossia.sty' => 'texlive-polyglossia', - 'tabulary.sty' => 'texlive-tabulary', - 'threeparttable.sty' => 'texlive-threeparttable', - 'titlesec.sty' => 'texlive-titlesec', - 'ucs.sty' => 'texlive-ucs', - 'upquote.sty' => 'texlive-upquote', - 'wrapfig.sty' => 'texlive-wrapfig', - 'ctexhook.sty' => 'texlive-ctex', -); - -# -# Subroutines that checks if a feature exists -# - -sub check_missing(%) -{ - my %map = %{$_[0]}; - - foreach my $prog (sort keys %missing) { - my $is_optional = $missing{$prog}; - - # At least on some LTS distros like CentOS 7, texlive doesn't - # provide all packages we need. When such distros are - # detected, we have to disable PDF output. - # - # So, we need to ignore the packages that distros would - # need for LaTeX to work - if ($is_optional == 2 && !$pdf) { - $optional--; - next; - } - - if ($verbose_warn_install) { - if ($is_optional) { - print "Warning: better to also install \"$prog\".\n"; - } else { - print "ERROR: please install \"$prog\", otherwise, build won't work.\n"; - } - } - if (defined($map{$prog})) { - $install .= " " . $map{$prog}; - } else { - $install .= " " . $prog; - } - } - - $install =~ s/^\s//; -} - -sub add_package($$) -{ - my $package = shift; - my $is_optional = shift; - - $missing{$package} = $is_optional; - if ($is_optional) { - $optional++; - } else { - $need++; - } -} - -sub check_missing_file($$$) -{ - my $files = shift; - my $package = shift; - my $is_optional = shift; - - for (@$files) { - return if(-e $_); - } - - add_package($package, $is_optional); -} - -sub findprog($) -{ - foreach(split(/:/, $ENV{PATH})) { - return "$_/$_[0]" if(-x "$_/$_[0]"); - } -} - -sub find_python_no_venv() -{ - my $prog = shift; - - my $cur_dir = qx(pwd); - $cur_dir =~ s/\s+$//; - - foreach my $dir (split(/:/, $ENV{PATH})) { - next if ($dir =~ m,($cur_dir)/sphinx,); - return "$dir/python3" if(-x "$dir/python3"); - } - foreach my $dir (split(/:/, $ENV{PATH})) { - next if ($dir =~ m,($cur_dir)/sphinx,); - return "$dir/python" if(-x "$dir/python"); - } - return "python"; -} - -sub check_program($$) -{ - my $prog = shift; - my $is_optional = shift; - - return $prog if findprog($prog); - - add_package($prog, $is_optional); -} - -sub check_perl_module($$) -{ - my $prog = shift; - my $is_optional = shift; - - my $err = system("perl -M$prog -e 1 2>/dev/null /dev/null"); - return if ($err == 0); - - add_package($prog, $is_optional); -} - -sub check_python_module($$) -{ - my $prog = shift; - my $is_optional = shift; - - return if (!$python_cmd); - - my $err = system("$python_cmd -c 'import $prog' 2>/dev/null /dev/null"); - return if ($err == 0); - - add_package($prog, $is_optional); -} - -sub check_rpm_missing($$) -{ - my @pkgs = @{$_[0]}; - my $is_optional = $_[1]; - - foreach my $prog(@pkgs) { - my $err = system("rpm -q '$prog' 2>/dev/null >/dev/null"); - add_package($prog, $is_optional) if ($err); - } -} - -sub check_pacman_missing($$) -{ - my @pkgs = @{$_[0]}; - my $is_optional = $_[1]; - - foreach my $prog(@pkgs) { - my $err = system("pacman -Q '$prog' 2>/dev/null >/dev/null"); - add_package($prog, $is_optional) if ($err); - } -} - -sub check_missing_tex($) -{ - my $is_optional = shift; - my $kpsewhich = findprog("kpsewhich"); - - foreach my $prog(keys %texlive) { - my $package = $texlive{$prog}; - if (!$kpsewhich) { - add_package($package, $is_optional); - next; - } - my $file = qx($kpsewhich $prog); - add_package($package, $is_optional) if ($file =~ /^\s*$/); - } -} - -sub get_sphinx_fname() -{ - if ($ENV{'SPHINXBUILD'}) { - return $ENV{'SPHINXBUILD'}; - } - - my $fname = "sphinx-build"; - return $fname if findprog($fname); - - $fname = "sphinx-build-3"; - if (findprog($fname)) { - $need_symlink = 1; - return $fname; - } - - return ""; -} - -sub get_sphinx_version($) -{ - my $cmd = shift; - my $ver; - - open IN, "$cmd --version 2>&1 |"; - while () { - if (m/^\s*sphinx-build\s+([\d\.]+)((\+\/[\da-f]+)|(b\d+))?$/) { - $ver=$1; - last; - } - # Sphinx 1.2.x uses a different format - if (m/^\s*Sphinx.*\s+([\d\.]+)$/) { - $ver=$1; - last; - } - } - close IN; - return $ver; -} - -sub check_sphinx() -{ - open IN, $conf or die "Can't open $conf"; - while () { - if (m/^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]/) { - $min_version=$1; - last; - } - } - close IN; - - die "Can't get needs_sphinx version from $conf" if (!$min_version); - - $virtenv_dir = $virtenv_prefix . "latest"; - - my $sphinx = get_sphinx_fname(); - if ($sphinx eq "") { - $need_sphinx = 1; - return; - } - - $cur_version = get_sphinx_version($sphinx); - die "$sphinx didn't return its version" if (!$cur_version); - - if ($cur_version lt $min_version) { - printf "ERROR: Sphinx version is %s. It should be >= %s\n", - $cur_version, $min_version; - $need_sphinx = 1; - return; - } - - return if ($cur_version lt $rec_version); - - # On version check mode, just assume Sphinx has all mandatory deps - exit (0) if ($version_check); -} - -# -# Ancillary subroutines -# - -sub catcheck($) -{ - my $res = ""; - $res = qx(cat $_[0]) if (-r $_[0]); - return $res; -} - -sub which($) -{ - my $file = shift; - my @path = split ":", $ENV{PATH}; - - foreach my $dir(@path) { - my $name = $dir.'/'.$file; - return $name if (-x $name ); - } - return undef; -} - -# -# Subroutines that check distro-specific hints -# - -sub give_debian_hints() -{ - my %map = ( - "python-sphinx" => "python3-sphinx", - "yaml" => "python3-yaml", - "ensurepip" => "python3-venv", - "virtualenv" => "virtualenv", - "dot" => "graphviz", - "convert" => "imagemagick", - "Pod::Usage" => "perl-modules", - "xelatex" => "texlive-xetex", - "rsvg-convert" => "librsvg2-bin", - ); - - if ($pdf) { - check_missing_file(["/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty"], - "texlive-lang-chinese", 2); - - check_missing_file(["/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"], - "fonts-dejavu", 2); - - check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc"], - "fonts-noto-cjk", 2); - } - - check_program("dvipng", 2) if ($pdf); - check_missing(\%map); - - return if (!$need && !$optional); - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo apt-get install $install\n"); -} - -sub give_redhat_hints() -{ - my %map = ( - "python-sphinx" => "python3-sphinx", - "yaml" => "python3-pyyaml", - "virtualenv" => "python3-virtualenv", - "dot" => "graphviz", - "convert" => "ImageMagick", - "Pod::Usage" => "perl-Pod-Usage", - "xelatex" => "texlive-xetex-bin", - "rsvg-convert" => "librsvg2-tools", - ); - - my @fedora26_opt_pkgs = ( - "graphviz-gd", # Fedora 26: needed for PDF support - ); - - my @fedora_tex_pkgs = ( - "texlive-collection-fontsrecommended", - "texlive-collection-latex", - "texlive-xecjk", - "dejavu-sans-fonts", - "dejavu-serif-fonts", - "dejavu-sans-mono-fonts", - ); - - # - # Checks valid for RHEL/CentOS version 7.x. - # - my $old = 0; - my $rel; - my $noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts"; - $rel = $2 if ($system_release =~ /(release|Linux)\s+(\d+)/); - - if (!($system_release =~ /Fedora/)) { - $map{"virtualenv"} = "python-virtualenv"; - - if ($rel && $rel < 8) { - $old = 1; - $pdf = 0; - - printf("Note: texlive packages on RHEL/CENTOS <= 7 are incomplete. Can't support PDF output\n"); - printf("If you want to build PDF, please read:\n"); - printf("\thttps://www.systutorials.com/241660/how-to-install-tex-live-on-centos-7-linux/\n"); - } - } else { - if ($rel && $rel < 26) { - $old = 1; - } - if ($rel && $rel >= 38) { - $noto_sans_redhat = "google-noto-sans-cjk-fonts"; - } - } - if (!$rel) { - printf("Couldn't identify release number\n"); - $old = 1; - $pdf = 0; - } - - if ($pdf) { - check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc"], - $noto_sans_redhat, 2); - } - - check_rpm_missing(\@fedora26_opt_pkgs, 2) if ($pdf && !$old); - check_rpm_missing(\@fedora_tex_pkgs, 2) if ($pdf); - check_missing_tex(2) if ($pdf); - check_missing(\%map); - - return if (!$need && !$optional); - - if (!$old) { - # dnf, for Fedora 18+ - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo dnf install -y $install\n"); - } else { - # yum, for RHEL (and clones) or Fedora version < 18 - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo yum install -y $install\n"); - } -} - -sub give_opensuse_hints() -{ - my %map = ( - "python-sphinx" => "python3-sphinx", - "yaml" => "python3-pyyaml", - "virtualenv" => "python3-virtualenv", - "dot" => "graphviz", - "convert" => "ImageMagick", - "Pod::Usage" => "perl-Pod-Usage", - "xelatex" => "texlive-xetex-bin", - ); - - # On Tumbleweed, this package is also named rsvg-convert - $map{"rsvg-convert"} = "rsvg-view" if (!($system_release =~ /Tumbleweed/)); - - my @suse_tex_pkgs = ( - "texlive-babel-english", - "texlive-caption", - "texlive-colortbl", - "texlive-courier", - "texlive-dvips", - "texlive-helvetic", - "texlive-makeindex", - "texlive-metafont", - "texlive-metapost", - "texlive-palatino", - "texlive-preview", - "texlive-times", - "texlive-zapfchan", - "texlive-zapfding", - ); - - $map{"latexmk"} = "texlive-latexmk-bin"; - - # FIXME: add support for installing CJK fonts - # - # I tried hard, but was unable to find a way to install - # "Noto Sans CJK SC" on openSUSE - - check_rpm_missing(\@suse_tex_pkgs, 2) if ($pdf); - check_missing_tex(2) if ($pdf); - check_missing(\%map); - - return if (!$need && !$optional); - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo zypper install --no-recommends $install\n"); -} - -sub give_mageia_hints() -{ - my %map = ( - "python-sphinx" => "python3-sphinx", - "yaml" => "python3-yaml", - "virtualenv" => "python3-virtualenv", - "dot" => "graphviz", - "convert" => "ImageMagick", - "Pod::Usage" => "perl-Pod-Usage", - "xelatex" => "texlive", - "rsvg-convert" => "librsvg2", - ); - - my @tex_pkgs = ( - "texlive-fontsextra", - ); - - $map{"latexmk"} = "texlive-collection-basic"; - - my $packager_cmd; - my $noto_sans; - if ($system_release =~ /OpenMandriva/) { - $packager_cmd = "dnf install"; - $noto_sans = "noto-sans-cjk-fonts"; - @tex_pkgs = ( "texlive-collection-fontsextra" ); - } else { - $packager_cmd = "urpmi"; - $noto_sans = "google-noto-sans-cjk-ttc-fonts"; - } - - - if ($pdf) { - check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", - "/usr/share/fonts/TTF/NotoSans-Regular.ttf"], - $noto_sans, 2); - } - - check_rpm_missing(\@tex_pkgs, 2) if ($pdf); - check_missing(\%map); - - return if (!$need && !$optional); - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo $packager_cmd $install\n"); -} - -sub give_arch_linux_hints() -{ - my %map = ( - "yaml" => "python-yaml", - "virtualenv" => "python-virtualenv", - "dot" => "graphviz", - "convert" => "imagemagick", - "xelatex" => "texlive-xetex", - "latexmk" => "texlive-core", - "rsvg-convert" => "extra/librsvg", - ); - - my @archlinux_tex_pkgs = ( - "texlive-core", - "texlive-latexextra", - "ttf-dejavu", - ); - check_pacman_missing(\@archlinux_tex_pkgs, 2) if ($pdf); - - if ($pdf) { - check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], - "noto-fonts-cjk", 2); - } - - check_missing(\%map); - - return if (!$need && !$optional); - printf("You should run:\n") if ($verbose_warn_install); - printf("\n\tsudo pacman -S $install\n"); -} - -sub give_gentoo_hints() -{ - my %map = ( - "yaml" => "dev-python/pyyaml", - "virtualenv" => "dev-python/virtualenv", - "dot" => "media-gfx/graphviz", - "convert" => "media-gfx/imagemagick", - "xelatex" => "dev-texlive/texlive-xetex media-fonts/dejavu", - "rsvg-convert" => "gnome-base/librsvg", - ); - - check_missing_file(["/usr/share/fonts/dejavu/DejaVuSans.ttf"], - "media-fonts/dejavu", 2) if ($pdf); - - if ($pdf) { - check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf", - "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc"], - "media-fonts/noto-cjk", 2); - } - - check_missing(\%map); - - return if (!$need && !$optional); - - printf("You should run:\n") if ($verbose_warn_install); - printf("\n"); - - my $imagemagick = "media-gfx/imagemagick svg png"; - my $cairo = "media-gfx/graphviz cairo pdf"; - my $portage_imagemagick = "/etc/portage/package.use/imagemagick"; - my $portage_cairo = "/etc/portage/package.use/graphviz"; - - if (qx(grep imagemagick $portage_imagemagick 2>/dev/null) eq "") { - printf("\tsudo su -c 'echo \"$imagemagick\" > $portage_imagemagick'\n") - } - if (qx(grep graphviz $portage_cairo 2>/dev/null) eq "") { - printf("\tsudo su -c 'echo \"$cairo\" > $portage_cairo'\n"); - } - - printf("\tsudo emerge --ask $install\n"); - -} - -sub check_distros() -{ - # Distro-specific hints - if ($system_release =~ /Red Hat Enterprise Linux/) { - give_redhat_hints; - return; - } - if ($system_release =~ /CentOS/) { - give_redhat_hints; - return; - } - if ($system_release =~ /Scientific Linux/) { - give_redhat_hints; - return; - } - if ($system_release =~ /Oracle Linux Server/) { - give_redhat_hints; - return; - } - if ($system_release =~ /Fedora/) { - give_redhat_hints; - return; - } - if ($system_release =~ /Ubuntu/) { - give_debian_hints; - return; - } - if ($system_release =~ /Debian/) { - give_debian_hints; - return; - } - if ($system_release =~ /openSUSE/) { - give_opensuse_hints; - return; - } - if ($system_release =~ /Mageia/) { - give_mageia_hints; - return; - } - if ($system_release =~ /OpenMandriva/) { - give_mageia_hints; - return; - } - if ($system_release =~ /Arch Linux/) { - give_arch_linux_hints; - return; - } - if ($system_release =~ /Gentoo/) { - give_gentoo_hints; - return; - } - - # - # Fall-back to generic hint code for other distros - # That's far from ideal, specially for LaTeX dependencies. - # - my %map = ( - "sphinx-build" => "sphinx" - ); - check_missing_tex(2) if ($pdf); - check_missing(\%map); - print "I don't know distro $system_release.\n"; - print "So, I can't provide you a hint with the install procedure.\n"; - print "There are likely missing dependencies.\n"; -} - -# -# Common dependencies -# - -sub deactivate_help() -{ - printf "\n If you want to exit the virtualenv, you can use:\n"; - printf "\tdeactivate\n"; -} - -sub get_virtenv() -{ - my $ver; - my $min_activate = "$ENV{'PWD'}/${virtenv_prefix}${min_version}/bin/activate"; - my @activates = glob "$ENV{'PWD'}/${virtenv_prefix}*/bin/activate"; - - @activates = sort {$b cmp $a} @activates; - - foreach my $f (@activates) { - next if ($f lt $min_activate); - - my $sphinx_cmd = $f; - $sphinx_cmd =~ s/activate/sphinx-build/; - next if (! -f $sphinx_cmd); - - my $ver = get_sphinx_version($sphinx_cmd); - - if (!$ver) { - $f =~ s#/bin/activate##; - print("Warning: virtual environment $f is not working.\nPython version upgrade? Remove it with:\n\n\trm -rf $f\n\n"); - } - - if ($need_sphinx && ($ver ge $min_version)) { - return ($f, $ver); - } elsif ($ver gt $cur_version) { - return ($f, $ver); - } - } - return ("", ""); -} - -sub recommend_sphinx_upgrade() -{ - my $venv_ver; - - # Avoid running sphinx-builds from venv if $cur_version is good - if ($cur_version && ($cur_version ge $rec_version)) { - $latest_avail_ver = $cur_version; - return; - } - - # Get the highest version from sphinx_*/bin/sphinx-build and the - # corresponding command to activate the venv/virtenv - ($activate_cmd, $venv_ver) = get_virtenv(); - - # Store the highest version from Sphinx existing virtualenvs - if (($activate_cmd ne "") && ($venv_ver gt $cur_version)) { - $latest_avail_ver = $venv_ver; - } else { - $latest_avail_ver = $cur_version if ($cur_version); - } - - # As we don't know package version of Sphinx, and there's no - # virtual environments, don't check if upgrades are needed - if (!$virtualenv) { - return if (!$latest_avail_ver); - } - - # Either there are already a virtual env or a new one should be created - $need_pip = 1; - - return if (!$latest_avail_ver); - - # Return if the reason is due to an upgrade or not - if ($latest_avail_ver lt $rec_version) { - $rec_sphinx_upgrade = 1; - } - - return $latest_avail_ver; -} - -# -# The logic here is complex, as it have to deal with different versions: -# - minimal supported version; -# - minimal PDF version; -# - recommended version. -# It also needs to work fine with both distro's package and venv/virtualenv -sub recommend_sphinx_version($) -{ - my $virtualenv_cmd = shift; - - # Version is OK. Nothing to do. - if ($cur_version && ($cur_version ge $rec_version)) { - return; - }; - - if (!$need_sphinx) { - # sphinx-build is present and its version is >= $min_version - - #only recommend enabling a newer virtenv version if makes sense. - if ($latest_avail_ver gt $cur_version) { - printf "\nYou may also use the newer Sphinx version $latest_avail_ver with:\n"; - printf "\tdeactivate\n" if ($ENV{'PWD'} =~ /${virtenv_prefix}/); - printf "\t. $activate_cmd\n"; - deactivate_help(); - - return; - } - return if ($latest_avail_ver ge $rec_version); - } - - if (!$virtualenv) { - # No sphinx either via package or via virtenv. As we can't - # Compare the versions here, just return, recommending the - # user to install it from the package distro. - return if (!$latest_avail_ver); - - # User doesn't want a virtenv recommendation, but he already - # installed one via virtenv with a newer version. - # So, print commands to enable it - if ($latest_avail_ver gt $cur_version) { - printf "\nYou may also use the Sphinx virtualenv version $latest_avail_ver with:\n"; - printf "\tdeactivate\n" if ($ENV{'PWD'} =~ /${virtenv_prefix}/); - printf "\t. $activate_cmd\n"; - deactivate_help(); - - return; - } - print "\n"; - } else { - $need++ if ($need_sphinx); - } - - # Suggest newer versions if current ones are too old - if ($latest_avail_ver && $latest_avail_ver ge $min_version) { - # If there's a good enough version, ask the user to enable it - if ($latest_avail_ver ge $rec_version) { - printf "\nNeed to activate Sphinx (version $latest_avail_ver) on virtualenv with:\n"; - printf "\t. $activate_cmd\n"; - deactivate_help(); - - return; - } - - # Version is above the minimal required one, but may be - # below the recommended one. So, print warnings/notes - - if ($latest_avail_ver lt $rec_version) { - print "Warning: It is recommended at least Sphinx version $rec_version.\n"; - } - } - - # At this point, either it needs Sphinx or upgrade is recommended, - # both via pip - - if ($rec_sphinx_upgrade) { - if (!$virtualenv) { - print "Instead of install/upgrade Python Sphinx pkg, you could use pip/pypi with:\n\n"; - } else { - print "To upgrade Sphinx, use:\n\n"; - } - } else { - print "\nSphinx needs to be installed either:\n1) via pip/pypi with:\n\n"; - } - - $python_cmd = find_python_no_venv(); - - printf "\t$virtualenv_cmd $virtenv_dir\n"; - - printf "\t. $virtenv_dir/bin/activate\n"; - printf "\tpip install -r $requirement_file\n"; - deactivate_help(); - - printf "\n2) As a package with:\n"; - - my $old_need = $need; - my $old_optional = $optional; - %missing = (); - $pdf = 0; - $optional = 0; - $install = ""; - $verbose_warn_install = 0; - - add_package("python-sphinx", 0); - - check_distros(); - - $need = $old_need; - $optional = $old_optional; - - printf "\n Please note that Sphinx >= 3.0 will currently produce false-positive\n"; - printf " warning when the same name is used for more than one type (functions,\n"; - printf " structs, enums,...). This is known Sphinx bug. For more details, see:\n"; - printf "\thttps://github.com/sphinx-doc/sphinx/pull/8313\n"; -} - -sub check_needs() -{ - # Check if Sphinx is already accessible from current environment - check_sphinx(); - - if ($system_release) { - print "Detected OS: $system_release.\n"; - } else { - print "Unknown OS\n"; - } - printf "Sphinx version: %s\n\n", $cur_version if ($cur_version); - - # Check python command line, trying first python3 - $python_cmd = findprog("python3"); - $python_cmd = check_program("python", 0) if (!$python_cmd); - - # Check the type of virtual env, depending on Python version - if ($python_cmd) { - if ($virtualenv) { - my $tmp = qx($python_cmd --version 2>&1); - if ($tmp =~ m/(\d+\.)(\d+\.)/) { - if ($1 < 3) { - # Fail if it finds python2 (or worse) - die "Python 3 is required to build the kernel docs\n"; - } - if ($1 == 3 && $2 < 3) { - # Need Python 3.3 or upper for venv - $need_virtualenv = 1; - } - } else { - die "Warning: couldn't identify $python_cmd version!"; - } - } else { - add_package("python-sphinx", 0); - } - } - - my $venv_ver = recommend_sphinx_upgrade(); - - my $virtualenv_cmd; - - if ($need_pip) { - # Set virtualenv command line, if python < 3.3 - if ($need_virtualenv) { - $virtualenv_cmd = findprog("virtualenv-3"); - $virtualenv_cmd = findprog("virtualenv-3.5") if (!$virtualenv_cmd); - if (!$virtualenv_cmd) { - check_program("virtualenv", 0); - $virtualenv_cmd = "virtualenv"; - } - } else { - $virtualenv_cmd = "$python_cmd -m venv"; - check_python_module("ensurepip", 0); - } - } - - # Check for needed programs/tools - check_perl_module("Pod::Usage", 0); - check_python_module("yaml", 0); - check_program("make", 0); - check_program("gcc", 0); - check_program("dot", 1); - check_program("convert", 1); - - # Extra PDF files - should use 2 for is_optional - check_program("xelatex", 2) if ($pdf); - check_program("rsvg-convert", 2) if ($pdf); - check_program("latexmk", 2) if ($pdf); - - # Do distro-specific checks and output distro-install commands - check_distros(); - - if (!$python_cmd) { - if ($need == 1) { - die "Can't build as $need mandatory dependency is missing"; - } elsif ($need) { - die "Can't build as $need mandatory dependencies are missing"; - } - } - - # Check if sphinx-build is called sphinx-build-3 - if ($need_symlink) { - printf "\tsudo ln -sf %s /usr/bin/sphinx-build\n\n", - which("sphinx-build-3"); - } - - recommend_sphinx_version($virtualenv_cmd); - printf "\n"; - - print "All optional dependencies are met.\n" if (!$optional); - - if ($need == 1) { - die "Can't build as $need mandatory dependency is missing"; - } elsif ($need) { - die "Can't build as $need mandatory dependencies are missing"; - } - - print "Needed package dependencies are met.\n"; -} - -# -# Main -# - -while (@ARGV) { - my $arg = shift(@ARGV); - - if ($arg eq "--no-virtualenv") { - $virtualenv = 0; - } elsif ($arg eq "--no-pdf"){ - $pdf = 0; - } elsif ($arg eq "--version-check"){ - $version_check = 1; - } else { - print "Usage:\n\t$0 <--no-virtualenv> <--no-pdf> <--version-check>\n\n"; - print "Where:\n"; - print "\t--no-virtualenv\t- Recommend installing Sphinx instead of using a virtualenv\n"; - print "\t--version-check\t- if version is compatible, don't check for missing dependencies\n"; - print "\t--no-pdf\t- don't check for dependencies required to build PDF docs\n\n"; - exit -1; - } -} - -# -# Determine the system type. There's no standard unique way that would -# work with all distros with a minimal package install. So, several -# methods are used here. -# -# By default, it will use lsb_release function. If not available, it will -# fail back to reading the known different places where the distro name -# is stored -# - -$system_release = qx(lsb_release -d) if which("lsb_release"); -$system_release =~ s/Description:\s*// if ($system_release); -$system_release = catcheck("/etc/system-release") if !$system_release; -$system_release = catcheck("/etc/redhat-release") if !$system_release; -$system_release = catcheck("/etc/lsb-release") if !$system_release; -$system_release = catcheck("/etc/gentoo-release") if !$system_release; - -# This seems more common than LSB these days -if (!$system_release) { - my %os_var; - if (open IN, "cat /etc/os-release|") { - while () { - if (m/^([\w\d\_]+)=\"?([^\"]*)\"?\n/) { - $os_var{$1}=$2; - } - } - $system_release = $os_var{"NAME"}; - if (defined($os_var{"VERSION_ID"})) { - $system_release .= " " . $os_var{"VERSION_ID"} if (defined($os_var{"VERSION_ID"})); - } else { - $system_release .= " " . $os_var{"VERSION"}; - } - } -} -$system_release = catcheck("/etc/issue") if !$system_release; -$system_release =~ s/\s+$//; - -check_needs; -- cgit v1.2.3 From df4d2f966354297c94ba5e198533cc31548a12a8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:53 +0200 Subject: scripts: sphinx-pre-install: update mandatory system deps To build docs, gcc is not needed. Also, Kernel can be built nowadays with clang. So, drop it. On the other hand, which is needed. Add a system dependency for it. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/4ec979e4692c9e4acd6c31424c0e2f4bf5b80e71.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index 7dfe5c2a6cc2..fc9dc45054d7 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -1476,7 +1476,7 @@ class SphinxDependencyChecker(MissingCheckers): self.check_perl_module("Pod::Usage", DepManager.SYSTEM_MANDATORY) self.check_program("make", DepManager.SYSTEM_MANDATORY) - self.check_program("gcc", DepManager.SYSTEM_MANDATORY) + self.check_program("which", DepManager.SYSTEM_MANDATORY) self.check_program("dot", DepManager.SYSTEM_OPTIONAL) self.check_program("convert", DepManager.SYSTEM_OPTIONAL) -- cgit v1.2.3 From 9f51a1d6966760dadf62ff1f62c614a342fb7e62 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:54 +0200 Subject: scripts: sphinx-pre-install: add support for RHEL8-based distros On RHEL8, only installing with a venv is supported, as there's no Sphinx package using Python 3.7 or upper. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/bcdde20edab07be6bf447eac18eecdd88c7f947c.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index fc9dc45054d7..324baa98a395 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -691,7 +691,7 @@ class SphinxDependencyChecker(MissingCheckers): super().__init__(args, texlive) - self.need_pip = 0 + self.need_pip = False self.rec_sphinx_upgrade = 0 self.system_release = self.get_system_release() @@ -840,9 +840,15 @@ class SphinxDependencyChecker(MissingCheckers): # RHEL 8 uses Python 3.6, which is not compatible with # the build system anymore. Suggest Python 3.11 if rel == 8: - self.deps.add_package("python39", DepManager.SYSTEM_MANDATORY) + self.check_program("python3.9", DepManager.SYSTEM_MANDATORY) + progs["python3.9"] = "python39" + progs["yaml"] = "python39-pyyaml" + self.recommend_python = True + # There's no python39-sphinx package. Only pip is supported + self.package_supported = False + if not self.distro_msg: self.distro_msg = \ "Note: RHEL-based distros typically require extra repositories.\n" \ @@ -915,8 +921,9 @@ class SphinxDependencyChecker(MissingCheckers): # the build system anymore. Suggest Python 3.11 if rel == 15: if not self.which(self.python_cmd): + self.check_program("python3.11", DepManager.SYSTEM_MANDATORY) + progs["python3.11"] = "python311" self.recommend_python = True - self.deps.add_package(self.python_cmd, DepManager.SYSTEM_MANDATORY) progs.update({ "python-sphinx": "python311-Sphinx", @@ -1289,7 +1296,7 @@ class SphinxDependencyChecker(MissingCheckers): return self.latest_avail_ver # Either there are already a virtual env or a new one should be created - self.need_pip = 1 + self.need_pip = True if not self.latest_avail_ver: return None @@ -1344,10 +1351,11 @@ class SphinxDependencyChecker(MissingCheckers): """ if self.recommend_python: - print("\nPython version is incompatible with doc build.\n" \ - "Please upgrade it and re-run.\n") - return - + cur_ver = sys.version_info[:3] + if cur_ver < MIN_PYTHON_VERSION: + print(f"\nPython version {cur_ver} is incompatible with doc build.\n" \ + "Please upgrade it and re-run.\n") + return # Version is OK. Nothing to do. if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION: @@ -1467,7 +1475,6 @@ class SphinxDependencyChecker(MissingCheckers): if sys.version_info < MIN_PYTHON_VERSION: self.need_pip = False print("Warning: python version is not supported.") - else: virtualenv_cmd = f"{self.python_cmd} -m venv" self.check_python_module("ensurepip") -- cgit v1.2.3 From 491a99511eaf333855caf28170cfb3f6f4460b54 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:55 +0200 Subject: scripts: sphinx-pre-install: add a warning for Debian-based distros On Some Debian-based distros, ImageMagick package has a broken policy that causes LaTeX to fail while building docs. Add a note about that. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/628d01784e8c24e3d93c69c436f12398e00165b3.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index 324baa98a395..09a337509b23 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -782,6 +782,11 @@ class SphinxDependencyChecker(MissingCheckers): self.check_program("dvipng", DepManager.PDF_MANDATORY) + if not self.distro_msg: + self.distro_msg = \ + "Note: ImageMagick is broken on some distros, affecting PDF output. For more details:\n" \ + "\thttps://askubuntu.com/questions/1158894/imagemagick-still-broken-using-with-usr-bin-convert" + return self.get_install_progs(progs, "apt-get install") def give_redhat_hints(self): @@ -1193,7 +1198,7 @@ class SphinxDependencyChecker(MissingCheckers): self.distro_msg = \ f"I don't know distro {self.system_release}.\n" \ "So, I can't provide you a hint with the install procedure.\n" \ - "There are likely missing dependencies.\n" + "There are likely missing dependencies." return self.get_install_progs(progs, None) -- cgit v1.2.3 From 6170b1eacac881af5732af78f33b76bd560c441b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 12 Aug 2025 17:52:56 +0200 Subject: scripts: sphinx-pre-install: some adjustments related to venv While nothing was really needed for virtualenv to work on most distros, we had an issue with OpenMandriva. While checking for it, it was noticed that there was no check if python-virtualenv was installed. This didn't solve the issues we faced there: at least with the half-broken OpenMandriva Lx 4.0 docker container we used, ensurepip was not available anywhere, causing venv to fail. Add a distro-specific note about that. Note: at least at the time we did our tests, OpenMandriva Lx 4.0 docker was shipped with wrong dnf repositories. Also, there was no repos available for it anymore. So, we had to do some hacks to upgrade to 4.3 before being able to run any tests. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/e3a0e5eccd50eb506846e3e8487a2d9124ef83e2.1754992972.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index 09a337509b23..b8474848df4e 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -983,6 +983,22 @@ class SphinxDependencyChecker(MissingCheckers): # Tested on OpenMandriva Lx 4.3 progs["convert"] = "imagemagick" progs["yaml"] = "python-pyyaml" + progs["python-virtualenv"] = "python-virtualenv" + progs["python-sphinx"] = "python-sphinx" + + self.check_program("python-virtualenv", DepManager.PYTHON_MANDATORY) + + # On my tests with openMandriva LX 4.0 docker image, upgraded + # to 4.3, python-virtualenv package is broken: it is missing + # ensurepip. Without it, the alternative would be to run: + # python3 -m venv --without-pip ~/sphinx_latest, but running + # pip there won't install sphinx at venv. + # + # Add a note about that. + + if not self.distro_msg: + self.distro_msg = \ + "Note: for venv, ensurepip could be broken, preventing its install method." else: packager_cmd = "urpmi" -- cgit v1.2.3 From f852ce052a8b78d6fade371bd1fad583541e78fa Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Thu, 14 Aug 2025 12:14:41 +0200 Subject: kbuild: align W=e with CONFIG_WERROR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONFIG_WERROR sets KBUILD_CPPFLAGS while W=e would only set KBUILD_CFLAGS. As a preparation to unify the two mechanism, align their effects. While at it, add some alignment whitespace to prepare for later additions to the list of changed variables. Signed-off-by: Thomas Weißschuh Reviewed-by: Nicolas Schier Link: https://lore.kernel.org/r/20250814-kbuild-werror-v2-1-c01e596309d2@linutronix.de Signed-off-by: Nathan Chancellor --- scripts/Makefile.extrawarn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn index dca175fffcab..4b4e8e136ce6 100644 --- a/scripts/Makefile.extrawarn +++ b/scripts/Makefile.extrawarn @@ -218,6 +218,6 @@ endif # ifneq ($(findstring e, $(KBUILD_EXTRA_WARN)),) -KBUILD_CFLAGS += -Werror +KBUILD_CPPFLAGS += -Werror endif -- cgit v1.2.3 From e7a10929c574cf30981a8e19ef39bc35e63e8b46 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Thu, 14 Aug 2025 12:14:42 +0200 Subject: kbuild: unify W=e and CONFIG_WERROR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two mechanisms have the same effect, unify their implementation. Also avoid spurious rebuilds when switching between the two. Signed-off-by: Thomas Weißschuh Reviewed-by: Nicolas Schier Link: https://lore.kernel.org/r/20250814-kbuild-werror-v2-2-c01e596309d2@linutronix.de Signed-off-by: Nathan Chancellor --- scripts/Makefile.extrawarn | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn index 4b4e8e136ce6..1ffc7beca43b 100644 --- a/scripts/Makefile.extrawarn +++ b/scripts/Makefile.extrawarn @@ -25,8 +25,6 @@ ifneq ($(CONFIG_FRAME_WARN),0) KBUILD_CFLAGS += -Wframe-larger-than=$(CONFIG_FRAME_WARN) endif -KBUILD_CPPFLAGS-$(CONFIG_WERROR) += -Werror -KBUILD_CPPFLAGS += $(KBUILD_CPPFLAGS-y) KBUILD_CFLAGS-$(CONFIG_CC_NO_ARRAY_BOUNDS) += -Wno-array-bounds ifdef CONFIG_CC_IS_CLANG @@ -214,9 +212,9 @@ KBUILD_CFLAGS += -Wno-unused-parameter endif # -# W=e - error out on warnings +# W=e and CONFIG_WERROR - error out on warnings # -ifneq ($(findstring e, $(KBUILD_EXTRA_WARN)),) +ifneq ($(findstring e, $(KBUILD_EXTRA_WARN))$(CONFIG_WERROR),) KBUILD_CPPFLAGS += -Werror -- cgit v1.2.3 From 592b571f20c5b905c47a1370210456f9f90ce04f Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Thu, 14 Aug 2025 12:14:43 +0200 Subject: kbuild: rust: move `-Dwarnings` handling to `Makefile.extrawarn` Following commit e88ca24319e4 ("kbuild: consolidate warning flags in scripts/Makefile.extrawarn"), move `-Dwarnings` handling into `Makefile.extrawarn` like C's `-Werror`. No functional change intended. Signed-off-by: Miguel Ojeda Reviewed-by: Nicolas Schier Link: https://lore.kernel.org/r/20250814-kbuild-werror-v2-3-c01e596309d2@linutronix.de Signed-off-by: Nathan Chancellor --- Makefile | 3 --- scripts/Makefile.extrawarn | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'scripts') diff --git a/Makefile b/Makefile index 6bfe776bf3c5..6630a66659c6 100644 --- a/Makefile +++ b/Makefile @@ -901,9 +901,6 @@ stackp-flags-$(CONFIG_STACKPROTECTOR_STRONG) := -fstack-protector-strong KBUILD_CFLAGS += $(stackp-flags-y) -KBUILD_RUSTFLAGS-$(CONFIG_WERROR) += -Dwarnings -KBUILD_RUSTFLAGS += $(KBUILD_RUSTFLAGS-y) - ifdef CONFIG_FRAME_POINTER KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls KBUILD_RUSTFLAGS += -Cforce-frame-pointers=y diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn index 1ffc7beca43b..b04b3062e0e4 100644 --- a/scripts/Makefile.extrawarn +++ b/scripts/Makefile.extrawarn @@ -217,5 +217,6 @@ endif ifneq ($(findstring e, $(KBUILD_EXTRA_WARN))$(CONFIG_WERROR),) KBUILD_CPPFLAGS += -Werror +KBUILD_RUSTFLAGS += -Dwarnings endif -- cgit v1.2.3 From ec4a3992bc0b5b659eceb44a9f8582b26f2c8489 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Thu, 14 Aug 2025 12:14:44 +0200 Subject: kbuild: respect CONFIG_WERROR for linker and assembler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The linker and assembler do not share the compiler flags. Make sure they also fail on warnings with CONFIG_WERROR and W=e. Signed-off-by: Thomas Weißschuh Reviewed-by: Nicolas Schier Link: https://lore.kernel.org/r/20250814-kbuild-werror-v2-4-c01e596309d2@linutronix.de Signed-off-by: Nathan Chancellor --- scripts/Makefile.extrawarn | 2 ++ 1 file changed, 2 insertions(+) (limited to 'scripts') diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn index b04b3062e0e4..6efb9e5eeeed 100644 --- a/scripts/Makefile.extrawarn +++ b/scripts/Makefile.extrawarn @@ -217,6 +217,8 @@ endif ifneq ($(findstring e, $(KBUILD_EXTRA_WARN))$(CONFIG_WERROR),) KBUILD_CPPFLAGS += -Werror +KBUILD_AFLAGS += -Wa,--fatal-warnings +KBUILD_LDFLAGS += --fatal-warnings KBUILD_RUSTFLAGS += -Dwarnings endif -- cgit v1.2.3 From 3f0ff4cc6ffb43ea250fdf69d9f7ae8e5a05f1f9 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Thu, 14 Aug 2025 12:14:45 +0200 Subject: kbuild: respect CONFIG_WERROR for userprogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The userprogs compiler and linker do not share the regular compiler flags. Make sure they also fail on warnings with CONFIG_WERROR and W=e. Signed-off-by: Thomas Weißschuh Reviewed-by: Nicolas Schier Link: https://lore.kernel.org/r/20250814-kbuild-werror-v2-5-c01e596309d2@linutronix.de Signed-off-by: Nathan Chancellor --- scripts/Makefile.extrawarn | 2 ++ 1 file changed, 2 insertions(+) (limited to 'scripts') diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn index 6efb9e5eeeed..96ff3f5582d6 100644 --- a/scripts/Makefile.extrawarn +++ b/scripts/Makefile.extrawarn @@ -219,6 +219,8 @@ ifneq ($(findstring e, $(KBUILD_EXTRA_WARN))$(CONFIG_WERROR),) KBUILD_CPPFLAGS += -Werror KBUILD_AFLAGS += -Wa,--fatal-warnings KBUILD_LDFLAGS += --fatal-warnings +KBUILD_USERCFLAGS += -Werror +KBUILD_USERLDFLAGS += -Wl,--fatal-warnings KBUILD_RUSTFLAGS += -Dwarnings endif -- cgit v1.2.3 From 670ec7333a2c4823ac777b70f045cf731525ae5e Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 14 Aug 2025 09:40:29 -0600 Subject: docs: kdoc: remove dead code create_parameter_list() tests an argument against the same regex twice, in two different locations; remove the pointless extra tests and the never-executed error cases that go with them. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250814154035.328769-2-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 9e65948f8254..96e3fe4ec431 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -564,28 +564,18 @@ class KernelDoc: args.insert(0, first_arg.pop()) dtype = ' '.join(first_arg) + bitfield_re = KernRe(r'(.*?):(\w+)') for param in args: - if KernRe(r'^(\*+)\s*(.*)').match(param): - r = KernRe(r'^(\*+)\s*(.*)') - if not r.match(param): - self.emit_msg(ln, f"Invalid param: {param}") - continue - - param = r.group(1) - + r = KernRe(r'^(\*+)\s*(.*)') + if r.match(param): self.push_parameter(ln, decl_type, r.group(2), f"{dtype} {r.group(1)}", arg, declaration_name) - elif KernRe(r'(.*?):(\w+)').search(param): - r = KernRe(r'(.*?):(\w+)') - if not r.match(param): - self.emit_msg(ln, f"Invalid param: {param}") - continue - + elif bitfield_re.search(param): if dtype != "": # Skip unnamed bit-fields - self.push_parameter(ln, decl_type, r.group(1), - f"{dtype}:{r.group(2)}", + self.push_parameter(ln, decl_type, bitfield_re.group(1), + f"{dtype}:{bitfield_re.group(2)}", arg, declaration_name) else: self.push_parameter(ln, decl_type, param, dtype, -- cgit v1.2.3 From f51b42b99e1d35698e0277337fde2c15ccc29a2b Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 14 Aug 2025 09:40:30 -0600 Subject: docs: kdoc: tidy up space removal in create_parameter_list() Remove a redundant test and add a comment describing what the space removal is doing. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250814154035.328769-3-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 96e3fe4ec431..53051ce831ba 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -545,12 +545,14 @@ class KernelDoc: arg, declaration_name) elif arg: + # + # Clean up extraneous spaces and split the string at commas; the first + # element of the resulting list will also include the type information. + # arg = KernRe(r'\s*:\s*').sub(":", arg) arg = KernRe(r'\s*\[').sub('[', arg) - args = KernRe(r'\s*,\s*').split(arg) - if args[0] and '*' in args[0]: - args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) + args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) first_arg = [] r = KernRe(r'^(.*\s+)(.*?\[.*\].*)$') -- cgit v1.2.3 From 05d72fe07242a8e4535aa52e0858f9198e668a41 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 14 Aug 2025 09:40:31 -0600 Subject: docs: kdoc: clean up the create_parameter_list() "first arg" logic The logic for finding the name of the first in a series of variable names is somewhat convoluted and, in the use of .extend(), actively buggy. Document what is happening and simplify the logic. Acked-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250814154035.328769-4-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 53051ce831ba..07234ce04409 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -553,18 +553,18 @@ class KernelDoc: arg = KernRe(r'\s*\[').sub('[', arg) args = KernRe(r'\s*,\s*').split(arg) args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) - - first_arg = [] - r = KernRe(r'^(.*\s+)(.*?\[.*\].*)$') - if args[0] and r.match(args[0]): - args.pop(0) - first_arg.extend(r.group(1)) - first_arg.append(r.group(2)) + # + # args[0] has a string of "type a". If "a" includes an [array] + # declaration, we want to not be fooled by any white space inside + # the brackets, so detect and handle that case specially. + # + r = KernRe(r'^([^[\]]*\s+)(.*)$') + if r.match(args[0]): + args[0] = r.group(2) + dtype = r.group(1) else: - first_arg = KernRe(r'\s+').split(args.pop(0)) - - args.insert(0, first_arg.pop()) - dtype = ' '.join(first_arg) + # No space in args[0]; this seems wrong but preserves previous behavior + dtype = '' bitfield_re = KernRe(r'(.*?):(\w+)') for param in args: -- cgit v1.2.3 From 8f05fbc5afb86f0d4dcae33f3cb0cda561d4d93e Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 14 Aug 2025 09:40:32 -0600 Subject: docs: kdoc: add a couple more comments in create_parameter_list() Make what the final code is doing a bit more clear to slow readers like me. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250814154035.328769-5-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 07234ce04409..29881757bf1c 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -568,12 +568,18 @@ class KernelDoc: bitfield_re = KernRe(r'(.*?):(\w+)') for param in args: + # + # For pointers, shift the star(s) from the variable name to the + # type declaration. + # r = KernRe(r'^(\*+)\s*(.*)') if r.match(param): self.push_parameter(ln, decl_type, r.group(2), f"{dtype} {r.group(1)}", arg, declaration_name) - + # + # Perform a similar shift for bitfields. + # elif bitfield_re.search(param): if dtype != "": # Skip unnamed bit-fields self.push_parameter(ln, decl_type, bitfield_re.group(1), -- cgit v1.2.3 From bf6b310d1b7e31a1cd6951eb75608a1d2876c04a Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 14 Aug 2025 09:40:33 -0600 Subject: docs: kdoc: tighten up the array-of-pointers case Simplify one gnarly regex and remove another altogether; add a comment describing what is going on. There will be no #-substituted commas in this case, so don't bother trying to put them back. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250814154035.328769-6-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 29881757bf1c..7f4d95dd47d4 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -527,23 +527,21 @@ class KernelDoc: dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) - + # + # The array-of-pointers case. Dig the parameter name out from the middle + # of the declaration. + # elif KernRe(r'\(.+\)\s*\[').search(arg): - # Array-of-pointers - - arg = arg.replace('#', ',') - r = KernRe(r'[^\(]+\(\s*\*\s*([\w\[\].]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)') + r = KernRe(r'[^\(]+\(\s*\*\s*' # Up to "(" and maybe "*" + r'([\w.]*?)' # The actual pointer name + r'\s*(\[\s*\w+\s*\]\s*)*\)') # The [array portion] if r.match(arg): param = r.group(1) else: self.emit_msg(ln, f"Invalid param: {arg}") param = arg - - dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) - - self.push_parameter(ln, decl_type, param, dtype, - arg, declaration_name) - + dtype = arg.replace(param, '') + self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) elif arg: # # Clean up extraneous spaces and split the string at commas; the first -- cgit v1.2.3 From e5d91662fcbac251dd17f04dbacf4d997939316e Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 14 Aug 2025 09:40:34 -0600 Subject: docs: kdoc: tighten up the pointer-to-function case Tighten up the code and remove an unneeded regex operation. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250814154035.328769-7-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 7f4d95dd47d4..998b1ece932a 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -511,22 +511,21 @@ class KernelDoc: # Treat preprocessor directive as a typeless variable self.push_parameter(ln, decl_type, arg, "", "", declaration_name) - + # + # The pointer-to-function case. + # elif KernRe(r'\(.+\)\s*\(').search(arg): - # Pointer-to-function - arg = arg.replace('#', ',') - - r = KernRe(r'[^\(]+\(\*?\s*([\w\[\].]*)\s*\)') + r = KernRe(r'[^\(]+\(\*?\s*' # Everything up to "(*" + r'([\w\[\].]*)' # Capture the name and possible [array] + r'\s*\)') # Make sure the trailing ")" is there if r.match(arg): param = r.group(1) else: self.emit_msg(ln, f"Invalid param: {arg}") param = arg - - dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) - self.push_parameter(ln, decl_type, param, dtype, - arg, declaration_name) + dtype = arg.replace(param, '') + self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) # # The array-of-pointers case. Dig the parameter name out from the middle # of the declaration. -- cgit v1.2.3 From 1d8125e27323d8a378cb38f88a6c5a0d7fdb2f6c Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 14 Aug 2025 09:40:35 -0600 Subject: docs: kdoc: remove redundant comment stripping By the time stuff gets to create_parameter_list(), comments have long since been stripped out, so we do not need to do it again here. Acked-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250814154035.328769-8-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 998b1ece932a..a560546c1867 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -493,9 +493,6 @@ class KernelDoc: args = arg_expr.sub(r"\1#", args) for arg in args.split(splitter): - # Strip comments - arg = KernRe(r'/\*.*\*/').sub('', arg) - # Ignore argument attributes arg = KernRe(r'\sPOS0?\s').sub(' ', arg) -- cgit v1.2.3 From 37c52167b007d9d0bb8c5ed53dd6efc4969a1356 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Wed, 13 Aug 2025 12:00:52 +0200 Subject: docs: Remove remainders of reiserfs Reiserfs has been removed in 6.13, there are still some mentions in the documentation about it and the tools. Remove those that don't seem relevant anymore but keep references to reiserfs' r5 hash used by some code. There's one change in a script scripts/selinux/install_policy.sh but it does not seem to be relevant either. Signed-off-by: David Sterba Acked-by: Paul Moore Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250813100053.1291961-1-dsterba@suse.com --- Documentation/admin-guide/ext4.rst | 2 +- Documentation/admin-guide/laptops/laptop-mode.rst | 8 ++++---- Documentation/arch/powerpc/eeh-pci-error-recovery.rst | 1 - Documentation/translations/it_IT/process/changes.rst | 14 -------------- fs/btrfs/tree-log.c | 2 +- scripts/selinux/install_policy.sh | 2 +- 6 files changed, 7 insertions(+), 22 deletions(-) (limited to 'scripts') diff --git a/Documentation/admin-guide/ext4.rst b/Documentation/admin-guide/ext4.rst index b857eb6ca1b6..ac0c709ea9e7 100644 --- a/Documentation/admin-guide/ext4.rst +++ b/Documentation/admin-guide/ext4.rst @@ -398,7 +398,7 @@ There are 3 different data modes: * writeback mode In data=writeback mode, ext4 does not journal data at all. This mode provides - a similar level of journaling as that of XFS, JFS, and ReiserFS in its default + a similar level of journaling as that of XFS and JFS in its default mode - metadata journaling. A crash+recovery can cause incorrect data to appear in files which were written shortly before the crash. This mode will typically provide the best ext4 performance. diff --git a/Documentation/admin-guide/laptops/laptop-mode.rst b/Documentation/admin-guide/laptops/laptop-mode.rst index b61cc601d298..66eb9cd918b5 100644 --- a/Documentation/admin-guide/laptops/laptop-mode.rst +++ b/Documentation/admin-guide/laptops/laptop-mode.rst @@ -61,7 +61,7 @@ Caveats Check your drive's rating, and don't wear down your drive's lifetime if you don't need to. -* If you mount some of your ext3/reiserfs filesystems with the -n option, then +* If you mount some of your ext3 filesystems with the -n option, then the control script will not be able to remount them correctly. You must set DO_REMOUNTS=0 in the control script, otherwise it will remount them with the wrong options -- or it will fail because it cannot write to /etc/mtab. @@ -96,7 +96,7 @@ control script increases dirty_expire_centisecs and dirty_writeback_centisecs in dirtied are not forced to be written to disk as often. The control script also changes the dirty background ratio, so that background writeback of dirty pages is not done anymore. Combined with a higher commit value (also 10 minutes) for -ext3 or ReiserFS filesystems (also done automatically by the control script), +ext3 filesystem (also done automatically by the control script), this results in concentration of disk activity in a small time interval which occurs only once every 10 minutes, or whenever the disk is forced to spin up by a cache miss. The disk can then be spun down in the periods of inactivity. @@ -587,7 +587,7 @@ Control script:: FST=$(deduce_fstype $MP) fi case "$FST" in - "ext3"|"reiserfs") + "ext3") PARSEDOPTS="$(parse_mount_opts commit "$OPTS")" mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE$NOATIME_OPT ;; @@ -647,7 +647,7 @@ Control script:: FST=$(deduce_fstype $MP) fi case "$FST" in - "ext3"|"reiserfs") + "ext3") PARSEDOPTS="$(parse_mount_opts_wfstab $DEV commit $OPTS)" PARSEDOPTS="$(parse_yesno_opts_wfstab $DEV atime atime $PARSEDOPTS)" mount $DEV -t $FST $MP -o remount,$PARSEDOPTS diff --git a/Documentation/arch/powerpc/eeh-pci-error-recovery.rst b/Documentation/arch/powerpc/eeh-pci-error-recovery.rst index d6643a91bdf8..153d0af055b6 100644 --- a/Documentation/arch/powerpc/eeh-pci-error-recovery.rst +++ b/Documentation/arch/powerpc/eeh-pci-error-recovery.rst @@ -315,7 +315,6 @@ network daemons and file systems that didn't need to be disturbed. ideally, the reset should happen at or below the block layer, so that the file systems are not disturbed. - Reiserfs does not tolerate errors returned from the block device. Ext3fs seems to be tolerant, retrying reads/writes until it does succeed. Both have been only lightly tested in this scenario. diff --git a/Documentation/translations/it_IT/process/changes.rst b/Documentation/translations/it_IT/process/changes.rst index 77db13c4022b..7e93833b4511 100644 --- a/Documentation/translations/it_IT/process/changes.rst +++ b/Documentation/translations/it_IT/process/changes.rst @@ -46,7 +46,6 @@ util-linux 2.10o mount --version kmod 13 depmod -V e2fsprogs 1.41.4 e2fsck -V jfsutils 1.1.3 fsck.jfs -V -reiserfsprogs 3.6.3 reiserfsck -V xfsprogs 2.6.0 xfs_db -V squashfs-tools 4.0 mksquashfs -version btrfs-progs 0.18 btrfsck @@ -260,14 +259,6 @@ Sono disponibili i seguenti strumenti: - sono disponibili altri strumenti per il file-system. -Reiserfsprogs -------------- - -Il pacchetto reiserfsprogs dovrebbe essere usato con reiserfs-3.6.x (Linux -kernel 2.4.x). Questo è un pacchetto combinato che contiene versioni -funzionanti di ``mkreiserfs``, ``resize_reiserfs``, ``debugreiserfs`` e -``reiserfsck``. Questi programmi funzionano sulle piattaforme i386 e alpha. - Xfsprogs -------- @@ -479,11 +470,6 @@ JFSutils - -Reiserfsprogs -------------- - -- - Xfsprogs -------- diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 2186e87fb61b..f72ce393f480 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -5460,7 +5460,7 @@ struct btrfs_dir_list { * See process_dir_items_leaf() for details about why it is needed. * This is a recursive operation - if an existing dentry corresponds to a * directory, that directory's new entries are logged too (same behaviour as - * ext3/4, xfs, f2fs, reiserfs, nilfs2). Note that when logging the inodes + * ext3/4, xfs, f2fs, nilfs2). Note that when logging the inodes * the dentries point to we do not acquire their VFS lock, otherwise lockdep * complains about the following circular lock dependency / possible deadlock: * diff --git a/scripts/selinux/install_policy.sh b/scripts/selinux/install_policy.sh index db40237e60ce..77368a73f111 100755 --- a/scripts/selinux/install_policy.sh +++ b/scripts/selinux/install_policy.sh @@ -74,7 +74,7 @@ cd /etc/selinux/dummy/contexts/files $SF -F file_contexts / mounts=`cat /proc/$$/mounts | \ - grep -E "ext[234]|jfs|xfs|reiserfs|jffs2|gfs2|btrfs|f2fs|ocfs2" | \ + grep -E "ext[234]|jfs|xfs|jffs2|gfs2|btrfs|f2fs|ocfs2" | \ awk '{ print $2 '}` $SF -F file_contexts $mounts -- cgit v1.2.3 From 27758d8c2583d10472b745a43ff86fef96c11ef7 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Thu, 14 Aug 2025 12:14:46 +0200 Subject: kbuild: enable -Werror for hostprogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hostprog compilers and linkers do not share the regular compiler flags, so they are not affected by CONFIG_WERROR or W=e. As hostprogs are used during the bootstrap of the build, they can't depend on kconfig options. Enable -Werror unconditionally. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/rust-for-linux/CANiq72k-PdSH2BNgbq=X+FhpyEErifSCKfO5ObXz6bu9_J8+fA@mail.gmail.com/ Link: https://lore.kernel.org/r/20250814-kbuild-werror-v2-6-c01e596309d2@linutronix.de Signed-off-by: Nathan Chancellor --- scripts/Makefile.extrawarn | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'scripts') diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn index 96ff3f5582d6..1434cb6208cb 100644 --- a/scripts/Makefile.extrawarn +++ b/scripts/Makefile.extrawarn @@ -224,3 +224,8 @@ KBUILD_USERLDFLAGS += -Wl,--fatal-warnings KBUILD_RUSTFLAGS += -Dwarnings endif + +# Hostprog flags are used during build bootstrapping and can not rely on CONFIG_ symbols. +KBUILD_HOSTCFLAGS += -Werror +KBUILD_HOSTLDFLAGS += -Wl,--fatal-warnings +KBUILD_HOSTRUSTFLAGS += -Dwarnings -- cgit v1.2.3 From 35883b030c5f19f12c7fe53319c93cb740e476be Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 11 Aug 2025 18:16:48 +0200 Subject: kconfig: nconf: Format and print 'line' without a temporary copy Use "%.*s" as the format specifier and supply the 'line' length 'len' to mvwprintw() to format and print each line without making a temporary copy. Remove the temporary buffer. Signed-off-by: Thorsten Blum Reviewed-by: Nicolas Schier Link: https://lore.kernel.org/r/20250811161650.37428-2-thorsten.blum@linux.dev Signed-off-by: Nathan Chancellor --- scripts/kconfig/nconf.gui.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index 7206437e784a..2d097bc7ef1a 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -173,12 +173,10 @@ void fill_window(WINDOW *win, const char *text) /* do not go over end of line */ total_lines = min(total_lines, y); for (i = 0; i < total_lines; i++) { - char tmp[x+10]; const char *line = get_line(text, i); - int len = get_line_length(line); - strncpy(tmp, line, min(len, x)); - tmp[len] = '\0'; - mvwprintw(win, i, 0, "%s", tmp); + int len = min(get_line_length(line), x); + + mvwprintw(win, i, 0, "%.*s", len, line); } } -- cgit v1.2.3 From 75a6b4595daa569bbc2899eef40372fc013b2d73 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 12 Aug 2025 15:35:02 -0700 Subject: kconfig: qconf/xconfig: show the OptionsMode radio button setting at startup When qconf (xconfig) exits, it saves the current Option settings for Show Name, Show Debug Info, and Show {Normal|All|Prompt} Options. When it is next run, it loads these Option settings from its config file. It correctly shows the flag settings for Show Name and Show Debug Info, but it does not show which of the 3 Show...Options is set. This can lead to confusing output, e.g., if the user thinks that xconfig is in Show All Options mode but kconfig options which have an unmet dependency are still being listed. Add code to show the radio button for the current Show...Options mode during startup so that it will reflect the current config setting. Signed-off-by: Randy Dunlap Tested-by: Nicolas Schier Acked-by: Nicolas Schier Link: https://lore.kernel.org/r/20250812223502.1356426-1-rdunlap@infradead.org Signed-off-by: Nathan Chancellor --- scripts/kconfig/qconf.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'scripts') diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index f8992db1870a..b84c9f2485d1 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -1377,6 +1377,19 @@ ConfigMainWindow::ConfigMainWindow(void) ConfigList::showPromptAction = new QAction("Show Prompt Options", optGroup); ConfigList::showPromptAction->setCheckable(true); + switch (configList->optMode) { + case allOpt: + ConfigList::showAllAction->setChecked(true); + break; + case promptOpt: + ConfigList::showPromptAction->setChecked(true); + break; + case normalOpt: + default: + ConfigList::showNormalAction->setChecked(true); + break; + } + QAction *showDebugAction = new QAction("Show Debug Info", this); showDebugAction->setCheckable(true); connect(showDebugAction, &QAction::toggled, -- cgit v1.2.3 From 4e9a563f0774fdd6f1bd68235314b4b8298fc0a9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 21 Aug 2025 10:16:44 +0200 Subject: scripts: sphinx-pre-install: fix PDF build issues on Ubuntu PDF output with current Debian-based distros require other packages for it to work. The main one is pzdr.tfm. Without that, \sphinxhyphen{} won't work, affecting multiple docs. For CJK, tex-gyre is required. Add the missing packages to the list. After the change, all PDF files build on latest Ubuntu: Ubuntu 25.04: ------------- PASSED: OS detection: Ubuntu 25.04 SKIPPED (Sphinx Sphinx 8.1.3): System packages SKIPPED (Sphinx already installed either as venv or as native package): Sphinx on venv SKIPPED (Sphinx already installed either as venv or as native package): Sphinx package PASSED: Clean documentation: Build time: 0:00, return code: 0 PASSED: Build HTML documentation: Build time: 3:28, return code: 0 PASSED: Build PDF documentation: Build time: 11:08, return code: 0 PDF docs: --------- PASSED: dev-tools: pdf/dev-tools.pdf PASSED: tools: pdf/tools.pdf PASSED: filesystems: pdf/filesystems.pdf PASSED: w1: pdf/w1.pdf PASSED: maintainer: pdf/maintainer.pdf PASSED: process: pdf/process.pdf PASSED: isdn: pdf/isdn.pdf PASSED: fault-injection: pdf/fault-injection.pdf PASSED: iio: pdf/iio.pdf PASSED: scheduler: pdf/scheduler.pdf PASSED: staging: pdf/staging.pdf PASSED: fpga: pdf/fpga.pdf PASSED: power: pdf/power.pdf PASSED: leds: pdf/leds.pdf PASSED: edac: pdf/edac.pdf PASSED: PCI: pdf/PCI.pdf PASSED: firmware-guide: pdf/firmware-guide.pdf PASSED: cpu-freq: pdf/cpu-freq.pdf PASSED: mhi: pdf/mhi.pdf PASSED: wmi: pdf/wmi.pdf PASSED: timers: pdf/timers.pdf PASSED: accel: pdf/accel.pdf PASSED: hid: pdf/hid.pdf PASSED: userspace-api: pdf/userspace-api.pdf PASSED: spi: pdf/spi.pdf PASSED: networking: pdf/networking.pdf PASSED: virt: pdf/virt.pdf PASSED: nvme: pdf/nvme.pdf PASSED: translations: pdf/translations.pdf PASSED: input: pdf/input.pdf PASSED: tee: pdf/tee.pdf PASSED: doc-guide: pdf/doc-guide.pdf PASSED: cdrom: pdf/cdrom.pdf PASSED: gpu: pdf/gpu.pdf PASSED: i2c: pdf/i2c.pdf PASSED: RCU: pdf/RCU.pdf PASSED: watchdog: pdf/watchdog.pdf PASSED: usb: pdf/usb.pdf PASSED: rust: pdf/rust.pdf PASSED: crypto: pdf/crypto.pdf PASSED: kbuild: pdf/kbuild.pdf PASSED: livepatch: pdf/livepatch.pdf PASSED: mm: pdf/mm.pdf PASSED: locking: pdf/locking.pdf PASSED: infiniband: pdf/infiniband.pdf PASSED: driver-api: pdf/driver-api.pdf PASSED: bpf: pdf/bpf.pdf PASSED: devicetree: pdf/devicetree.pdf PASSED: block: pdf/block.pdf PASSED: target: pdf/target.pdf PASSED: arch: pdf/arch.pdf PASSED: pcmcia: pdf/pcmcia.pdf PASSED: scsi: pdf/scsi.pdf PASSED: netlabel: pdf/netlabel.pdf PASSED: sound: pdf/sound.pdf PASSED: security: pdf/security.pdf PASSED: accounting: pdf/accounting.pdf PASSED: admin-guide: pdf/admin-guide.pdf PASSED: core-api: pdf/core-api.pdf PASSED: fb: pdf/fb.pdf PASSED: peci: pdf/peci.pdf PASSED: trace: pdf/trace.pdf PASSED: misc-devices: pdf/misc-devices.pdf PASSED: kernel-hacking: pdf/kernel-hacking.pdf PASSED: hwmon: pdf/hwmon.pdf Reported-by: Akira Yokosawa Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/b5e2e0df68b377b148fdbdd721f6c1cbefe6f861.1755763127.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index b8474848df4e..b24a6f91ec0a 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -764,9 +764,6 @@ class SphinxDependencyChecker(MissingCheckers): if self.pdf: pdf_pkgs = { - "texlive-lang-chinese": [ - "/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty", - ], "fonts-dejavu": [ "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", ], @@ -775,6 +772,15 @@ class SphinxDependencyChecker(MissingCheckers): "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc", ], + "tex-gyre": [ + "/usr/share/texmf/tex/latex/tex-gyre/tgtermes.sty" + ], + "texlive-fonts-recommended": [ + "/usr/share/texlive/texmf-dist/fonts/tfm/adobe/zapfding/pzdr.tfm", + ], + "texlive-lang-chinese": [ + "/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty", + ], } for package, files in pdf_pkgs.items(): -- cgit v1.2.3 From 9ff5c2f51da284906c2f89e7d594f31be69e7abe Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 21 Aug 2025 10:16:45 +0200 Subject: scripts: sphinx-pre-install: add missing gentoo pdf dependencies There are two packages that are required to build PDF at gentoo: dev-texlive/texlive-latexextra media-fonts/lm Place latex_dependencies on a list to make it easier to maintain and add the missing ones. With that, most PDF documents now build on Gentoo: Gentoo Base System release 2.17: -------------------------------- PASSED: OS detection: Gentoo Base System release 2.17 SKIPPED (Sphinx Sphinx 8.2.3): System packages SKIPPED (Sphinx already installed either as venv or as native package): Sphinx on venv SKIPPED (Sphinx already installed either as venv or as native package): Sphinx package PASSED: Clean documentation: Build time: 0:00, return code: 0 PASSED: Build HTML documentation: Build time: 5:28, return code: 0 PARTIAL: Build PDF documentation: Test failed (Build time: 9:19, return code: 2) PDF docs: --------- PASSED: dev-tools: pdf/dev-tools.pdf PASSED: tools: pdf/tools.pdf PASSED: filesystems: pdf/filesystems.pdf PASSED: w1: pdf/w1.pdf PASSED: maintainer: pdf/maintainer.pdf PASSED: process: pdf/process.pdf PASSED: isdn: pdf/isdn.pdf PASSED: fault-injection: pdf/fault-injection.pdf PASSED: iio: pdf/iio.pdf PASSED: scheduler: pdf/scheduler.pdf PASSED: staging: pdf/staging.pdf PASSED: fpga: pdf/fpga.pdf PASSED: power: pdf/power.pdf PASSED: leds: pdf/leds.pdf PASSED: edac: pdf/edac.pdf PASSED: PCI: pdf/PCI.pdf PASSED: firmware-guide: pdf/firmware-guide.pdf PASSED: cpu-freq: pdf/cpu-freq.pdf PASSED: mhi: pdf/mhi.pdf PASSED: wmi: pdf/wmi.pdf PASSED: timers: pdf/timers.pdf PASSED: accel: pdf/accel.pdf PASSED: hid: pdf/hid.pdf FAILED: userspace-api: Build failed (FAILED) PASSED: spi: pdf/spi.pdf PASSED: networking: pdf/networking.pdf PASSED: virt: pdf/virt.pdf PASSED: nvme: pdf/nvme.pdf FAILED: translations: Build failed (FAILED) PASSED: input: pdf/input.pdf PASSED: tee: pdf/tee.pdf PASSED: doc-guide: pdf/doc-guide.pdf PASSED: cdrom: pdf/cdrom.pdf FAILED: gpu: Build failed (FAILED) FAILED: i2c: Build failed (FAILED) FAILED: RCU: Build failed (FAILED) PASSED: watchdog: pdf/watchdog.pdf PASSED: usb: pdf/usb.pdf PASSED: rust: pdf/rust.pdf PASSED: crypto: pdf/crypto.pdf PASSED: kbuild: pdf/kbuild.pdf PASSED: livepatch: pdf/livepatch.pdf PASSED: mm: pdf/mm.pdf PASSED: locking: pdf/locking.pdf PASSED: infiniband: pdf/infiniband.pdf PASSED: driver-api: pdf/driver-api.pdf PASSED: bpf: pdf/bpf.pdf PASSED: devicetree: pdf/devicetree.pdf PASSED: block: pdf/block.pdf PASSED: target: pdf/target.pdf FAILED: arch: Build failed (FAILED) PASSED: pcmcia: pdf/pcmcia.pdf PASSED: scsi: pdf/scsi.pdf PASSED: netlabel: pdf/netlabel.pdf PASSED: sound: pdf/sound.pdf PASSED: security: pdf/security.pdf PASSED: accounting: pdf/accounting.pdf PASSED: admin-guide: pdf/admin-guide.pdf FAILED: core-api: Build failed (FAILED) PASSED: fb: pdf/fb.pdf PASSED: peci: pdf/peci.pdf PASSED: trace: pdf/trace.pdf PASSED: misc-devices: pdf/misc-devices.pdf PASSED: kernel-hacking: pdf/kernel-hacking.pdf PASSED: hwmon: pdf/hwmon.pdf Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/0ac8d6b7484aaf930917c8edde53742d425e7e8f.1755763127.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index b24a6f91ec0a..f987abfec802 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -1058,12 +1058,19 @@ class SphinxDependencyChecker(MissingCheckers): """ Provide package installation hints for Gentoo. """ + texlive_deps = [ + "dev-texlive/texlive-latexextra", + "dev-texlive/texlive-xetex", + "media-fonts/dejavu", + "media-fonts/lm", + ] + progs = { "convert": "media-gfx/imagemagick", "dot": "media-gfx/graphviz", "rsvg-convert": "gnome-base/librsvg", "virtualenv": "dev-python/virtualenv", - "xelatex": "dev-texlive/texlive-xetex media-fonts/dejavu", + "xelatex": " ".join(texlive_deps), "yaml": "dev-python/pyyaml", "python-sphinx": "dev-python/sphinx", } -- cgit v1.2.3 From b2d5d61c1371dc6e0ddda69a0a2c921e3ceb928e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 21 Aug 2025 10:16:46 +0200 Subject: scripts: sphinx-pre-install: fix PDF dependencies for openSuse The dependencies are outdated: both versions need texlive-dejavu fonts. Also, for PDF generation, python311-Sphinx-latex is required. With that, all PDF files are now tuilt on both: openSUSE Leap 15.6: ------------------- PASSED: OS detection: openSUSE Leap 15.6 SKIPPED (Sphinx Sphinx 7.2.6): System packages SKIPPED (Sphinx already installed either as venv or as native package): Sphinx on venv SKIPPED (Sphinx already installed either as venv or as native package): Sphinx package PASSED: Clean documentation: Build time: 0:00, return code: 0 PASSED: Build HTML documentation: Build time: 5:29, return code: 0 PASSED: Build PDF documentation: Build time: 13:45, return code: 0 openSUSE Tumbleweed: -------------------- PASSED: OS detection: openSUSE Tumbleweed SKIPPED (Sphinx Sphinx 8.2.3): System packages SKIPPED (Sphinx already installed either as venv or as native package): Sphinx on venv SKIPPED (Sphinx already installed either as venv or as native package): Sphinx package PASSED: Clean documentation: Build time: 0:00, return code: 0 PASSED: Build HTML documentation: Build time: 4:33, return code: 0 PASSED: Build PDF documentation: Build time: 13:18, return code: 0 Summary ======= PASSED - openSUSE Leap 15.6 (7 tests) PASSED - openSUSE Tumbleweed (7 tests) Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/d78457376f9dfd24cb7ac3a32895c654412715f3.1755763127.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index f987abfec802..86f129c76ecd 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -901,7 +901,7 @@ class SphinxDependencyChecker(MissingCheckers): "dot": "graphviz", "python-sphinx": "python3-sphinx", "virtualenv": "python3-virtualenv", - "xelatex": "texlive-xetex-bin", + "xelatex": "texlive-xetex-bin texlive-dejavu", "yaml": "python3-pyyaml", } @@ -937,7 +937,7 @@ class SphinxDependencyChecker(MissingCheckers): self.recommend_python = True progs.update({ - "python-sphinx": "python311-Sphinx", + "python-sphinx": "python311-Sphinx python311-Sphinx-latex", "virtualenv": "python311-virtualenv", "yaml": "python311-PyYAML", }) @@ -945,7 +945,7 @@ class SphinxDependencyChecker(MissingCheckers): # Tumbleweed defaults to Python 3.11 progs.update({ - "python-sphinx": "python313-Sphinx", + "python-sphinx": "python313-Sphinx python313-Sphinx-latex", "virtualenv": "python313-virtualenv", "yaml": "python313-PyYAML", }) -- cgit v1.2.3 From b51f8c12d16bcf29496ebaf1d7cf3587ca28ba0a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 21 Aug 2025 10:16:47 +0200 Subject: scripts: sphinx-pre-install: fix dependencies for OpenMandriva The dependeny list for OpenMandriva is wrong. Update it. Yet, on my tests with OpenMandriva LX 4.3, the texlive packages are broken: xelatex can't build anything there, as it lacks xelatex.sfm. Yet, this could be a problem at the way I created the container. Just in case, add a note about that. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/669e759ba366328e5c8d5b14a591ba45a1f58176.1755763127.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index 86f129c76ecd..224db3af17db 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -984,13 +984,19 @@ class SphinxDependencyChecker(MissingCheckers): if re.search(r"OpenMandriva", self.system_release): packager_cmd = "dnf install" noto_sans = "noto-sans-cjk-fonts" - tex_pkgs = ["texlive-collection-fontsextra"] + tex_pkgs = [ + "texlive-collection-basic", + "texlive-collection-langcjk", + "texlive-collection-fontsextra", + "texlive-collection-fontsrecommended" + ] # Tested on OpenMandriva Lx 4.3 progs["convert"] = "imagemagick" progs["yaml"] = "python-pyyaml" progs["python-virtualenv"] = "python-virtualenv" progs["python-sphinx"] = "python-sphinx" + progs["xelatex"] = "texlive" self.check_program("python-virtualenv", DepManager.PYTHON_MANDATORY) @@ -1004,7 +1010,9 @@ class SphinxDependencyChecker(MissingCheckers): if not self.distro_msg: self.distro_msg = \ - "Note: for venv, ensurepip could be broken, preventing its install method." + "Notes:\n"\ + "1. for venv, ensurepip could be broken, preventing its install method.\n" \ + "2. at least on OpenMandriva LX 4.3, texlive packages seem broken" else: packager_cmd = "urpmi" -- cgit v1.2.3 From c71c5d6dcb34423c0f15c146240b25a0e78f65aa Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 21 Aug 2025 10:16:48 +0200 Subject: scripts: sphinx-pre-install: fix pdf dependencies for Mageia 9 On Mageia 9, two packages are missing. Add them. With that, all PDF packages now build: Mageia 9: --------- PASSED: OS detection: Mageia 9 PASSED: System packages: Packages installed PASSED: Sphinx on venv: Sphinx Sphinx 8.1.3 PASSED: Sphinx package: Sphinx Sphinx 6.1.3 PASSED: Clean documentation: Build time: 0:00, return code: 0 PASSED: Build HTML documentation: Build time: 5:17, return code: 0 PASSED: Build PDF documentation: Build time: 14:28, return code: 0 PDF docs: --------- PASSED: dev-tools: pdf/dev-tools.pdf PASSED: tools: pdf/tools.pdf PASSED: filesystems: pdf/filesystems.pdf PASSED: w1: pdf/w1.pdf PASSED: maintainer: pdf/maintainer.pdf PASSED: process: pdf/process.pdf PASSED: isdn: pdf/isdn.pdf PASSED: fault-injection: pdf/fault-injection.pdf PASSED: iio: pdf/iio.pdf PASSED: scheduler: pdf/scheduler.pdf PASSED: staging: pdf/staging.pdf PASSED: fpga: pdf/fpga.pdf PASSED: power: pdf/power.pdf PASSED: leds: pdf/leds.pdf PASSED: edac: pdf/edac.pdf PASSED: PCI: pdf/PCI.pdf PASSED: firmware-guide: pdf/firmware-guide.pdf PASSED: cpu-freq: pdf/cpu-freq.pdf PASSED: mhi: pdf/mhi.pdf PASSED: wmi: pdf/wmi.pdf PASSED: timers: pdf/timers.pdf PASSED: accel: pdf/accel.pdf PASSED: hid: pdf/hid.pdf PASSED: userspace-api: pdf/userspace-api.pdf PASSED: spi: pdf/spi.pdf PASSED: networking: pdf/networking.pdf PASSED: virt: pdf/virt.pdf PASSED: nvme: pdf/nvme.pdf PASSED: translations: pdf/translations.pdf PASSED: input: pdf/input.pdf PASSED: tee: pdf/tee.pdf PASSED: doc-guide: pdf/doc-guide.pdf PASSED: cdrom: pdf/cdrom.pdf PASSED: gpu: pdf/gpu.pdf PASSED: i2c: pdf/i2c.pdf PASSED: RCU: pdf/RCU.pdf PASSED: watchdog: pdf/watchdog.pdf PASSED: usb: pdf/usb.pdf PASSED: rust: pdf/rust.pdf PASSED: crypto: pdf/crypto.pdf PASSED: kbuild: pdf/kbuild.pdf PASSED: livepatch: pdf/livepatch.pdf PASSED: mm: pdf/mm.pdf PASSED: locking: pdf/locking.pdf PASSED: infiniband: pdf/infiniband.pdf PASSED: driver-api: pdf/driver-api.pdf PASSED: bpf: pdf/bpf.pdf PASSED: devicetree: pdf/devicetree.pdf PASSED: block: pdf/block.pdf PASSED: target: pdf/target.pdf PASSED: arch: pdf/arch.pdf PASSED: pcmcia: pdf/pcmcia.pdf PASSED: scsi: pdf/scsi.pdf PASSED: netlabel: pdf/netlabel.pdf PASSED: sound: pdf/sound.pdf PASSED: security: pdf/security.pdf PASSED: accounting: pdf/accounting.pdf PASSED: admin-guide: pdf/admin-guide.pdf PASSED: core-api: pdf/core-api.pdf PASSED: fb: pdf/fb.pdf PASSED: peci: pdf/peci.pdf PASSED: trace: pdf/trace.pdf PASSED: misc-devices: pdf/misc-devices.pdf PASSED: kernel-hacking: pdf/kernel-hacking.pdf PASSED: hwmon: pdf/hwmon.pdf Summary ======= PASSED - Mageia 9 (7 tests) Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/bd6e03c79b890ad0168493cdb4cdaf610bbc8c45.1755763127.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 2 ++ 1 file changed, 2 insertions(+) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index 224db3af17db..758a84ae6347 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -979,6 +979,8 @@ class SphinxDependencyChecker(MissingCheckers): tex_pkgs = [ "texlive-fontsextra", + "texlive-fonts-asian", + "fonts-ttf-dejavu", ] if re.search(r"OpenMandriva", self.system_release): -- cgit v1.2.3 From 4509d36ceea288a78bb256416d9ca38d3298c7cb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 21 Aug 2025 10:16:49 +0200 Subject: scripts: sphinx-pre-install: fix PDF dependencies for gentoo Package fonts are wrong. Fix it. With that, most PDF files now builds. PDF docs: --------- PASSED: dev-tools: pdf/dev-tools.pdf PASSED: tools: pdf/tools.pdf PASSED: filesystems: pdf/filesystems.pdf PASSED: w1: pdf/w1.pdf PASSED: maintainer: pdf/maintainer.pdf PASSED: process: pdf/process.pdf PASSED: isdn: pdf/isdn.pdf PASSED: fault-injection: pdf/fault-injection.pdf PASSED: iio: pdf/iio.pdf PASSED: scheduler: pdf/scheduler.pdf PASSED: staging: pdf/staging.pdf PASSED: fpga: pdf/fpga.pdf PASSED: power: pdf/power.pdf PASSED: leds: pdf/leds.pdf PASSED: edac: pdf/edac.pdf PASSED: PCI: pdf/PCI.pdf PASSED: firmware-guide: pdf/firmware-guide.pdf PASSED: cpu-freq: pdf/cpu-freq.pdf PASSED: mhi: pdf/mhi.pdf PASSED: wmi: pdf/wmi.pdf PASSED: timers: pdf/timers.pdf PASSED: accel: pdf/accel.pdf PASSED: hid: pdf/hid.pdf FAILED: userspace-api: Build failed (FAILED) PASSED: spi: pdf/spi.pdf PASSED: networking: pdf/networking.pdf PASSED: virt: pdf/virt.pdf PASSED: nvme: pdf/nvme.pdf FAILED: translations: Build failed (FAILED) PASSED: input: pdf/input.pdf PASSED: tee: pdf/tee.pdf PASSED: doc-guide: pdf/doc-guide.pdf PASSED: cdrom: pdf/cdrom.pdf FAILED: gpu: Build failed (FAILED) FAILED: i2c: Build failed (FAILED) FAILED: RCU: Build failed (FAILED) PASSED: watchdog: pdf/watchdog.pdf PASSED: usb: pdf/usb.pdf PASSED: rust: pdf/rust.pdf PASSED: crypto: pdf/crypto.pdf PASSED: kbuild: pdf/kbuild.pdf PASSED: livepatch: pdf/livepatch.pdf PASSED: mm: pdf/mm.pdf PASSED: locking: pdf/locking.pdf PASSED: infiniband: pdf/infiniband.pdf PASSED: driver-api: pdf/driver-api.pdf PASSED: bpf: pdf/bpf.pdf PASSED: devicetree: pdf/devicetree.pdf PASSED: block: pdf/block.pdf PASSED: target: pdf/target.pdf FAILED: arch: Build failed (FAILED) PASSED: pcmcia: pdf/pcmcia.pdf PASSED: scsi: pdf/scsi.pdf PASSED: netlabel: pdf/netlabel.pdf PASSED: sound: pdf/sound.pdf PASSED: security: pdf/security.pdf PASSED: accounting: pdf/accounting.pdf PASSED: admin-guide: pdf/admin-guide.pdf FAILED: core-api: Build failed (FAILED) PASSED: fb: pdf/fb.pdf PASSED: peci: pdf/peci.pdf PASSED: trace: pdf/trace.pdf PASSED: misc-devices: pdf/misc-devices.pdf PASSED: kernel-hacking: pdf/kernel-hacking.pdf PASSED: hwmon: pdf/hwmon.pdf Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/1ccbac9fd1f4e598dda82e775b64768ec3696248.1755763127.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index 758a84ae6347..c46d7b76f93c 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -1069,10 +1069,10 @@ class SphinxDependencyChecker(MissingCheckers): Provide package installation hints for Gentoo. """ texlive_deps = [ + "dev-texlive/texlive-fontsrecommended", "dev-texlive/texlive-latexextra", "dev-texlive/texlive-xetex", "media-fonts/dejavu", - "media-fonts/lm", ] progs = { -- cgit v1.2.3 From c6e23912855d4848883080200e09551b6dcbc7df Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 21 Aug 2025 10:16:50 +0200 Subject: scripts/sphinx-pre-install: fix Archlinux PDF dependencies There are some missing packages causing PDF build to fail on Archlinux and add latexmk (from texlive-binextra package). Yet, at least today, PDF builds are failing on a very late stage, when trying to run xdvipdfmx: $ xdvipdfmx -E -o "peci.pdf" "peci.xdv" xdvipdfmx:fatal: Unrecognized paper format: # Simply write the paper name. See man 1 paper and "paper --no-size --all" for possible values Despite its message, even using a very simple document like: \def\sphinxdocclass{report} \documentclass[a4paper,11pt,english]{sphinxmanual} \begin{document} Test \end{document} or even: \def\sphinxdocclass{report} \documentclass{sphinxmanual} \begin{document} Test \end{document} Is causing xdvipdfmx to complain about geometry. As Archlinux is a rolling release distro, maybe I got it on a bad day. So, let's fix it in the hope that soon enough someone would fix the issues there. Such broken scenario happens with those packages installed: texlive-basic 2025.2-1 texlive-bin 2025.2-1 texlive-binextra 2025.2-1 texlive-fontsrecommended 2025.2-1 texlive-langchinese 2025.2-1 texlive-langcjk 2025.2-1 texlive-latex 2025.2-1 texlive-latexextra 2025.2-1 texlive-latexrecommended 2025.2-1 texlive-pictures 2025.2-1 texlive-xetex 2025.2-1 python-docutils 1:0.21.2-3 python-sphinx 8.2.3-1 python-sphinx-alabaster-theme 1.0.0-4 python-sphinxcontrib-applehelp 2.0.0-3 python-sphinxcontrib-devhelp 2.0.0-4 python-sphinxcontrib-htmlhelp 2.1.0-3 python-sphinxcontrib-jsmath 1.0.1-19 python-sphinxcontrib-qthelp 2.0.0-3 python-sphinxcontrib-serializinghtml 2.0.0-3 Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/574d902f7691861e18339217f42409850ee58791.1755763127.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index c46d7b76f93c..954ed3dc0645 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -1048,7 +1048,12 @@ class SphinxDependencyChecker(MissingCheckers): } archlinux_tex_pkgs = [ + "texlive-basic", + "texlive-binextra", "texlive-core", + "texlive-fontsrecommended", + "texlive-langchinese", + "texlive-langcjk", "texlive-latexextra", "ttf-dejavu", ] -- cgit v1.2.3 From 0354e81b7bd629f9c3379c9524e988ebc504fa25 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Mon, 25 Aug 2025 17:00:37 +0200 Subject: scripts/misc-check: update export checks for EXPORT_SYMBOL_FOR_MODULES() The module export checks are looking for EXPORT_SYMBOL_GPL_FOR_MODULES() which was renamed to EXPORT_SYMBOL_FOR_MODULES(). Update the checks. Fixes: 6d3c3ca4c77e ("module: Rename EXPORT_SYMBOL_GPL_FOR_MODULES to EXPORT_SYMBOL_FOR_MODULES") Signed-off-by: Vlastimil Babka Reviewed-by: Daniel Gomez Reviewed-by: Nicolas Schier Link: https://lore.kernel.org/r/20250825-export_modules_fix-v1-1-5c331e949538@suse.cz Signed-off-by: Nathan Chancellor --- scripts/misc-check | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/misc-check b/scripts/misc-check index 84f08da17b2c..40e5a4b01ff4 100755 --- a/scripts/misc-check +++ b/scripts/misc-check @@ -45,7 +45,7 @@ check_tracked_ignored_files () { # does not automatically fix it. check_missing_include_linux_export_h () { - git -C "${srctree:-.}" grep --files-with-matches -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_GPL_FOR_MODULES)\(.*\)' \ + git -C "${srctree:-.}" grep --files-with-matches -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_FOR_MODULES)\(.*\)' \ -- '*.[ch]' :^tools/ :^include/linux/export.h | xargs -r git -C "${srctree:-.}" grep --files-without-match '#include[[:space:]]*' | xargs -r printf "%s: warning: EXPORT_SYMBOL() is used, but #include is missing\n" >&2 @@ -58,7 +58,7 @@ check_unnecessary_include_linux_export_h () { git -C "${srctree:-.}" grep --files-with-matches '#include[[:space:]]*' \ -- '*.[c]' :^tools/ | - xargs -r git -C "${srctree:-.}" grep --files-without-match -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_GPL_FOR_MODULES)\(.*\)' | + xargs -r git -C "${srctree:-.}" grep --files-without-match -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_FOR_MODULES)\(.*\)' | xargs -r printf "%s: warning: EXPORT_SYMBOL() is not used, but #include is present\n" >&2 } -- cgit v1.2.3 From 51337a9a3a404fde0f5337662ffc7699793dfeb5 Mon Sep 17 00:00:00 2001 From: Ada Couprie Diaz Date: Thu, 21 Aug 2025 13:07:35 +0100 Subject: kasan: fix GCC mem-intrinsic prefix with sw tags GCC doesn't support "hwasan-kernel-mem-intrinsic-prefix", only "asan-kernel-mem-intrinsic-prefix"[0], while LLVM supports both. This is already taken into account when checking "CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX", but not in the KASAN Makefile adding those parameters when "CONFIG_KASAN_SW_TAGS" is enabled. Replace the version check with "CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX", which already validates that mem-intrinsic prefix parameter can be used, and choose the correct name depending on compiler. GCC 13 and above trigger "CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX" which prevents `mem{cpy,move,set}()` being redefined in "mm/kasan/shadow.c" since commit 36be5cba99f6 ("kasan: treat meminstrinsic as builtins in uninstrumented files"), as we expect the compiler to prefix those calls with `__(hw)asan_` instead. But as the option passed to GCC has been incorrect, the compiler has not been emitting those prefixes, effectively never calling the instrumented versions of `mem{cpy,move,set}()` with "CONFIG_KASAN_SW_TAGS" enabled. If "CONFIG_FORTIFY_SOURCES" is enabled, this issue would be mitigated as it redefines `mem{cpy,move,set}()` and properly aliases the `__underlying_mem*()` that will be called to the instrumented versions. Link: https://lkml.kernel.org/r/20250821120735.156244-1-ada.coupriediaz@arm.com Link: https://gcc.gnu.org/onlinedocs/gcc-13.4.0/gcc/Optimize-Options.html [0] Signed-off-by: Ada Couprie Diaz Fixes: 36be5cba99f6 ("kasan: treat meminstrinsic as builtins in uninstrumented files") Reviewed-by: Yeoreum Yun Cc: Alexander Potapenko Cc: Andrey Konovalov Cc: Andrey Ryabinin Cc: Dmitriy Vyukov Cc: Marco Elver Cc: Marc Rutland Cc: Michael Ellerman Cc: Nathan Chancellor Cc: Vincenzo Frascino Cc: Signed-off-by: Andrew Morton --- scripts/Makefile.kasan | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan index 693dbbebebba..0ba2aac3b8dc 100644 --- a/scripts/Makefile.kasan +++ b/scripts/Makefile.kasan @@ -86,10 +86,14 @@ kasan_params += hwasan-instrument-stack=$(stack_enable) \ hwasan-use-short-granules=0 \ hwasan-inline-all-checks=0 -# Instrument memcpy/memset/memmove calls by using instrumented __hwasan_mem*(). -ifeq ($(call clang-min-version, 150000)$(call gcc-min-version, 130000),y) - kasan_params += hwasan-kernel-mem-intrinsic-prefix=1 -endif +# Instrument memcpy/memset/memmove calls by using instrumented __(hw)asan_mem*(). +ifdef CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX + ifdef CONFIG_CC_IS_GCC + kasan_params += asan-kernel-mem-intrinsic-prefix=1 + else + kasan_params += hwasan-kernel-mem-intrinsic-prefix=1 + endif +endif # CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX endif # CONFIG_KASAN_SW_TAGS -- cgit v1.2.3 From c2a756891bb428104fa8899998ba277042274cdb Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 25 Aug 2025 13:18:28 -0700 Subject: uapi: wrap compiler_types.h in an ifdef instead of the implicit strip The uAPI stddef header includes compiler_types.h, a kernel-only header, to make sure that kernel definitions of annotations like __counted_by() take precedence. There is a hack in scripts/headers_install.sh which strips includes of compiler.h and compiler_types.h when installing uAPI headers. While explicit handling makes sense for compiler.h, which is included all over the uAPI, compiler_types.h is only included by stddef.h (within the uAPI, obviously it's included in kernel code a lot). Remove the stripping from scripts/headers_install.sh and wrap the include of compiler_types.h in #ifdef __KERNEL__ instead. This should be equivalent functionally, but is easier to understand to a casual reader of the code. It also makes it easier to work with kernel headers directly from under tools/ Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Link: https://patch.msgid.link/20250825201828.2370083-1-kuba@kernel.org Signed-off-by: Paolo Abeni --- include/uapi/linux/stddef.h | 2 ++ scripts/headers_install.sh | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/include/uapi/linux/stddef.h b/include/uapi/linux/stddef.h index b87df1b485c2..9a28f7d9a334 100644 --- a/include/uapi/linux/stddef.h +++ b/include/uapi/linux/stddef.h @@ -2,7 +2,9 @@ #ifndef _UAPI_LINUX_STDDEF_H #define _UAPI_LINUX_STDDEF_H +#ifdef __KERNEL__ #include +#endif #ifndef __always_inline #define __always_inline inline diff --git a/scripts/headers_install.sh b/scripts/headers_install.sh index 6bbccb43f7e7..4c20c62c4faf 100755 --- a/scripts/headers_install.sh +++ b/scripts/headers_install.sh @@ -32,7 +32,7 @@ fi sed -E -e ' s/([[:space:](])(__user|__force|__iomem)[[:space:]]/\1/g s/__attribute_const__([[:space:]]|$)/\1/g - s@^#include @@ + s@^#include @@ s/(^|[^a-zA-Z0-9])__packed([^a-zA-Z0-9_]|$)/\1__attribute__((packed))\2/g s/(^|[[:space:](])(inline|asm|volatile)([[:space:](]|$)/\1__\2__\3/g s@#(ifndef|define|endif[[:space:]]*/[*])[[:space:]]*_UAPI@#\1 @ -- cgit v1.2.3 From 1e150869caf2fe540dd9c3367e124b59c23228c7 Mon Sep 17 00:00:00 2001 From: Maxime Thiebaut Date: Fri, 22 Aug 2025 10:40:03 +0200 Subject: extract-vmlinux: Output used decompression method When extract-vmlinux succeeds, it doesn't output which decompression method was found at which offset. Adding this additional output in check_vmlinux() helps troubleshooting and reverse-engineering images. The last check_vmlinux() call was also quoted to accept spaces. Signed-off-by: Maxime Thiebaut Reviewed-by: Nicolas Schier Link: https://lore.kernel.org/r/X6OQ4pHdpreJtlTnf0tFEb4Uxz8T8gFv_7Yw6tpBK4ZBgHYjJr_URwUwCVynpkb-H8Yjk7DdBF01zY-sfqu_7N5trZQfcd6s_4PtdGlHtlA=@thiebaut.dev Signed-off-by: Nathan Chancellor --- scripts/extract-vmlinux | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/extract-vmlinux b/scripts/extract-vmlinux index 189956b5a5c8..266df9bc7a48 100755 --- a/scripts/extract-vmlinux +++ b/scripts/extract-vmlinux @@ -10,12 +10,15 @@ # # ---------------------------------------------------------------------- +me=${0##*/} + check_vmlinux() { if file "$1" | grep -q 'Linux kernel.*boot executable' || readelf -h "$1" > /dev/null 2>&1 then cat "$1" + echo "$me: Extracted vmlinux using '$2' from offset $3" >&2 exit 0 fi } @@ -30,12 +33,11 @@ try_decompress() do pos=${pos%%:*} tail -c+$pos "$img" | $3 > $tmp 2> /dev/null - check_vmlinux $tmp + check_vmlinux $tmp "$3" $pos done } # Check invocation: -me=${0##*/} img=$1 if [ $# -ne 1 -o ! -s "$img" ] then @@ -57,7 +59,7 @@ try_decompress '\002!L\030' xxx 'lz4 -d' try_decompress '(\265/\375' xxx unzstd # Finally check for uncompressed images or objects: -check_vmlinux $img +check_vmlinux "$img" cat 0 # Bail out: echo "$me: Cannot find vmlinux." >&2 -- cgit v1.2.3 From 20c0989283564d662dcc4c77252a60b1812b32ca Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 21 Aug 2025 14:15:38 -0700 Subject: kbuild: Bump minimum version of LLVM for building the kernel to 15.0.0 s390 and x86 have required LLVM 15 since 30d17fac6aae ("scripts/min-tool-version.sh: raise minimum clang version to 15.0.0 for s390") 7861640aac52 ("x86/build: Raise the minimum LLVM version to 15.0.0") respectively but most other architectures allow LLVM 13.0.1 or newer. In accordance with the recent minimum supported version of GCC bump that happened in 118c40b7b503 ("kbuild: require gcc-8 and binutils-2.30") do the same for LLVM to 15.0.0. Of the supported releases of Arch Linux, Debian, Fedora, and OpenSUSE surveyed in evaluating this bump, this only leaves behind Debian Bookworm (14.0.6) and Ubuntu Jammy (14.0.0). Debian Trixie has 19.1.7 and Ubuntu Noble has 18.1.3 (so there are viable upgrade paths) or users can use apt.llvm.org, which provides even newer packages for those distributions. Reviewed-by: Kees Cook Acked-by: Arnd Bergmann Acked-by: Nicolas Schier Link: https://lore.kernel.org/r/20250821-bump-min-llvm-ver-15-v2-1-635f3294e5f0@kernel.org Signed-off-by: Nathan Chancellor --- Documentation/process/changes.rst | 2 +- scripts/min-tool-version.sh | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst index bccfa19b45df..b38622b0d525 100644 --- a/Documentation/process/changes.rst +++ b/Documentation/process/changes.rst @@ -30,7 +30,7 @@ you probably needn't concern yourself with pcmciautils. Program Minimal version Command to check the version ====================== =============== ======================================== GNU C 8.1 gcc --version -Clang/LLVM (optional) 13.0.1 clang --version +Clang/LLVM (optional) 15.0.0 clang --version Rust (optional) 1.78.0 rustc --version bindgen (optional) 0.65.1 bindgen --version GNU make 4.0 make --version diff --git a/scripts/min-tool-version.sh b/scripts/min-tool-version.sh index 0d223b4a9445..99b5575c1ef7 100755 --- a/scripts/min-tool-version.sh +++ b/scripts/min-tool-version.sh @@ -24,12 +24,10 @@ gcc) fi ;; llvm) - if [ "$SRCARCH" = s390 -o "$SRCARCH" = x86 ]; then - echo 15.0.0 - elif [ "$SRCARCH" = loongarch ]; then + if [ "$SRCARCH" = loongarch ]; then echo 18.0.0 else - echo 13.0.1 + echo 15.0.0 fi ;; rustc) -- cgit v1.2.3 From 362f92286065d9f8282da5def89e173a12191568 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 27 Aug 2025 08:11:31 -0700 Subject: lib/crypto: tests: Add KUnit tests for BLAKE2s Add a KUnit test suite for BLAKE2s. Most of the core test logic is in the previously-added hash-test-template.h. This commit just adds the actual KUnit suite, commits the generated test vectors to the tree so that gen-hash-testvecs.py won't have to be run at build time, and adds a few BLAKE2s-specific test cases. This is the replacement for blake2s-selftest, which an earlier commit removed. Improvements over blake2s-selftest include integration with KUnit, more comprehensive test cases, and support for benchmarking. Reviewed-by: Ard Biesheuvel Link: https://lore.kernel.org/r/20250827151131.27733-13-ebiggers@kernel.org Signed-off-by: Eric Biggers --- lib/crypto/tests/Kconfig | 10 ++ lib/crypto/tests/Makefile | 1 + lib/crypto/tests/blake2s-testvecs.h | 238 ++++++++++++++++++++++++++++++++++++ lib/crypto/tests/blake2s_kunit.c | 134 ++++++++++++++++++++ scripts/crypto/gen-hash-testvecs.py | 27 +++- 5 files changed, 407 insertions(+), 3 deletions(-) create mode 100644 lib/crypto/tests/blake2s-testvecs.h create mode 100644 lib/crypto/tests/blake2s_kunit.c (limited to 'scripts') diff --git a/lib/crypto/tests/Kconfig b/lib/crypto/tests/Kconfig index c21d53fd4b0c..fd341aa12f15 100644 --- a/lib/crypto/tests/Kconfig +++ b/lib/crypto/tests/Kconfig @@ -1,5 +1,15 @@ # SPDX-License-Identifier: GPL-2.0-or-later +config CRYPTO_LIB_BLAKE2S_KUNIT_TEST + tristate "KUnit tests for BLAKE2s" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS + select CRYPTO_LIB_BENCHMARK_VISIBLE + # No need to select CRYPTO_LIB_BLAKE2S here, as that option doesn't + # exist; the BLAKE2s code is always built-in for the /dev/random driver. + help + KUnit tests for the BLAKE2s cryptographic hash function. + config CRYPTO_LIB_MD5_KUNIT_TEST tristate "KUnit tests for MD5" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/lib/crypto/tests/Makefile b/lib/crypto/tests/Makefile index f6f82c6f9cb5..be7de929af2c 100644 --- a/lib/crypto/tests/Makefile +++ b/lib/crypto/tests/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later +obj-$(CONFIG_CRYPTO_LIB_BLAKE2S_KUNIT_TEST) += blake2s_kunit.o obj-$(CONFIG_CRYPTO_LIB_MD5_KUNIT_TEST) += md5_kunit.o obj-$(CONFIG_CRYPTO_LIB_POLY1305_KUNIT_TEST) += poly1305_kunit.o obj-$(CONFIG_CRYPTO_LIB_SHA1_KUNIT_TEST) += sha1_kunit.o diff --git a/lib/crypto/tests/blake2s-testvecs.h b/lib/crypto/tests/blake2s-testvecs.h new file mode 100644 index 000000000000..6f978b79a59b --- /dev/null +++ b/lib/crypto/tests/blake2s-testvecs.h @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* This file was generated by: ./scripts/crypto/gen-hash-testvecs.py blake2s */ + +static const struct { + size_t data_len; + u8 digest[BLAKE2S_HASH_SIZE]; +} hash_testvecs[] = { + { + .data_len = 0, + .digest = { + 0x69, 0x21, 0x7a, 0x30, 0x79, 0x90, 0x80, 0x94, + 0xe1, 0x11, 0x21, 0xd0, 0x42, 0x35, 0x4a, 0x7c, + 0x1f, 0x55, 0xb6, 0x48, 0x2c, 0xa1, 0xa5, 0x1e, + 0x1b, 0x25, 0x0d, 0xfd, 0x1e, 0xd0, 0xee, 0xf9, + }, + }, + { + .data_len = 1, + .digest = { + 0x7c, 0xab, 0x53, 0xe2, 0x48, 0x87, 0xdf, 0x64, + 0x98, 0x6a, 0xc1, 0x7e, 0xf0, 0x01, 0x4d, 0xc9, + 0x07, 0x4f, 0xb8, 0x2f, 0x46, 0xd7, 0xee, 0xa9, + 0xad, 0xe5, 0xf8, 0x21, 0xac, 0xfe, 0x17, 0x58, + }, + }, + { + .data_len = 2, + .digest = { + 0x5e, 0x63, 0x2c, 0xd0, 0xf8, 0x7b, 0xf5, 0xae, + 0x61, 0x97, 0x94, 0x57, 0xc8, 0x76, 0x22, 0xd9, + 0x8b, 0x04, 0x5e, 0xf1, 0x5d, 0xd0, 0xfc, 0xd9, + 0x0c, 0x19, 0x2e, 0xe2, 0xc5, 0xd9, 0x73, 0x51, + }, + }, + { + .data_len = 3, + .digest = { + 0x33, 0x65, 0xa6, 0x37, 0xbf, 0xf8, 0x4f, 0x15, + 0x4c, 0xac, 0x9e, 0xa4, 0x3b, 0x02, 0x07, 0x0c, + 0x80, 0x86, 0x0d, 0x6c, 0xe4, 0xaf, 0x1c, 0xbc, + 0x0b, 0x9c, 0x0a, 0x98, 0xc2, 0x99, 0x71, 0xcd, + }, + }, + { + .data_len = 16, + .digest = { + 0x59, 0xd2, 0x10, 0xd3, 0x75, 0xac, 0x48, 0x32, + 0xb1, 0xea, 0xee, 0xcf, 0x0a, 0xd2, 0x8b, 0x15, + 0x5d, 0x72, 0x71, 0x4c, 0xa7, 0x29, 0xb0, 0x7a, + 0x44, 0x48, 0x8a, 0x54, 0x54, 0x54, 0x41, 0xf5, + }, + }, + { + .data_len = 32, + .digest = { + 0xdc, 0xfc, 0x46, 0x81, 0xc6, 0x1b, 0x2b, 0x47, + 0x8b, 0xed, 0xe0, 0x73, 0x34, 0x38, 0x53, 0x92, + 0x97, 0x2f, 0xfb, 0x51, 0xab, 0x4f, 0x2d, 0x9d, + 0x69, 0x04, 0xa9, 0x5d, 0x33, 0xef, 0xcb, 0x1c, + }, + }, + { + .data_len = 48, + .digest = { + 0xd6, 0x2a, 0x7f, 0x96, 0x04, 0x4d, 0x16, 0xc8, + 0x49, 0xe0, 0x37, 0x33, 0xe3, 0x7b, 0x34, 0x56, + 0x99, 0xc5, 0x78, 0x57, 0x06, 0x02, 0xb4, 0xea, + 0x80, 0xc4, 0xf8, 0x8f, 0x8d, 0x2b, 0xe4, 0x05, + }, + }, + { + .data_len = 49, + .digest = { + 0x8b, 0x58, 0x62, 0xb5, 0x85, 0xf6, 0x83, 0x36, + 0xf5, 0x34, 0xb8, 0xd4, 0xbc, 0x5c, 0x8b, 0x38, + 0xfd, 0x15, 0xcd, 0x44, 0x83, 0x25, 0x71, 0xe1, + 0xd5, 0xe8, 0xa1, 0xa4, 0x36, 0x98, 0x7e, 0x68, + }, + }, + { + .data_len = 63, + .digest = { + 0x7e, 0xeb, 0x06, 0x87, 0xdf, 0x1a, 0xdc, 0xe5, + 0xfb, 0x64, 0xd4, 0xd1, 0x5d, 0x9e, 0x75, 0xc0, + 0xb9, 0xad, 0x55, 0x6c, 0xe6, 0xba, 0x4d, 0x98, + 0x2f, 0xbf, 0x72, 0xad, 0x61, 0x37, 0xf6, 0x11, + }, + }, + { + .data_len = 64, + .digest = { + 0x72, 0xdb, 0x43, 0x16, 0x57, 0x8e, 0x3a, 0x96, + 0xf3, 0x98, 0x19, 0x24, 0x17, 0x3b, 0xe8, 0xad, + 0xa1, 0x9b, 0xa4, 0x1b, 0x74, 0x85, 0x2e, 0x24, + 0x70, 0xea, 0x31, 0x5a, 0x1c, 0xbe, 0x43, 0xb5, + }, + }, + { + .data_len = 65, + .digest = { + 0x32, 0x48, 0xb0, 0xf0, 0x3f, 0xbb, 0xd2, 0xa3, + 0xfd, 0xf6, 0x28, 0x4a, 0x2a, 0xc5, 0xbe, 0x4b, + 0x73, 0x50, 0x63, 0xd6, 0x16, 0x00, 0xef, 0xed, + 0xfe, 0x97, 0x41, 0x29, 0xb2, 0x84, 0xc4, 0xa3, + }, + }, + { + .data_len = 127, + .digest = { + 0x17, 0xda, 0x6b, 0x96, 0x6a, 0xa6, 0xa4, 0xa6, + 0xa6, 0xf3, 0x9d, 0x18, 0x19, 0x8d, 0x98, 0x7c, + 0x66, 0x38, 0xe8, 0x99, 0xe7, 0x0a, 0x50, 0x92, + 0xaf, 0x11, 0x80, 0x05, 0x66, 0xed, 0xab, 0x74, + }, + }, + { + .data_len = 128, + .digest = { + 0x13, 0xd5, 0x8b, 0x22, 0xae, 0x90, 0x7b, 0x67, + 0x87, 0x4e, 0x3c, 0x35, 0x4e, 0x01, 0xf0, 0xb1, + 0xd3, 0xd1, 0x67, 0xbb, 0x43, 0xdb, 0x7c, 0x75, + 0xa4, 0xc7, 0x64, 0x83, 0x1e, 0x9b, 0x98, 0xad, + }, + }, + { + .data_len = 129, + .digest = { + 0x6f, 0xe0, 0x5d, 0x9d, 0xd5, 0x78, 0x29, 0xfb, + 0xd0, 0x77, 0xd1, 0x8a, 0xf0, 0x80, 0xcb, 0x81, + 0x71, 0x9e, 0x4d, 0x49, 0xde, 0x74, 0x2a, 0x37, + 0xc0, 0xd5, 0xf0, 0xfa, 0x50, 0xe6, 0x23, 0xfe, + }, + }, + { + .data_len = 256, + .digest = { + 0x89, 0xac, 0xf6, 0xe7, 0x5e, 0xba, 0x53, 0xf4, + 0x92, 0x32, 0xd5, 0x64, 0xfb, 0xc4, 0x08, 0xac, + 0x2c, 0x19, 0x6e, 0x63, 0x13, 0x75, 0xd0, 0x60, + 0x54, 0x35, 0x82, 0xc4, 0x6d, 0x03, 0x1a, 0x05, + }, + }, + { + .data_len = 511, + .digest = { + 0x1c, 0xaf, 0x94, 0x7d, 0x9c, 0xce, 0x57, 0x64, + 0xf8, 0xa8, 0x25, 0x45, 0x32, 0x86, 0x2b, 0x04, + 0xb3, 0x2e, 0x67, 0xca, 0x73, 0x04, 0x2f, 0xab, + 0xcc, 0xda, 0x9e, 0x42, 0xa1, 0xaf, 0x83, 0x5a, + }, + }, + { + .data_len = 513, + .digest = { + 0x21, 0xdf, 0xdc, 0x29, 0xd9, 0xfc, 0x7b, 0xe7, + 0x3a, 0xc4, 0xe1, 0x61, 0xc5, 0xb5, 0xe1, 0xee, + 0x7a, 0x9d, 0x0c, 0x66, 0x36, 0x63, 0xe4, 0x12, + 0x62, 0xe2, 0xf5, 0x68, 0x72, 0xfc, 0x1e, 0x18, + }, + }, + { + .data_len = 1000, + .digest = { + 0x6e, 0xc7, 0x2e, 0xac, 0xd0, 0xbb, 0x22, 0xe0, + 0xc2, 0x40, 0xb2, 0xfe, 0x8c, 0xaf, 0x9e, 0xcf, + 0x32, 0x06, 0xc6, 0x45, 0x29, 0xbd, 0xe0, 0x7f, + 0x53, 0x32, 0xc3, 0x2b, 0x2f, 0x68, 0x12, 0xcd, + }, + }, + { + .data_len = 3333, + .digest = { + 0x76, 0xba, 0x52, 0xb5, 0x09, 0xf5, 0x19, 0x09, + 0x70, 0x1c, 0x09, 0x28, 0xb4, 0xaa, 0x98, 0x6a, + 0x79, 0xe7, 0x5e, 0xcd, 0xe8, 0xa4, 0x73, 0x69, + 0x1f, 0xf8, 0x05, 0x0a, 0xb4, 0xfe, 0xf9, 0x63, + }, + }, + { + .data_len = 4096, + .digest = { + 0xf7, 0xad, 0xf9, 0xc8, 0x0e, 0x04, 0x2f, 0xdf, + 0xbe, 0x39, 0x79, 0x07, 0x0d, 0xd8, 0x1b, 0x06, + 0x42, 0x3a, 0x43, 0x93, 0xf6, 0x7c, 0xc4, 0xe5, + 0xc2, 0xd5, 0xd0, 0xa6, 0x35, 0x6c, 0xbd, 0x17, + }, + }, + { + .data_len = 4128, + .digest = { + 0x38, 0xd7, 0xab, 0x7e, 0x08, 0xdc, 0x1e, 0xab, + 0x55, 0xbb, 0x3b, 0x7b, 0x6a, 0x17, 0xcc, 0x79, + 0xa7, 0x02, 0x62, 0x66, 0x9b, 0xca, 0xee, 0xc0, + 0x3d, 0x75, 0x34, 0x2e, 0x55, 0x82, 0x26, 0x3c, + }, + }, + { + .data_len = 4160, + .digest = { + 0xf7, 0xeb, 0x2f, 0x24, 0x98, 0x54, 0x04, 0x5a, + 0x19, 0xe4, 0x12, 0x9d, 0x97, 0xbc, 0x87, 0xa5, + 0x0b, 0x85, 0x29, 0xa1, 0x36, 0x89, 0xc9, 0xba, + 0xa0, 0xe0, 0xac, 0x99, 0x7d, 0xa4, 0x51, 0x9f, + }, + }, + { + .data_len = 4224, + .digest = { + 0x8f, 0xe8, 0xa7, 0x79, 0x02, 0xbb, 0x4a, 0x56, + 0x66, 0x91, 0xef, 0x22, 0xd1, 0x09, 0x26, 0x6c, + 0xa9, 0x13, 0xd7, 0x44, 0xc7, 0x19, 0x9c, 0x0b, + 0xfb, 0x4f, 0xca, 0x72, 0x8f, 0x34, 0xf7, 0x82, + }, + }, + { + .data_len = 16384, + .digest = { + 0xaa, 0x21, 0xbb, 0x25, 0x4b, 0x66, 0x6e, 0x29, + 0x71, 0xc1, 0x44, 0x67, 0x19, 0xed, 0xe6, 0xe6, + 0x61, 0x13, 0xf4, 0xb7, 0x02, 0x94, 0x81, 0x0f, + 0xa7, 0x4d, 0xbb, 0x2c, 0xb8, 0xeb, 0x41, 0x0e, + }, + }, +}; + +static const u8 hash_testvec_consolidated[BLAKE2S_HASH_SIZE] = { + 0x84, 0x21, 0xbb, 0x73, 0x64, 0x47, 0x45, 0xe0, + 0xc1, 0x83, 0x78, 0xf1, 0xea, 0xe5, 0xfd, 0xdb, + 0x01, 0xda, 0xb7, 0x86, 0x70, 0x3b, 0x83, 0xb3, + 0xbc, 0xd9, 0xfd, 0x96, 0xbd, 0x50, 0x06, 0x67, +}; + +static const u8 blake2s_keyed_testvec_consolidated[BLAKE2S_HASH_SIZE] = { + 0xa6, 0xad, 0xcd, 0xb8, 0xd9, 0xdd, 0xc7, 0x70, + 0x07, 0x09, 0x7f, 0x9f, 0x41, 0xa9, 0x70, 0xa4, + 0x1c, 0xca, 0x61, 0xbb, 0x58, 0xb5, 0xb2, 0x1d, + 0xd1, 0x71, 0x16, 0xb0, 0x49, 0x4f, 0x9e, 0x1b, +}; diff --git a/lib/crypto/tests/blake2s_kunit.c b/lib/crypto/tests/blake2s_kunit.c new file mode 100644 index 000000000000..057c40132246 --- /dev/null +++ b/lib/crypto/tests/blake2s_kunit.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2025 Google LLC + */ +#include +#include "blake2s-testvecs.h" + +/* + * The following are compatibility functions that present BLAKE2s as an unkeyed + * hash function that produces hashes of fixed length BLAKE2S_HASH_SIZE, so that + * hash-test-template.h can be reused to test it. + */ + +static void blake2s_default(const u8 *data, size_t len, + u8 out[BLAKE2S_HASH_SIZE]) +{ + blake2s(out, data, NULL, BLAKE2S_HASH_SIZE, len, 0); +} + +static void blake2s_init_default(struct blake2s_state *state) +{ + blake2s_init(state, BLAKE2S_HASH_SIZE); +} + +/* + * Generate the HASH_KUNIT_CASES using hash-test-template.h. These test BLAKE2s + * with a key length of 0 and a hash length of BLAKE2S_HASH_SIZE. + */ +#define HASH blake2s_default +#define HASH_CTX blake2s_state +#define HASH_SIZE BLAKE2S_HASH_SIZE +#define HASH_INIT blake2s_init_default +#define HASH_UPDATE blake2s_update +#define HASH_FINAL blake2s_final +#include "hash-test-template.h" + +/* + * BLAKE2s specific test case which tests all possible combinations of key + * length and hash length. + */ +static void test_blake2s_all_key_and_hash_lens(struct kunit *test) +{ + const size_t data_len = 100; + u8 *data = &test_buf[0]; + u8 *key = data + data_len; + u8 *hash = key + BLAKE2S_KEY_SIZE; + struct blake2s_state main_state; + u8 main_hash[BLAKE2S_HASH_SIZE]; + + rand_bytes_seeded_from_len(data, data_len); + blake2s_init(&main_state, BLAKE2S_HASH_SIZE); + for (int key_len = 0; key_len <= BLAKE2S_KEY_SIZE; key_len++) { + rand_bytes_seeded_from_len(key, key_len); + for (int out_len = 1; out_len <= BLAKE2S_HASH_SIZE; out_len++) { + blake2s(hash, data, key, out_len, data_len, key_len); + blake2s_update(&main_state, hash, out_len); + } + } + blake2s_final(&main_state, main_hash); + KUNIT_ASSERT_MEMEQ(test, main_hash, blake2s_keyed_testvec_consolidated, + BLAKE2S_HASH_SIZE); +} + +/* + * BLAKE2s specific test case which tests using a guarded buffer for all allowed + * key lengths. Also tests both blake2s() and blake2s_init_key(). + */ +static void test_blake2s_with_guarded_key_buf(struct kunit *test) +{ + const size_t data_len = 100; + + rand_bytes(test_buf, data_len); + for (int key_len = 0; key_len <= BLAKE2S_KEY_SIZE; key_len++) { + u8 key[BLAKE2S_KEY_SIZE]; + u8 *guarded_key = &test_buf[TEST_BUF_LEN - key_len]; + u8 hash1[BLAKE2S_HASH_SIZE]; + u8 hash2[BLAKE2S_HASH_SIZE]; + struct blake2s_state state; + + rand_bytes(key, key_len); + memcpy(guarded_key, key, key_len); + + blake2s(hash1, test_buf, key, + BLAKE2S_HASH_SIZE, data_len, key_len); + blake2s(hash2, test_buf, guarded_key, + BLAKE2S_HASH_SIZE, data_len, key_len); + KUNIT_ASSERT_MEMEQ(test, hash1, hash2, BLAKE2S_HASH_SIZE); + + blake2s_init_key(&state, BLAKE2S_HASH_SIZE, + guarded_key, key_len); + blake2s_update(&state, test_buf, data_len); + blake2s_final(&state, hash2); + KUNIT_ASSERT_MEMEQ(test, hash1, hash2, BLAKE2S_HASH_SIZE); + } +} + +/* + * BLAKE2s specific test case which tests using a guarded output buffer for all + * allowed output lengths. + */ +static void test_blake2s_with_guarded_out_buf(struct kunit *test) +{ + const size_t data_len = 100; + + rand_bytes(test_buf, data_len); + for (int out_len = 1; out_len <= BLAKE2S_HASH_SIZE; out_len++) { + u8 hash[BLAKE2S_HASH_SIZE]; + u8 *guarded_hash = &test_buf[TEST_BUF_LEN - out_len]; + + blake2s(hash, test_buf, NULL, out_len, data_len, 0); + blake2s(guarded_hash, test_buf, NULL, out_len, data_len, 0); + KUNIT_ASSERT_MEMEQ(test, hash, guarded_hash, out_len); + } +} + +static struct kunit_case blake2s_test_cases[] = { + HASH_KUNIT_CASES, + KUNIT_CASE(test_blake2s_all_key_and_hash_lens), + KUNIT_CASE(test_blake2s_with_guarded_key_buf), + KUNIT_CASE(test_blake2s_with_guarded_out_buf), + KUNIT_CASE(benchmark_hash), + {}, +}; + +static struct kunit_suite blake2s_test_suite = { + .name = "blake2s", + .test_cases = blake2s_test_cases, + .suite_init = hash_suite_init, + .suite_exit = hash_suite_exit, +}; +kunit_test_suite(blake2s_test_suite); + +MODULE_DESCRIPTION("KUnit tests and benchmark for BLAKE2s"); +MODULE_LICENSE("GPL"); diff --git a/scripts/crypto/gen-hash-testvecs.py b/scripts/crypto/gen-hash-testvecs.py index 4ac927d40cf5..fc063f2ee95f 100755 --- a/scripts/crypto/gen-hash-testvecs.py +++ b/scripts/crypto/gen-hash-testvecs.py @@ -84,11 +84,16 @@ def print_c_struct_u8_array_field(name, value): print_bytes('\t\t\t', value, 8) print('\t\t},') +def alg_digest_size_const(alg): + if alg == 'blake2s': + return 'BLAKE2S_HASH_SIZE' + return f'{alg.upper()}_DIGEST_SIZE' + def gen_unkeyed_testvecs(alg): print('') print('static const struct {') print('\tsize_t data_len;') - print(f'\tu8 digest[{alg.upper()}_DIGEST_SIZE];') + print(f'\tu8 digest[{alg_digest_size_const(alg)}];') print('} hash_testvecs[] = {') for data_len in DATA_LENS: data = rand_bytes(data_len) @@ -103,7 +108,7 @@ def gen_unkeyed_testvecs(alg): for data_len in range(len(data) + 1): hash_update(ctx, compute_hash(alg, data[:data_len])) print_static_u8_array_definition( - f'hash_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]', + f'hash_testvec_consolidated[{alg_digest_size_const(alg)}]', hash_final(ctx)) def gen_hmac_testvecs(alg): @@ -119,6 +124,20 @@ def gen_hmac_testvecs(alg): f'hmac_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]', ctx.digest()) +BLAKE2S_KEY_SIZE = 32 +BLAKE2S_HASH_SIZE = 32 + +def gen_additional_blake2s_testvecs(): + hashes = b'' + for key_len in range(BLAKE2S_KEY_SIZE + 1): + for out_len in range(1, BLAKE2S_HASH_SIZE + 1): + h = hashlib.blake2s(digest_size=out_len, key=rand_bytes(key_len)) + h.update(rand_bytes(100)) + hashes += h.digest() + print_static_u8_array_definition( + 'blake2s_keyed_testvec_consolidated[BLAKE2S_HASH_SIZE]', + compute_hash('blake2s', hashes)) + def gen_additional_poly1305_testvecs(): key = b'\xff' * POLY1305_KEY_SIZE data = b'' @@ -141,7 +160,9 @@ alg = sys.argv[1] print('/* SPDX-License-Identifier: GPL-2.0-or-later */') print(f'/* This file was generated by: {sys.argv[0]} {" ".join(sys.argv[1:])} */') gen_unkeyed_testvecs(alg) -if alg == 'poly1305': +if alg == 'blake2s': + gen_additional_blake2s_testvecs() +elif alg == 'poly1305': gen_additional_poly1305_testvecs() else: gen_hmac_testvecs(alg) -- cgit v1.2.3 From 8a298579cdfcfdbcb70bb7af5e30769387494f37 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 22 Aug 2025 16:19:35 +0200 Subject: scripts: sphinx-build-wrapper: get rid of uapi/media Makefile Now that kernel-include directive supports parsing data structs directly, we can finally get rid of the horrible hack we added to support parsing media uAPI symbols. As a side effect, Documentation/output doesn't have anymore media auto-generated .rst files on it. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/5dbb257a4b283697271c9c7b8f4713857e8191c8.1755872208.git.mchehab+huawei@kernel.org --- Documentation/Makefile | 3 +- Documentation/userspace-api/media/Makefile | 64 -- .../userspace-api/media/ca.h.rst.exceptions | 25 - .../userspace-api/media/cec.h.rst.exceptions | 577 ----------------- .../userspace-api/media/cec/cec-header.rst | 5 +- .../userspace-api/media/cec/cec.h.rst.exceptions | 577 +++++++++++++++++ .../userspace-api/media/dmx.h.rst.exceptions | 66 -- .../userspace-api/media/dvb/ca.h.rst.exceptions | 25 + .../userspace-api/media/dvb/dmx.h.rst.exceptions | 66 ++ .../media/dvb/frontend.h.rst.exceptions | 246 +++++++ Documentation/userspace-api/media/dvb/headers.rst | 17 +- .../userspace-api/media/dvb/net.h.rst.exceptions | 13 + .../userspace-api/media/frontend.h.rst.exceptions | 246 ------- .../userspace-api/media/lirc.h.rst.exceptions | 87 --- .../userspace-api/media/media.h.rst.exceptions | 32 - .../userspace-api/media/mediactl/media-header.rst | 5 +- .../media/mediactl/media.h.rst.exceptions | 32 + .../userspace-api/media/net.h.rst.exceptions | 13 - .../userspace-api/media/rc/lirc-header.rst | 4 +- .../userspace-api/media/rc/lirc.h.rst.exceptions | 87 +++ Documentation/userspace-api/media/v4l/videodev.rst | 4 +- .../media/v4l/videodev2.h.rst.exceptions | 610 +++++++++++++++++ .../userspace-api/media/videodev2.h.rst.exceptions | 610 ----------------- scripts/sphinx-build-wrapper | 719 +++++++++++++++++++++ 24 files changed, 2401 insertions(+), 1732 deletions(-) delete mode 100644 Documentation/userspace-api/media/Makefile delete mode 100644 Documentation/userspace-api/media/ca.h.rst.exceptions delete mode 100644 Documentation/userspace-api/media/cec.h.rst.exceptions create mode 100644 Documentation/userspace-api/media/cec/cec.h.rst.exceptions delete mode 100644 Documentation/userspace-api/media/dmx.h.rst.exceptions create mode 100644 Documentation/userspace-api/media/dvb/ca.h.rst.exceptions create mode 100644 Documentation/userspace-api/media/dvb/dmx.h.rst.exceptions create mode 100644 Documentation/userspace-api/media/dvb/frontend.h.rst.exceptions create mode 100644 Documentation/userspace-api/media/dvb/net.h.rst.exceptions delete mode 100644 Documentation/userspace-api/media/frontend.h.rst.exceptions delete mode 100644 Documentation/userspace-api/media/lirc.h.rst.exceptions delete mode 100644 Documentation/userspace-api/media/media.h.rst.exceptions create mode 100644 Documentation/userspace-api/media/mediactl/media.h.rst.exceptions delete mode 100644 Documentation/userspace-api/media/net.h.rst.exceptions create mode 100644 Documentation/userspace-api/media/rc/lirc.h.rst.exceptions create mode 100644 Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions delete mode 100644 Documentation/userspace-api/media/videodev2.h.rst.exceptions create mode 100755 scripts/sphinx-build-wrapper (limited to 'scripts') diff --git a/Documentation/Makefile b/Documentation/Makefile index 2ed334971acd..5c20c68be89a 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -87,7 +87,7 @@ loop_cmd = $(echo-cmd) $(cmd_$(1)) || exit; PYTHONPYCACHEPREFIX ?= $(abspath $(BUILDDIR)/__pycache__) quiet_cmd_sphinx = SPHINX $@ --> file://$(abspath $(BUILDDIR)/$3/$4) - cmd_sphinx = $(MAKE) BUILDDIR=$(abspath $(BUILDDIR)) $(build)=Documentation/userspace-api/media $2 && \ + cmd_sphinx = \ PYTHONPYCACHEPREFIX="$(PYTHONPYCACHEPREFIX)" \ BUILDDIR=$(abspath $(BUILDDIR)) SPHINX_CONF=$(abspath $(src)/$5/$(SPHINX_CONF)) \ $(PYTHON3) $(srctree)/scripts/jobserver-exec \ @@ -171,7 +171,6 @@ refcheckdocs: cleandocs: $(Q)rm -rf $(BUILDDIR) - $(Q)$(MAKE) BUILDDIR=$(abspath $(BUILDDIR)) $(build)=Documentation/userspace-api/media clean dochelp: @echo ' Linux kernel internal documentation in different formats from ReST:' diff --git a/Documentation/userspace-api/media/Makefile b/Documentation/userspace-api/media/Makefile deleted file mode 100644 index accc734d045a..000000000000 --- a/Documentation/userspace-api/media/Makefile +++ /dev/null @@ -1,64 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -# Rules to convert a .h file to inline RST documentation - -SRC_DIR=$(srctree)/Documentation/userspace-api/media -PARSER = $(srctree)/tools/docs/parse-headers.py -UAPI = $(srctree)/include/uapi/linux -KAPI = $(srctree)/include/linux - -FILES = ca.h.rst dmx.h.rst frontend.h.rst net.h.rst \ - videodev2.h.rst media.h.rst cec.h.rst lirc.h.rst - -TARGETS := $(addprefix $(BUILDDIR)/, $(FILES)) - -gen_rst = \ - echo ${PARSER} $< $@ $(SRC_DIR)/$(notdir $@).exceptions; \ - ${PARSER} $< $@ $(SRC_DIR)/$(notdir $@).exceptions - -quiet_gen_rst = echo ' PARSE $(patsubst $(srctree)/%,%,$<)'; \ - ${PARSER} $< $@ $(SRC_DIR)/$(notdir $@).exceptions - -silent_gen_rst = ${gen_rst} - -$(BUILDDIR)/ca.h.rst: ${UAPI}/dvb/ca.h ${PARSER} $(SRC_DIR)/ca.h.rst.exceptions - @$($(quiet)gen_rst) - -$(BUILDDIR)/dmx.h.rst: ${UAPI}/dvb/dmx.h ${PARSER} $(SRC_DIR)/dmx.h.rst.exceptions - @$($(quiet)gen_rst) - -$(BUILDDIR)/frontend.h.rst: ${UAPI}/dvb/frontend.h ${PARSER} $(SRC_DIR)/frontend.h.rst.exceptions - @$($(quiet)gen_rst) - -$(BUILDDIR)/net.h.rst: ${UAPI}/dvb/net.h ${PARSER} $(SRC_DIR)/net.h.rst.exceptions - @$($(quiet)gen_rst) - -$(BUILDDIR)/videodev2.h.rst: ${UAPI}/videodev2.h ${PARSER} $(SRC_DIR)/videodev2.h.rst.exceptions - @$($(quiet)gen_rst) - -$(BUILDDIR)/media.h.rst: ${UAPI}/media.h ${PARSER} $(SRC_DIR)/media.h.rst.exceptions - @$($(quiet)gen_rst) - -$(BUILDDIR)/cec.h.rst: ${UAPI}/cec.h ${PARSER} $(SRC_DIR)/cec.h.rst.exceptions - @$($(quiet)gen_rst) - -$(BUILDDIR)/lirc.h.rst: ${UAPI}/lirc.h ${PARSER} $(SRC_DIR)/lirc.h.rst.exceptions - @$($(quiet)gen_rst) - -# Media build rules - -.PHONY: all html texinfo epub xml latex - -all: $(IMGDOT) $(BUILDDIR) ${TARGETS} -html: all -texinfo: all -epub: all -xml: all -latex: $(IMGPDF) all -linkcheck: - -clean: - -rm -f $(DOTTGT) $(IMGTGT) ${TARGETS} 2>/dev/null - -$(BUILDDIR): - $(Q)mkdir -p $@ diff --git a/Documentation/userspace-api/media/ca.h.rst.exceptions b/Documentation/userspace-api/media/ca.h.rst.exceptions deleted file mode 100644 index f6828238eb48..000000000000 --- a/Documentation/userspace-api/media/ca.h.rst.exceptions +++ /dev/null @@ -1,25 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -# Ignore header name -ignore define _DVBCA_H_ - -# struct ca_slot_info defines -replace define CA_CI :c:type:`ca_slot_info` -replace define CA_CI_LINK :c:type:`ca_slot_info` -replace define CA_CI_PHYS :c:type:`ca_slot_info` -replace define CA_DESCR :c:type:`ca_slot_info` -replace define CA_SC :c:type:`ca_slot_info` -replace define CA_CI_MODULE_PRESENT :c:type:`ca_slot_info` -replace define CA_CI_MODULE_READY :c:type:`ca_slot_info` - -# struct ca_descr_info defines -replace define CA_ECD :c:type:`ca_descr_info` -replace define CA_NDS :c:type:`ca_descr_info` -replace define CA_DSS :c:type:`ca_descr_info` - -# some typedefs should point to struct/enums -replace typedef ca_slot_info_t :c:type:`ca_slot_info` -replace typedef ca_descr_info_t :c:type:`ca_descr_info` -replace typedef ca_caps_t :c:type:`ca_caps` -replace typedef ca_msg_t :c:type:`ca_msg` -replace typedef ca_descr_t :c:type:`ca_descr` diff --git a/Documentation/userspace-api/media/cec.h.rst.exceptions b/Documentation/userspace-api/media/cec.h.rst.exceptions deleted file mode 100644 index 15fa1752d4ef..000000000000 --- a/Documentation/userspace-api/media/cec.h.rst.exceptions +++ /dev/null @@ -1,577 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -# Ignore header name -ignore define _CEC_UAPI_H - -# define macros to ignore - -ignore define CEC_MAX_MSG_SIZE -ignore define CEC_MAX_LOG_ADDRS - -ignore define CEC_LOG_ADDR_MASK_TV -ignore define CEC_LOG_ADDR_MASK_RECORD -ignore define CEC_LOG_ADDR_MASK_TUNER -ignore define CEC_LOG_ADDR_MASK_PLAYBACK -ignore define CEC_LOG_ADDR_MASK_AUDIOSYSTEM -ignore define CEC_LOG_ADDR_MASK_BACKUP -ignore define CEC_LOG_ADDR_MASK_SPECIFIC -ignore define CEC_LOG_ADDR_MASK_UNREGISTERED - -# Shouldn't them be documented? -ignore define CEC_LOG_ADDR_INVALID -ignore define CEC_PHYS_ADDR_INVALID - -ignore define CEC_VENDOR_ID_NONE - -ignore define CEC_MODE_INITIATOR_MSK -ignore define CEC_MODE_FOLLOWER_MSK - -# Part of CEC 2.0 spec - shouldn't be documented too? -ignore define CEC_LOG_ADDR_TV -ignore define CEC_LOG_ADDR_RECORD_1 -ignore define CEC_LOG_ADDR_RECORD_2 -ignore define CEC_LOG_ADDR_TUNER_1 -ignore define CEC_LOG_ADDR_PLAYBACK_1 -ignore define CEC_LOG_ADDR_AUDIOSYSTEM -ignore define CEC_LOG_ADDR_TUNER_2 -ignore define CEC_LOG_ADDR_TUNER_3 -ignore define CEC_LOG_ADDR_PLAYBACK_2 -ignore define CEC_LOG_ADDR_RECORD_3 -ignore define CEC_LOG_ADDR_TUNER_4 -ignore define CEC_LOG_ADDR_PLAYBACK_3 -ignore define CEC_LOG_ADDR_BACKUP_1 -ignore define CEC_LOG_ADDR_BACKUP_2 -ignore define CEC_LOG_ADDR_SPECIFIC -ignore define CEC_LOG_ADDR_UNREGISTERED -ignore define CEC_LOG_ADDR_BROADCAST - -# IMHO, those should also be documented - -ignore define CEC_MSG_ACTIVE_SOURCE -ignore define CEC_MSG_IMAGE_VIEW_ON -ignore define CEC_MSG_TEXT_VIEW_ON - -ignore define CEC_MSG_INACTIVE_SOURCE -ignore define CEC_MSG_REQUEST_ACTIVE_SOURCE -ignore define CEC_MSG_ROUTING_CHANGE -ignore define CEC_MSG_ROUTING_INFORMATION -ignore define CEC_MSG_SET_STREAM_PATH - -ignore define CEC_MSG_STANDBY - -ignore define CEC_MSG_RECORD_OFF -ignore define CEC_MSG_RECORD_ON - -ignore define CEC_OP_RECORD_SRC_OWN -ignore define CEC_OP_RECORD_SRC_DIGITAL -ignore define CEC_OP_RECORD_SRC_ANALOG -ignore define CEC_OP_RECORD_SRC_EXT_PLUG -ignore define CEC_OP_RECORD_SRC_EXT_PHYS_ADDR - -ignore define CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID -ignore define CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL - -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2 -ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T - -ignore define CEC_OP_ANA_BCAST_TYPE_CABLE -ignore define CEC_OP_ANA_BCAST_TYPE_SATELLITE -ignore define CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL - -ignore define CEC_OP_BCAST_SYSTEM_PAL_BG -ignore define CEC_OP_BCAST_SYSTEM_SECAM_LQ -ignore define CEC_OP_BCAST_SYSTEM_PAL_M -ignore define CEC_OP_BCAST_SYSTEM_NTSC_M -ignore define CEC_OP_BCAST_SYSTEM_PAL_I -ignore define CEC_OP_BCAST_SYSTEM_SECAM_DK -ignore define CEC_OP_BCAST_SYSTEM_SECAM_BG -ignore define CEC_OP_BCAST_SYSTEM_SECAM_L -ignore define CEC_OP_BCAST_SYSTEM_PAL_DK -ignore define CEC_OP_BCAST_SYSTEM_OTHER - -ignore define CEC_OP_CHANNEL_NUMBER_FMT_1_PART -ignore define CEC_OP_CHANNEL_NUMBER_FMT_2_PART - -ignore define CEC_MSG_RECORD_STATUS - -ignore define CEC_OP_RECORD_STATUS_CUR_SRC -ignore define CEC_OP_RECORD_STATUS_DIG_SERVICE -ignore define CEC_OP_RECORD_STATUS_ANA_SERVICE -ignore define CEC_OP_RECORD_STATUS_EXT_INPUT -ignore define CEC_OP_RECORD_STATUS_NO_DIG_SERVICE -ignore define CEC_OP_RECORD_STATUS_NO_ANA_SERVICE -ignore define CEC_OP_RECORD_STATUS_NO_SERVICE -ignore define CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG -ignore define CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR -ignore define CEC_OP_RECORD_STATUS_UNSUP_CA -ignore define CEC_OP_RECORD_STATUS_NO_CA_ENTITLEMENTS -ignore define CEC_OP_RECORD_STATUS_CANT_COPY_SRC -ignore define CEC_OP_RECORD_STATUS_NO_MORE_COPIES -ignore define CEC_OP_RECORD_STATUS_NO_MEDIA -ignore define CEC_OP_RECORD_STATUS_PLAYING -ignore define CEC_OP_RECORD_STATUS_ALREADY_RECORDING -ignore define CEC_OP_RECORD_STATUS_MEDIA_PROT -ignore define CEC_OP_RECORD_STATUS_NO_SIGNAL -ignore define CEC_OP_RECORD_STATUS_MEDIA_PROBLEM -ignore define CEC_OP_RECORD_STATUS_NO_SPACE -ignore define CEC_OP_RECORD_STATUS_PARENTAL_LOCK -ignore define CEC_OP_RECORD_STATUS_TERMINATED_OK -ignore define CEC_OP_RECORD_STATUS_ALREADY_TERM -ignore define CEC_OP_RECORD_STATUS_OTHER - -ignore define CEC_MSG_RECORD_TV_SCREEN - -ignore define CEC_MSG_CLEAR_ANALOGUE_TIMER - -ignore define CEC_OP_REC_SEQ_SUNDAY -ignore define CEC_OP_REC_SEQ_MONDAY -ignore define CEC_OP_REC_SEQ_TUESDAY -ignore define CEC_OP_REC_SEQ_WEDNESDAY -ignore define CEC_OP_REC_SEQ_THURSDAY -ignore define CEC_OP_REC_SEQ_FRIDAY -ignore define CEC_OP_REC_SEQ_SATURDAY -ignore define CEC_OP_REC_SEQ_ONCE_ONLY - -ignore define CEC_MSG_CLEAR_DIGITAL_TIMER - -ignore define CEC_MSG_CLEAR_EXT_TIMER - -ignore define CEC_OP_EXT_SRC_PLUG -ignore define CEC_OP_EXT_SRC_PHYS_ADDR - -ignore define CEC_MSG_SET_ANALOGUE_TIMER -ignore define CEC_MSG_SET_DIGITAL_TIMER -ignore define CEC_MSG_SET_EXT_TIMER - -ignore define CEC_MSG_SET_TIMER_PROGRAM_TITLE -ignore define CEC_MSG_TIMER_CLEARED_STATUS - -ignore define CEC_OP_TIMER_CLR_STAT_RECORDING -ignore define CEC_OP_TIMER_CLR_STAT_NO_MATCHING -ignore define CEC_OP_TIMER_CLR_STAT_NO_INFO -ignore define CEC_OP_TIMER_CLR_STAT_CLEARED - -ignore define CEC_MSG_TIMER_STATUS - -ignore define CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP -ignore define CEC_OP_TIMER_OVERLAP_WARNING_OVERLAP - -ignore define CEC_OP_MEDIA_INFO_UNPROT_MEDIA -ignore define CEC_OP_MEDIA_INFO_PROT_MEDIA -ignore define CEC_OP_MEDIA_INFO_NO_MEDIA - -ignore define CEC_OP_PROG_IND_NOT_PROGRAMMED -ignore define CEC_OP_PROG_IND_PROGRAMMED - -ignore define CEC_OP_PROG_INFO_ENOUGH_SPACE -ignore define CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE -ignore define CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE -ignore define CEC_OP_PROG_INFO_NONE_AVAILABLE - -ignore define CEC_OP_PROG_ERROR_NO_FREE_TIMER -ignore define CEC_OP_PROG_ERROR_DATE_OUT_OF_RANGE -ignore define CEC_OP_PROG_ERROR_REC_SEQ_ERROR -ignore define CEC_OP_PROG_ERROR_INV_EXT_PLUG -ignore define CEC_OP_PROG_ERROR_INV_EXT_PHYS_ADDR -ignore define CEC_OP_PROG_ERROR_CA_UNSUPP -ignore define CEC_OP_PROG_ERROR_INSUF_CA_ENTITLEMENTS -ignore define CEC_OP_PROG_ERROR_RESOLUTION_UNSUPP -ignore define CEC_OP_PROG_ERROR_PARENTAL_LOCK -ignore define CEC_OP_PROG_ERROR_CLOCK_FAILURE -ignore define CEC_OP_PROG_ERROR_DUPLICATE - -ignore define CEC_MSG_CEC_VERSION - -ignore define CEC_OP_CEC_VERSION_1_3A -ignore define CEC_OP_CEC_VERSION_1_4 -ignore define CEC_OP_CEC_VERSION_2_0 - -ignore define CEC_MSG_GET_CEC_VERSION -ignore define CEC_MSG_GIVE_PHYSICAL_ADDR -ignore define CEC_MSG_GET_MENU_LANGUAGE -ignore define CEC_MSG_REPORT_PHYSICAL_ADDR - -ignore define CEC_OP_PRIM_DEVTYPE_TV -ignore define CEC_OP_PRIM_DEVTYPE_RECORD -ignore define CEC_OP_PRIM_DEVTYPE_TUNER -ignore define CEC_OP_PRIM_DEVTYPE_PLAYBACK -ignore define CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM -ignore define CEC_OP_PRIM_DEVTYPE_SWITCH -ignore define CEC_OP_PRIM_DEVTYPE_PROCESSOR - -ignore define CEC_MSG_SET_MENU_LANGUAGE -ignore define CEC_MSG_REPORT_FEATURES - -ignore define CEC_OP_ALL_DEVTYPE_TV -ignore define CEC_OP_ALL_DEVTYPE_RECORD -ignore define CEC_OP_ALL_DEVTYPE_TUNER -ignore define CEC_OP_ALL_DEVTYPE_PLAYBACK -ignore define CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM -ignore define CEC_OP_ALL_DEVTYPE_SWITCH - -ignore define CEC_OP_FEAT_EXT - -ignore define CEC_OP_FEAT_RC_TV_PROFILE_NONE -ignore define CEC_OP_FEAT_RC_TV_PROFILE_1 -ignore define CEC_OP_FEAT_RC_TV_PROFILE_2 -ignore define CEC_OP_FEAT_RC_TV_PROFILE_3 -ignore define CEC_OP_FEAT_RC_TV_PROFILE_4 -ignore define CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU -ignore define CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU -ignore define CEC_OP_FEAT_RC_SRC_HAS_CONTENTS_MENU -ignore define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU -ignore define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU - -ignore define CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN -ignore define CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING -ignore define CEC_OP_FEAT_DEV_HAS_DECK_CONTROL -ignore define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE -ignore define CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX -ignore define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX -ignore define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_VOLUME_LEVEL - -ignore define CEC_MSG_GIVE_FEATURES - -ignore define CEC_MSG_DECK_CONTROL - -ignore define CEC_OP_DECK_CTL_MODE_SKIP_FWD -ignore define CEC_OP_DECK_CTL_MODE_SKIP_REV -ignore define CEC_OP_DECK_CTL_MODE_STOP -ignore define CEC_OP_DECK_CTL_MODE_EJECT - -ignore define CEC_MSG_DECK_STATUS - -ignore define CEC_OP_DECK_INFO_PLAY -ignore define CEC_OP_DECK_INFO_RECORD -ignore define CEC_OP_DECK_INFO_PLAY_REV -ignore define CEC_OP_DECK_INFO_STILL -ignore define CEC_OP_DECK_INFO_SLOW -ignore define CEC_OP_DECK_INFO_SLOW_REV -ignore define CEC_OP_DECK_INFO_FAST_FWD -ignore define CEC_OP_DECK_INFO_FAST_REV -ignore define CEC_OP_DECK_INFO_NO_MEDIA -ignore define CEC_OP_DECK_INFO_STOP -ignore define CEC_OP_DECK_INFO_SKIP_FWD -ignore define CEC_OP_DECK_INFO_SKIP_REV -ignore define CEC_OP_DECK_INFO_INDEX_SEARCH_FWD -ignore define CEC_OP_DECK_INFO_INDEX_SEARCH_REV -ignore define CEC_OP_DECK_INFO_OTHER - -ignore define CEC_MSG_GIVE_DECK_STATUS - -ignore define CEC_OP_STATUS_REQ_ON -ignore define CEC_OP_STATUS_REQ_OFF -ignore define CEC_OP_STATUS_REQ_ONCE - -ignore define CEC_MSG_PLAY - -ignore define CEC_OP_PLAY_MODE_PLAY_FWD -ignore define CEC_OP_PLAY_MODE_PLAY_REV -ignore define CEC_OP_PLAY_MODE_PLAY_STILL -ignore define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN -ignore define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED -ignore define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX -ignore define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN -ignore define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED -ignore define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX -ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN -ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED -ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX -ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN -ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED -ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX - -ignore define CEC_MSG_GIVE_TUNER_DEVICE_STATUS -ignore define CEC_MSG_SELECT_ANALOGUE_SERVICE -ignore define CEC_MSG_SELECT_DIGITAL_SERVICE -ignore define CEC_MSG_TUNER_DEVICE_STATUS - -ignore define CEC_OP_REC_FLAG_USED -ignore define CEC_OP_REC_FLAG_NOT_USED - -ignore define CEC_OP_TUNER_DISPLAY_INFO_DIGITAL -ignore define CEC_OP_TUNER_DISPLAY_INFO_NONE -ignore define CEC_OP_TUNER_DISPLAY_INFO_ANALOGUE - -ignore define CEC_MSG_TUNER_STEP_DECREMENT -ignore define CEC_MSG_TUNER_STEP_INCREMENT - -ignore define CEC_MSG_DEVICE_VENDOR_ID -ignore define CEC_MSG_GIVE_DEVICE_VENDOR_ID -ignore define CEC_MSG_VENDOR_COMMAND -ignore define CEC_MSG_VENDOR_COMMAND_WITH_ID -ignore define CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN -ignore define CEC_MSG_VENDOR_REMOTE_BUTTON_UP - -ignore define CEC_MSG_SET_OSD_STRING - -ignore define CEC_OP_DISP_CTL_DEFAULT -ignore define CEC_OP_DISP_CTL_UNTIL_CLEARED -ignore define CEC_OP_DISP_CTL_CLEAR - -ignore define CEC_MSG_GIVE_OSD_NAME -ignore define CEC_MSG_SET_OSD_NAME - -ignore define CEC_MSG_MENU_REQUEST - -ignore define CEC_OP_MENU_REQUEST_ACTIVATE -ignore define CEC_OP_MENU_REQUEST_DEACTIVATE -ignore define CEC_OP_MENU_REQUEST_QUERY - -ignore define CEC_MSG_MENU_STATUS - -ignore define CEC_OP_MENU_STATE_ACTIVATED -ignore define CEC_OP_MENU_STATE_DEACTIVATED - -ignore define CEC_MSG_USER_CONTROL_PRESSED - -ignore define CEC_OP_UI_CMD_SELECT -ignore define CEC_OP_UI_CMD_UP -ignore define CEC_OP_UI_CMD_DOWN -ignore define CEC_OP_UI_CMD_LEFT -ignore define CEC_OP_UI_CMD_RIGHT -ignore define CEC_OP_UI_CMD_RIGHT_UP -ignore define CEC_OP_UI_CMD_RIGHT_DOWN -ignore define CEC_OP_UI_CMD_LEFT_UP -ignore define CEC_OP_UI_CMD_LEFT_DOWN -ignore define CEC_OP_UI_CMD_DEVICE_ROOT_MENU -ignore define CEC_OP_UI_CMD_DEVICE_SETUP_MENU -ignore define CEC_OP_UI_CMD_CONTENTS_MENU -ignore define CEC_OP_UI_CMD_FAVORITE_MENU -ignore define CEC_OP_UI_CMD_BACK -ignore define CEC_OP_UI_CMD_MEDIA_TOP_MENU -ignore define CEC_OP_UI_CMD_MEDIA_CONTEXT_SENSITIVE_MENU -ignore define CEC_OP_UI_CMD_NUMBER_ENTRY_MODE -ignore define CEC_OP_UI_CMD_NUMBER_11 -ignore define CEC_OP_UI_CMD_NUMBER_12 -ignore define CEC_OP_UI_CMD_NUMBER_0_OR_NUMBER_10 -ignore define CEC_OP_UI_CMD_NUMBER_1 -ignore define CEC_OP_UI_CMD_NUMBER_2 -ignore define CEC_OP_UI_CMD_NUMBER_3 -ignore define CEC_OP_UI_CMD_NUMBER_4 -ignore define CEC_OP_UI_CMD_NUMBER_5 -ignore define CEC_OP_UI_CMD_NUMBER_6 -ignore define CEC_OP_UI_CMD_NUMBER_7 -ignore define CEC_OP_UI_CMD_NUMBER_8 -ignore define CEC_OP_UI_CMD_NUMBER_9 -ignore define CEC_OP_UI_CMD_DOT -ignore define CEC_OP_UI_CMD_ENTER -ignore define CEC_OP_UI_CMD_CLEAR -ignore define CEC_OP_UI_CMD_NEXT_FAVORITE -ignore define CEC_OP_UI_CMD_CHANNEL_UP -ignore define CEC_OP_UI_CMD_CHANNEL_DOWN -ignore define CEC_OP_UI_CMD_PREVIOUS_CHANNEL -ignore define CEC_OP_UI_CMD_SOUND_SELECT -ignore define CEC_OP_UI_CMD_INPUT_SELECT -ignore define CEC_OP_UI_CMD_DISPLAY_INFORMATION -ignore define CEC_OP_UI_CMD_HELP -ignore define CEC_OP_UI_CMD_PAGE_UP -ignore define CEC_OP_UI_CMD_PAGE_DOWN -ignore define CEC_OP_UI_CMD_POWER -ignore define CEC_OP_UI_CMD_VOLUME_UP -ignore define CEC_OP_UI_CMD_VOLUME_DOWN -ignore define CEC_OP_UI_CMD_MUTE -ignore define CEC_OP_UI_CMD_PLAY -ignore define CEC_OP_UI_CMD_STOP -ignore define CEC_OP_UI_CMD_PAUSE -ignore define CEC_OP_UI_CMD_RECORD -ignore define CEC_OP_UI_CMD_REWIND -ignore define CEC_OP_UI_CMD_FAST_FORWARD -ignore define CEC_OP_UI_CMD_EJECT -ignore define CEC_OP_UI_CMD_SKIP_FORWARD -ignore define CEC_OP_UI_CMD_SKIP_BACKWARD -ignore define CEC_OP_UI_CMD_STOP_RECORD -ignore define CEC_OP_UI_CMD_PAUSE_RECORD -ignore define CEC_OP_UI_CMD_ANGLE -ignore define CEC_OP_UI_CMD_SUB_PICTURE -ignore define CEC_OP_UI_CMD_VIDEO_ON_DEMAND -ignore define CEC_OP_UI_CMD_ELECTRONIC_PROGRAM_GUIDE -ignore define CEC_OP_UI_CMD_TIMER_PROGRAMMING -ignore define CEC_OP_UI_CMD_INITIAL_CONFIGURATION -ignore define CEC_OP_UI_CMD_SELECT_BROADCAST_TYPE -ignore define CEC_OP_UI_CMD_SELECT_SOUND_PRESENTATION -ignore define CEC_OP_UI_CMD_AUDIO_DESCRIPTION -ignore define CEC_OP_UI_CMD_INTERNET -ignore define CEC_OP_UI_CMD_3D_MODE -ignore define CEC_OP_UI_CMD_PLAY_FUNCTION -ignore define CEC_OP_UI_CMD_PAUSE_PLAY_FUNCTION -ignore define CEC_OP_UI_CMD_RECORD_FUNCTION -ignore define CEC_OP_UI_CMD_PAUSE_RECORD_FUNCTION -ignore define CEC_OP_UI_CMD_STOP_FUNCTION -ignore define CEC_OP_UI_CMD_MUTE_FUNCTION -ignore define CEC_OP_UI_CMD_RESTORE_VOLUME_FUNCTION -ignore define CEC_OP_UI_CMD_TUNE_FUNCTION -ignore define CEC_OP_UI_CMD_SELECT_MEDIA_FUNCTION -ignore define CEC_OP_UI_CMD_SELECT_AV_INPUT_FUNCTION -ignore define CEC_OP_UI_CMD_SELECT_AUDIO_INPUT_FUNCTION -ignore define CEC_OP_UI_CMD_POWER_TOGGLE_FUNCTION -ignore define CEC_OP_UI_CMD_POWER_OFF_FUNCTION -ignore define CEC_OP_UI_CMD_POWER_ON_FUNCTION -ignore define CEC_OP_UI_CMD_F1_BLUE -ignore define CEC_OP_UI_CMD_F2_RED -ignore define CEC_OP_UI_CMD_F3_GREEN -ignore define CEC_OP_UI_CMD_F4_YELLOW -ignore define CEC_OP_UI_CMD_F5 -ignore define CEC_OP_UI_CMD_DATA - -ignore define CEC_OP_UI_BCAST_TYPE_TOGGLE_ALL -ignore define CEC_OP_UI_BCAST_TYPE_TOGGLE_DIG_ANA -ignore define CEC_OP_UI_BCAST_TYPE_ANALOGUE -ignore define CEC_OP_UI_BCAST_TYPE_ANALOGUE_T -ignore define CEC_OP_UI_BCAST_TYPE_ANALOGUE_CABLE -ignore define CEC_OP_UI_BCAST_TYPE_ANALOGUE_SAT -ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL -ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL_T -ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL_CABLE -ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL_SAT -ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT -ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT2 -ignore define CEC_OP_UI_BCAST_TYPE_IP - -ignore define CEC_OP_UI_SND_PRES_CTL_DUAL_MONO -ignore define CEC_OP_UI_SND_PRES_CTL_KARAOKE -ignore define CEC_OP_UI_SND_PRES_CTL_DOWNMIX -ignore define CEC_OP_UI_SND_PRES_CTL_REVERB -ignore define CEC_OP_UI_SND_PRES_CTL_EQUALIZER -ignore define CEC_OP_UI_SND_PRES_CTL_BASS_UP -ignore define CEC_OP_UI_SND_PRES_CTL_BASS_NEUTRAL -ignore define CEC_OP_UI_SND_PRES_CTL_BASS_DOWN -ignore define CEC_OP_UI_SND_PRES_CTL_TREBLE_UP -ignore define CEC_OP_UI_SND_PRES_CTL_TREBLE_NEUTRAL -ignore define CEC_OP_UI_SND_PRES_CTL_TREBLE_DOWN - -ignore define CEC_MSG_USER_CONTROL_RELEASED - -ignore define CEC_MSG_GIVE_DEVICE_POWER_STATUS -ignore define CEC_MSG_REPORT_POWER_STATUS - -ignore define CEC_OP_POWER_STATUS_ON -ignore define CEC_OP_POWER_STATUS_STANDBY -ignore define CEC_OP_POWER_STATUS_TO_ON -ignore define CEC_OP_POWER_STATUS_TO_STANDBY - -ignore define CEC_MSG_FEATURE_ABORT - -ignore define CEC_OP_ABORT_UNRECOGNIZED_OP -ignore define CEC_OP_ABORT_INCORRECT_MODE -ignore define CEC_OP_ABORT_NO_SOURCE -ignore define CEC_OP_ABORT_INVALID_OP -ignore define CEC_OP_ABORT_REFUSED -ignore define CEC_OP_ABORT_UNDETERMINED - -ignore define CEC_MSG_ABORT - -ignore define CEC_MSG_GIVE_AUDIO_STATUS -ignore define CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS -ignore define CEC_MSG_REPORT_AUDIO_STATUS - -ignore define CEC_OP_AUD_MUTE_STATUS_OFF -ignore define CEC_OP_AUD_MUTE_STATUS_ON - -ignore define CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR -ignore define CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR -ignore define CEC_MSG_SET_SYSTEM_AUDIO_MODE - -ignore define CEC_OP_SYS_AUD_STATUS_OFF -ignore define CEC_OP_SYS_AUD_STATUS_ON - -ignore define CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST -ignore define CEC_MSG_SYSTEM_AUDIO_MODE_STATUS -ignore define CEC_MSG_SET_AUDIO_VOLUME_LEVEL - -ignore define CEC_OP_AUD_FMT_ID_CEA861 -ignore define CEC_OP_AUD_FMT_ID_CEA861_CXT - -ignore define CEC_MSG_SET_AUDIO_RATE - -ignore define CEC_OP_AUD_RATE_OFF -ignore define CEC_OP_AUD_RATE_WIDE_STD -ignore define CEC_OP_AUD_RATE_WIDE_FAST -ignore define CEC_OP_AUD_RATE_WIDE_SLOW -ignore define CEC_OP_AUD_RATE_NARROW_STD -ignore define CEC_OP_AUD_RATE_NARROW_FAST -ignore define CEC_OP_AUD_RATE_NARROW_SLOW - -ignore define CEC_MSG_INITIATE_ARC -ignore define CEC_MSG_REPORT_ARC_INITIATED -ignore define CEC_MSG_REPORT_ARC_TERMINATED -ignore define CEC_MSG_REQUEST_ARC_INITIATION -ignore define CEC_MSG_REQUEST_ARC_TERMINATION -ignore define CEC_MSG_TERMINATE_ARC - -ignore define CEC_MSG_REQUEST_CURRENT_LATENCY -ignore define CEC_MSG_REPORT_CURRENT_LATENCY - -ignore define CEC_OP_LOW_LATENCY_MODE_OFF -ignore define CEC_OP_LOW_LATENCY_MODE_ON - -ignore define CEC_OP_AUD_OUT_COMPENSATED_NA -ignore define CEC_OP_AUD_OUT_COMPENSATED_DELAY -ignore define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY -ignore define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY - -ignore define CEC_MSG_CDC_MESSAGE - -ignore define CEC_MSG_CDC_HEC_INQUIRE_STATE -ignore define CEC_MSG_CDC_HEC_REPORT_STATE - -ignore define CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED -ignore define CEC_OP_HEC_FUNC_STATE_INACTIVE -ignore define CEC_OP_HEC_FUNC_STATE_ACTIVE -ignore define CEC_OP_HEC_FUNC_STATE_ACTIVATION_FIELD - -ignore define CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED -ignore define CEC_OP_HOST_FUNC_STATE_INACTIVE -ignore define CEC_OP_HOST_FUNC_STATE_ACTIVE - -ignore define CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED -ignore define CEC_OP_ENC_FUNC_STATE_EXT_CON_INACTIVE -ignore define CEC_OP_ENC_FUNC_STATE_EXT_CON_ACTIVE - -ignore define CEC_OP_CDC_ERROR_CODE_NONE -ignore define CEC_OP_CDC_ERROR_CODE_CAP_UNSUPPORTED -ignore define CEC_OP_CDC_ERROR_CODE_WRONG_STATE -ignore define CEC_OP_CDC_ERROR_CODE_OTHER - -ignore define CEC_OP_HEC_SUPPORT_NO -ignore define CEC_OP_HEC_SUPPORT_YES - -ignore define CEC_OP_HEC_ACTIVATION_ON -ignore define CEC_OP_HEC_ACTIVATION_OFF - -ignore define CEC_MSG_CDC_HEC_SET_STATE_ADJACENT -ignore define CEC_MSG_CDC_HEC_SET_STATE - -ignore define CEC_OP_HEC_SET_STATE_DEACTIVATE -ignore define CEC_OP_HEC_SET_STATE_ACTIVATE - -ignore define CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION -ignore define CEC_MSG_CDC_HEC_NOTIFY_ALIVE -ignore define CEC_MSG_CDC_HEC_DISCOVER - -ignore define CEC_MSG_CDC_HPD_SET_STATE - -ignore define CEC_OP_HPD_STATE_CP_EDID_DISABLE -ignore define CEC_OP_HPD_STATE_CP_EDID_ENABLE -ignore define CEC_OP_HPD_STATE_CP_EDID_DISABLE_ENABLE -ignore define CEC_OP_HPD_STATE_EDID_DISABLE -ignore define CEC_OP_HPD_STATE_EDID_ENABLE -ignore define CEC_OP_HPD_STATE_EDID_DISABLE_ENABLE -ignore define CEC_MSG_CDC_HPD_REPORT_STATE - -ignore define CEC_OP_HPD_ERROR_NONE -ignore define CEC_OP_HPD_ERROR_INITIATOR_NOT_CAPABLE -ignore define CEC_OP_HPD_ERROR_INITIATOR_WRONG_STATE -ignore define CEC_OP_HPD_ERROR_OTHER -ignore define CEC_OP_HPD_ERROR_NONE_NO_VIDEO diff --git a/Documentation/userspace-api/media/cec/cec-header.rst b/Documentation/userspace-api/media/cec/cec-header.rst index d70736ac2b1d..f67003bb8740 100644 --- a/Documentation/userspace-api/media/cec/cec-header.rst +++ b/Documentation/userspace-api/media/cec/cec-header.rst @@ -6,5 +6,6 @@ CEC Header File *************** -.. kernel-include:: $BUILDDIR/cec.h.rst - +.. kernel-include:: include/uapi/linux/cec.h + :generate-cross-refs: + :exception-file: cec.h.rst.exceptions diff --git a/Documentation/userspace-api/media/cec/cec.h.rst.exceptions b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions new file mode 100644 index 000000000000..15fa1752d4ef --- /dev/null +++ b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions @@ -0,0 +1,577 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Ignore header name +ignore define _CEC_UAPI_H + +# define macros to ignore + +ignore define CEC_MAX_MSG_SIZE +ignore define CEC_MAX_LOG_ADDRS + +ignore define CEC_LOG_ADDR_MASK_TV +ignore define CEC_LOG_ADDR_MASK_RECORD +ignore define CEC_LOG_ADDR_MASK_TUNER +ignore define CEC_LOG_ADDR_MASK_PLAYBACK +ignore define CEC_LOG_ADDR_MASK_AUDIOSYSTEM +ignore define CEC_LOG_ADDR_MASK_BACKUP +ignore define CEC_LOG_ADDR_MASK_SPECIFIC +ignore define CEC_LOG_ADDR_MASK_UNREGISTERED + +# Shouldn't them be documented? +ignore define CEC_LOG_ADDR_INVALID +ignore define CEC_PHYS_ADDR_INVALID + +ignore define CEC_VENDOR_ID_NONE + +ignore define CEC_MODE_INITIATOR_MSK +ignore define CEC_MODE_FOLLOWER_MSK + +# Part of CEC 2.0 spec - shouldn't be documented too? +ignore define CEC_LOG_ADDR_TV +ignore define CEC_LOG_ADDR_RECORD_1 +ignore define CEC_LOG_ADDR_RECORD_2 +ignore define CEC_LOG_ADDR_TUNER_1 +ignore define CEC_LOG_ADDR_PLAYBACK_1 +ignore define CEC_LOG_ADDR_AUDIOSYSTEM +ignore define CEC_LOG_ADDR_TUNER_2 +ignore define CEC_LOG_ADDR_TUNER_3 +ignore define CEC_LOG_ADDR_PLAYBACK_2 +ignore define CEC_LOG_ADDR_RECORD_3 +ignore define CEC_LOG_ADDR_TUNER_4 +ignore define CEC_LOG_ADDR_PLAYBACK_3 +ignore define CEC_LOG_ADDR_BACKUP_1 +ignore define CEC_LOG_ADDR_BACKUP_2 +ignore define CEC_LOG_ADDR_SPECIFIC +ignore define CEC_LOG_ADDR_UNREGISTERED +ignore define CEC_LOG_ADDR_BROADCAST + +# IMHO, those should also be documented + +ignore define CEC_MSG_ACTIVE_SOURCE +ignore define CEC_MSG_IMAGE_VIEW_ON +ignore define CEC_MSG_TEXT_VIEW_ON + +ignore define CEC_MSG_INACTIVE_SOURCE +ignore define CEC_MSG_REQUEST_ACTIVE_SOURCE +ignore define CEC_MSG_ROUTING_CHANGE +ignore define CEC_MSG_ROUTING_INFORMATION +ignore define CEC_MSG_SET_STREAM_PATH + +ignore define CEC_MSG_STANDBY + +ignore define CEC_MSG_RECORD_OFF +ignore define CEC_MSG_RECORD_ON + +ignore define CEC_OP_RECORD_SRC_OWN +ignore define CEC_OP_RECORD_SRC_DIGITAL +ignore define CEC_OP_RECORD_SRC_ANALOG +ignore define CEC_OP_RECORD_SRC_EXT_PLUG +ignore define CEC_OP_RECORD_SRC_EXT_PHYS_ADDR + +ignore define CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID +ignore define CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL + +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2 +ignore define CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T + +ignore define CEC_OP_ANA_BCAST_TYPE_CABLE +ignore define CEC_OP_ANA_BCAST_TYPE_SATELLITE +ignore define CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL + +ignore define CEC_OP_BCAST_SYSTEM_PAL_BG +ignore define CEC_OP_BCAST_SYSTEM_SECAM_LQ +ignore define CEC_OP_BCAST_SYSTEM_PAL_M +ignore define CEC_OP_BCAST_SYSTEM_NTSC_M +ignore define CEC_OP_BCAST_SYSTEM_PAL_I +ignore define CEC_OP_BCAST_SYSTEM_SECAM_DK +ignore define CEC_OP_BCAST_SYSTEM_SECAM_BG +ignore define CEC_OP_BCAST_SYSTEM_SECAM_L +ignore define CEC_OP_BCAST_SYSTEM_PAL_DK +ignore define CEC_OP_BCAST_SYSTEM_OTHER + +ignore define CEC_OP_CHANNEL_NUMBER_FMT_1_PART +ignore define CEC_OP_CHANNEL_NUMBER_FMT_2_PART + +ignore define CEC_MSG_RECORD_STATUS + +ignore define CEC_OP_RECORD_STATUS_CUR_SRC +ignore define CEC_OP_RECORD_STATUS_DIG_SERVICE +ignore define CEC_OP_RECORD_STATUS_ANA_SERVICE +ignore define CEC_OP_RECORD_STATUS_EXT_INPUT +ignore define CEC_OP_RECORD_STATUS_NO_DIG_SERVICE +ignore define CEC_OP_RECORD_STATUS_NO_ANA_SERVICE +ignore define CEC_OP_RECORD_STATUS_NO_SERVICE +ignore define CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG +ignore define CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR +ignore define CEC_OP_RECORD_STATUS_UNSUP_CA +ignore define CEC_OP_RECORD_STATUS_NO_CA_ENTITLEMENTS +ignore define CEC_OP_RECORD_STATUS_CANT_COPY_SRC +ignore define CEC_OP_RECORD_STATUS_NO_MORE_COPIES +ignore define CEC_OP_RECORD_STATUS_NO_MEDIA +ignore define CEC_OP_RECORD_STATUS_PLAYING +ignore define CEC_OP_RECORD_STATUS_ALREADY_RECORDING +ignore define CEC_OP_RECORD_STATUS_MEDIA_PROT +ignore define CEC_OP_RECORD_STATUS_NO_SIGNAL +ignore define CEC_OP_RECORD_STATUS_MEDIA_PROBLEM +ignore define CEC_OP_RECORD_STATUS_NO_SPACE +ignore define CEC_OP_RECORD_STATUS_PARENTAL_LOCK +ignore define CEC_OP_RECORD_STATUS_TERMINATED_OK +ignore define CEC_OP_RECORD_STATUS_ALREADY_TERM +ignore define CEC_OP_RECORD_STATUS_OTHER + +ignore define CEC_MSG_RECORD_TV_SCREEN + +ignore define CEC_MSG_CLEAR_ANALOGUE_TIMER + +ignore define CEC_OP_REC_SEQ_SUNDAY +ignore define CEC_OP_REC_SEQ_MONDAY +ignore define CEC_OP_REC_SEQ_TUESDAY +ignore define CEC_OP_REC_SEQ_WEDNESDAY +ignore define CEC_OP_REC_SEQ_THURSDAY +ignore define CEC_OP_REC_SEQ_FRIDAY +ignore define CEC_OP_REC_SEQ_SATURDAY +ignore define CEC_OP_REC_SEQ_ONCE_ONLY + +ignore define CEC_MSG_CLEAR_DIGITAL_TIMER + +ignore define CEC_MSG_CLEAR_EXT_TIMER + +ignore define CEC_OP_EXT_SRC_PLUG +ignore define CEC_OP_EXT_SRC_PHYS_ADDR + +ignore define CEC_MSG_SET_ANALOGUE_TIMER +ignore define CEC_MSG_SET_DIGITAL_TIMER +ignore define CEC_MSG_SET_EXT_TIMER + +ignore define CEC_MSG_SET_TIMER_PROGRAM_TITLE +ignore define CEC_MSG_TIMER_CLEARED_STATUS + +ignore define CEC_OP_TIMER_CLR_STAT_RECORDING +ignore define CEC_OP_TIMER_CLR_STAT_NO_MATCHING +ignore define CEC_OP_TIMER_CLR_STAT_NO_INFO +ignore define CEC_OP_TIMER_CLR_STAT_CLEARED + +ignore define CEC_MSG_TIMER_STATUS + +ignore define CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP +ignore define CEC_OP_TIMER_OVERLAP_WARNING_OVERLAP + +ignore define CEC_OP_MEDIA_INFO_UNPROT_MEDIA +ignore define CEC_OP_MEDIA_INFO_PROT_MEDIA +ignore define CEC_OP_MEDIA_INFO_NO_MEDIA + +ignore define CEC_OP_PROG_IND_NOT_PROGRAMMED +ignore define CEC_OP_PROG_IND_PROGRAMMED + +ignore define CEC_OP_PROG_INFO_ENOUGH_SPACE +ignore define CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE +ignore define CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE +ignore define CEC_OP_PROG_INFO_NONE_AVAILABLE + +ignore define CEC_OP_PROG_ERROR_NO_FREE_TIMER +ignore define CEC_OP_PROG_ERROR_DATE_OUT_OF_RANGE +ignore define CEC_OP_PROG_ERROR_REC_SEQ_ERROR +ignore define CEC_OP_PROG_ERROR_INV_EXT_PLUG +ignore define CEC_OP_PROG_ERROR_INV_EXT_PHYS_ADDR +ignore define CEC_OP_PROG_ERROR_CA_UNSUPP +ignore define CEC_OP_PROG_ERROR_INSUF_CA_ENTITLEMENTS +ignore define CEC_OP_PROG_ERROR_RESOLUTION_UNSUPP +ignore define CEC_OP_PROG_ERROR_PARENTAL_LOCK +ignore define CEC_OP_PROG_ERROR_CLOCK_FAILURE +ignore define CEC_OP_PROG_ERROR_DUPLICATE + +ignore define CEC_MSG_CEC_VERSION + +ignore define CEC_OP_CEC_VERSION_1_3A +ignore define CEC_OP_CEC_VERSION_1_4 +ignore define CEC_OP_CEC_VERSION_2_0 + +ignore define CEC_MSG_GET_CEC_VERSION +ignore define CEC_MSG_GIVE_PHYSICAL_ADDR +ignore define CEC_MSG_GET_MENU_LANGUAGE +ignore define CEC_MSG_REPORT_PHYSICAL_ADDR + +ignore define CEC_OP_PRIM_DEVTYPE_TV +ignore define CEC_OP_PRIM_DEVTYPE_RECORD +ignore define CEC_OP_PRIM_DEVTYPE_TUNER +ignore define CEC_OP_PRIM_DEVTYPE_PLAYBACK +ignore define CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM +ignore define CEC_OP_PRIM_DEVTYPE_SWITCH +ignore define CEC_OP_PRIM_DEVTYPE_PROCESSOR + +ignore define CEC_MSG_SET_MENU_LANGUAGE +ignore define CEC_MSG_REPORT_FEATURES + +ignore define CEC_OP_ALL_DEVTYPE_TV +ignore define CEC_OP_ALL_DEVTYPE_RECORD +ignore define CEC_OP_ALL_DEVTYPE_TUNER +ignore define CEC_OP_ALL_DEVTYPE_PLAYBACK +ignore define CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM +ignore define CEC_OP_ALL_DEVTYPE_SWITCH + +ignore define CEC_OP_FEAT_EXT + +ignore define CEC_OP_FEAT_RC_TV_PROFILE_NONE +ignore define CEC_OP_FEAT_RC_TV_PROFILE_1 +ignore define CEC_OP_FEAT_RC_TV_PROFILE_2 +ignore define CEC_OP_FEAT_RC_TV_PROFILE_3 +ignore define CEC_OP_FEAT_RC_TV_PROFILE_4 +ignore define CEC_OP_FEAT_RC_SRC_HAS_DEV_ROOT_MENU +ignore define CEC_OP_FEAT_RC_SRC_HAS_DEV_SETUP_MENU +ignore define CEC_OP_FEAT_RC_SRC_HAS_CONTENTS_MENU +ignore define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_TOP_MENU +ignore define CEC_OP_FEAT_RC_SRC_HAS_MEDIA_CONTEXT_MENU + +ignore define CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN +ignore define CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING +ignore define CEC_OP_FEAT_DEV_HAS_DECK_CONTROL +ignore define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE +ignore define CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX +ignore define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX +ignore define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_VOLUME_LEVEL + +ignore define CEC_MSG_GIVE_FEATURES + +ignore define CEC_MSG_DECK_CONTROL + +ignore define CEC_OP_DECK_CTL_MODE_SKIP_FWD +ignore define CEC_OP_DECK_CTL_MODE_SKIP_REV +ignore define CEC_OP_DECK_CTL_MODE_STOP +ignore define CEC_OP_DECK_CTL_MODE_EJECT + +ignore define CEC_MSG_DECK_STATUS + +ignore define CEC_OP_DECK_INFO_PLAY +ignore define CEC_OP_DECK_INFO_RECORD +ignore define CEC_OP_DECK_INFO_PLAY_REV +ignore define CEC_OP_DECK_INFO_STILL +ignore define CEC_OP_DECK_INFO_SLOW +ignore define CEC_OP_DECK_INFO_SLOW_REV +ignore define CEC_OP_DECK_INFO_FAST_FWD +ignore define CEC_OP_DECK_INFO_FAST_REV +ignore define CEC_OP_DECK_INFO_NO_MEDIA +ignore define CEC_OP_DECK_INFO_STOP +ignore define CEC_OP_DECK_INFO_SKIP_FWD +ignore define CEC_OP_DECK_INFO_SKIP_REV +ignore define CEC_OP_DECK_INFO_INDEX_SEARCH_FWD +ignore define CEC_OP_DECK_INFO_INDEX_SEARCH_REV +ignore define CEC_OP_DECK_INFO_OTHER + +ignore define CEC_MSG_GIVE_DECK_STATUS + +ignore define CEC_OP_STATUS_REQ_ON +ignore define CEC_OP_STATUS_REQ_OFF +ignore define CEC_OP_STATUS_REQ_ONCE + +ignore define CEC_MSG_PLAY + +ignore define CEC_OP_PLAY_MODE_PLAY_FWD +ignore define CEC_OP_PLAY_MODE_PLAY_REV +ignore define CEC_OP_PLAY_MODE_PLAY_STILL +ignore define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN +ignore define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED +ignore define CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX +ignore define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN +ignore define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED +ignore define CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX +ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN +ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED +ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX +ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN +ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED +ignore define CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX + +ignore define CEC_MSG_GIVE_TUNER_DEVICE_STATUS +ignore define CEC_MSG_SELECT_ANALOGUE_SERVICE +ignore define CEC_MSG_SELECT_DIGITAL_SERVICE +ignore define CEC_MSG_TUNER_DEVICE_STATUS + +ignore define CEC_OP_REC_FLAG_USED +ignore define CEC_OP_REC_FLAG_NOT_USED + +ignore define CEC_OP_TUNER_DISPLAY_INFO_DIGITAL +ignore define CEC_OP_TUNER_DISPLAY_INFO_NONE +ignore define CEC_OP_TUNER_DISPLAY_INFO_ANALOGUE + +ignore define CEC_MSG_TUNER_STEP_DECREMENT +ignore define CEC_MSG_TUNER_STEP_INCREMENT + +ignore define CEC_MSG_DEVICE_VENDOR_ID +ignore define CEC_MSG_GIVE_DEVICE_VENDOR_ID +ignore define CEC_MSG_VENDOR_COMMAND +ignore define CEC_MSG_VENDOR_COMMAND_WITH_ID +ignore define CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN +ignore define CEC_MSG_VENDOR_REMOTE_BUTTON_UP + +ignore define CEC_MSG_SET_OSD_STRING + +ignore define CEC_OP_DISP_CTL_DEFAULT +ignore define CEC_OP_DISP_CTL_UNTIL_CLEARED +ignore define CEC_OP_DISP_CTL_CLEAR + +ignore define CEC_MSG_GIVE_OSD_NAME +ignore define CEC_MSG_SET_OSD_NAME + +ignore define CEC_MSG_MENU_REQUEST + +ignore define CEC_OP_MENU_REQUEST_ACTIVATE +ignore define CEC_OP_MENU_REQUEST_DEACTIVATE +ignore define CEC_OP_MENU_REQUEST_QUERY + +ignore define CEC_MSG_MENU_STATUS + +ignore define CEC_OP_MENU_STATE_ACTIVATED +ignore define CEC_OP_MENU_STATE_DEACTIVATED + +ignore define CEC_MSG_USER_CONTROL_PRESSED + +ignore define CEC_OP_UI_CMD_SELECT +ignore define CEC_OP_UI_CMD_UP +ignore define CEC_OP_UI_CMD_DOWN +ignore define CEC_OP_UI_CMD_LEFT +ignore define CEC_OP_UI_CMD_RIGHT +ignore define CEC_OP_UI_CMD_RIGHT_UP +ignore define CEC_OP_UI_CMD_RIGHT_DOWN +ignore define CEC_OP_UI_CMD_LEFT_UP +ignore define CEC_OP_UI_CMD_LEFT_DOWN +ignore define CEC_OP_UI_CMD_DEVICE_ROOT_MENU +ignore define CEC_OP_UI_CMD_DEVICE_SETUP_MENU +ignore define CEC_OP_UI_CMD_CONTENTS_MENU +ignore define CEC_OP_UI_CMD_FAVORITE_MENU +ignore define CEC_OP_UI_CMD_BACK +ignore define CEC_OP_UI_CMD_MEDIA_TOP_MENU +ignore define CEC_OP_UI_CMD_MEDIA_CONTEXT_SENSITIVE_MENU +ignore define CEC_OP_UI_CMD_NUMBER_ENTRY_MODE +ignore define CEC_OP_UI_CMD_NUMBER_11 +ignore define CEC_OP_UI_CMD_NUMBER_12 +ignore define CEC_OP_UI_CMD_NUMBER_0_OR_NUMBER_10 +ignore define CEC_OP_UI_CMD_NUMBER_1 +ignore define CEC_OP_UI_CMD_NUMBER_2 +ignore define CEC_OP_UI_CMD_NUMBER_3 +ignore define CEC_OP_UI_CMD_NUMBER_4 +ignore define CEC_OP_UI_CMD_NUMBER_5 +ignore define CEC_OP_UI_CMD_NUMBER_6 +ignore define CEC_OP_UI_CMD_NUMBER_7 +ignore define CEC_OP_UI_CMD_NUMBER_8 +ignore define CEC_OP_UI_CMD_NUMBER_9 +ignore define CEC_OP_UI_CMD_DOT +ignore define CEC_OP_UI_CMD_ENTER +ignore define CEC_OP_UI_CMD_CLEAR +ignore define CEC_OP_UI_CMD_NEXT_FAVORITE +ignore define CEC_OP_UI_CMD_CHANNEL_UP +ignore define CEC_OP_UI_CMD_CHANNEL_DOWN +ignore define CEC_OP_UI_CMD_PREVIOUS_CHANNEL +ignore define CEC_OP_UI_CMD_SOUND_SELECT +ignore define CEC_OP_UI_CMD_INPUT_SELECT +ignore define CEC_OP_UI_CMD_DISPLAY_INFORMATION +ignore define CEC_OP_UI_CMD_HELP +ignore define CEC_OP_UI_CMD_PAGE_UP +ignore define CEC_OP_UI_CMD_PAGE_DOWN +ignore define CEC_OP_UI_CMD_POWER +ignore define CEC_OP_UI_CMD_VOLUME_UP +ignore define CEC_OP_UI_CMD_VOLUME_DOWN +ignore define CEC_OP_UI_CMD_MUTE +ignore define CEC_OP_UI_CMD_PLAY +ignore define CEC_OP_UI_CMD_STOP +ignore define CEC_OP_UI_CMD_PAUSE +ignore define CEC_OP_UI_CMD_RECORD +ignore define CEC_OP_UI_CMD_REWIND +ignore define CEC_OP_UI_CMD_FAST_FORWARD +ignore define CEC_OP_UI_CMD_EJECT +ignore define CEC_OP_UI_CMD_SKIP_FORWARD +ignore define CEC_OP_UI_CMD_SKIP_BACKWARD +ignore define CEC_OP_UI_CMD_STOP_RECORD +ignore define CEC_OP_UI_CMD_PAUSE_RECORD +ignore define CEC_OP_UI_CMD_ANGLE +ignore define CEC_OP_UI_CMD_SUB_PICTURE +ignore define CEC_OP_UI_CMD_VIDEO_ON_DEMAND +ignore define CEC_OP_UI_CMD_ELECTRONIC_PROGRAM_GUIDE +ignore define CEC_OP_UI_CMD_TIMER_PROGRAMMING +ignore define CEC_OP_UI_CMD_INITIAL_CONFIGURATION +ignore define CEC_OP_UI_CMD_SELECT_BROADCAST_TYPE +ignore define CEC_OP_UI_CMD_SELECT_SOUND_PRESENTATION +ignore define CEC_OP_UI_CMD_AUDIO_DESCRIPTION +ignore define CEC_OP_UI_CMD_INTERNET +ignore define CEC_OP_UI_CMD_3D_MODE +ignore define CEC_OP_UI_CMD_PLAY_FUNCTION +ignore define CEC_OP_UI_CMD_PAUSE_PLAY_FUNCTION +ignore define CEC_OP_UI_CMD_RECORD_FUNCTION +ignore define CEC_OP_UI_CMD_PAUSE_RECORD_FUNCTION +ignore define CEC_OP_UI_CMD_STOP_FUNCTION +ignore define CEC_OP_UI_CMD_MUTE_FUNCTION +ignore define CEC_OP_UI_CMD_RESTORE_VOLUME_FUNCTION +ignore define CEC_OP_UI_CMD_TUNE_FUNCTION +ignore define CEC_OP_UI_CMD_SELECT_MEDIA_FUNCTION +ignore define CEC_OP_UI_CMD_SELECT_AV_INPUT_FUNCTION +ignore define CEC_OP_UI_CMD_SELECT_AUDIO_INPUT_FUNCTION +ignore define CEC_OP_UI_CMD_POWER_TOGGLE_FUNCTION +ignore define CEC_OP_UI_CMD_POWER_OFF_FUNCTION +ignore define CEC_OP_UI_CMD_POWER_ON_FUNCTION +ignore define CEC_OP_UI_CMD_F1_BLUE +ignore define CEC_OP_UI_CMD_F2_RED +ignore define CEC_OP_UI_CMD_F3_GREEN +ignore define CEC_OP_UI_CMD_F4_YELLOW +ignore define CEC_OP_UI_CMD_F5 +ignore define CEC_OP_UI_CMD_DATA + +ignore define CEC_OP_UI_BCAST_TYPE_TOGGLE_ALL +ignore define CEC_OP_UI_BCAST_TYPE_TOGGLE_DIG_ANA +ignore define CEC_OP_UI_BCAST_TYPE_ANALOGUE +ignore define CEC_OP_UI_BCAST_TYPE_ANALOGUE_T +ignore define CEC_OP_UI_BCAST_TYPE_ANALOGUE_CABLE +ignore define CEC_OP_UI_BCAST_TYPE_ANALOGUE_SAT +ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL +ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL_T +ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL_CABLE +ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL_SAT +ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT +ignore define CEC_OP_UI_BCAST_TYPE_DIGITAL_COM_SAT2 +ignore define CEC_OP_UI_BCAST_TYPE_IP + +ignore define CEC_OP_UI_SND_PRES_CTL_DUAL_MONO +ignore define CEC_OP_UI_SND_PRES_CTL_KARAOKE +ignore define CEC_OP_UI_SND_PRES_CTL_DOWNMIX +ignore define CEC_OP_UI_SND_PRES_CTL_REVERB +ignore define CEC_OP_UI_SND_PRES_CTL_EQUALIZER +ignore define CEC_OP_UI_SND_PRES_CTL_BASS_UP +ignore define CEC_OP_UI_SND_PRES_CTL_BASS_NEUTRAL +ignore define CEC_OP_UI_SND_PRES_CTL_BASS_DOWN +ignore define CEC_OP_UI_SND_PRES_CTL_TREBLE_UP +ignore define CEC_OP_UI_SND_PRES_CTL_TREBLE_NEUTRAL +ignore define CEC_OP_UI_SND_PRES_CTL_TREBLE_DOWN + +ignore define CEC_MSG_USER_CONTROL_RELEASED + +ignore define CEC_MSG_GIVE_DEVICE_POWER_STATUS +ignore define CEC_MSG_REPORT_POWER_STATUS + +ignore define CEC_OP_POWER_STATUS_ON +ignore define CEC_OP_POWER_STATUS_STANDBY +ignore define CEC_OP_POWER_STATUS_TO_ON +ignore define CEC_OP_POWER_STATUS_TO_STANDBY + +ignore define CEC_MSG_FEATURE_ABORT + +ignore define CEC_OP_ABORT_UNRECOGNIZED_OP +ignore define CEC_OP_ABORT_INCORRECT_MODE +ignore define CEC_OP_ABORT_NO_SOURCE +ignore define CEC_OP_ABORT_INVALID_OP +ignore define CEC_OP_ABORT_REFUSED +ignore define CEC_OP_ABORT_UNDETERMINED + +ignore define CEC_MSG_ABORT + +ignore define CEC_MSG_GIVE_AUDIO_STATUS +ignore define CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS +ignore define CEC_MSG_REPORT_AUDIO_STATUS + +ignore define CEC_OP_AUD_MUTE_STATUS_OFF +ignore define CEC_OP_AUD_MUTE_STATUS_ON + +ignore define CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR +ignore define CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR +ignore define CEC_MSG_SET_SYSTEM_AUDIO_MODE + +ignore define CEC_OP_SYS_AUD_STATUS_OFF +ignore define CEC_OP_SYS_AUD_STATUS_ON + +ignore define CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST +ignore define CEC_MSG_SYSTEM_AUDIO_MODE_STATUS +ignore define CEC_MSG_SET_AUDIO_VOLUME_LEVEL + +ignore define CEC_OP_AUD_FMT_ID_CEA861 +ignore define CEC_OP_AUD_FMT_ID_CEA861_CXT + +ignore define CEC_MSG_SET_AUDIO_RATE + +ignore define CEC_OP_AUD_RATE_OFF +ignore define CEC_OP_AUD_RATE_WIDE_STD +ignore define CEC_OP_AUD_RATE_WIDE_FAST +ignore define CEC_OP_AUD_RATE_WIDE_SLOW +ignore define CEC_OP_AUD_RATE_NARROW_STD +ignore define CEC_OP_AUD_RATE_NARROW_FAST +ignore define CEC_OP_AUD_RATE_NARROW_SLOW + +ignore define CEC_MSG_INITIATE_ARC +ignore define CEC_MSG_REPORT_ARC_INITIATED +ignore define CEC_MSG_REPORT_ARC_TERMINATED +ignore define CEC_MSG_REQUEST_ARC_INITIATION +ignore define CEC_MSG_REQUEST_ARC_TERMINATION +ignore define CEC_MSG_TERMINATE_ARC + +ignore define CEC_MSG_REQUEST_CURRENT_LATENCY +ignore define CEC_MSG_REPORT_CURRENT_LATENCY + +ignore define CEC_OP_LOW_LATENCY_MODE_OFF +ignore define CEC_OP_LOW_LATENCY_MODE_ON + +ignore define CEC_OP_AUD_OUT_COMPENSATED_NA +ignore define CEC_OP_AUD_OUT_COMPENSATED_DELAY +ignore define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY +ignore define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY + +ignore define CEC_MSG_CDC_MESSAGE + +ignore define CEC_MSG_CDC_HEC_INQUIRE_STATE +ignore define CEC_MSG_CDC_HEC_REPORT_STATE + +ignore define CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED +ignore define CEC_OP_HEC_FUNC_STATE_INACTIVE +ignore define CEC_OP_HEC_FUNC_STATE_ACTIVE +ignore define CEC_OP_HEC_FUNC_STATE_ACTIVATION_FIELD + +ignore define CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED +ignore define CEC_OP_HOST_FUNC_STATE_INACTIVE +ignore define CEC_OP_HOST_FUNC_STATE_ACTIVE + +ignore define CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED +ignore define CEC_OP_ENC_FUNC_STATE_EXT_CON_INACTIVE +ignore define CEC_OP_ENC_FUNC_STATE_EXT_CON_ACTIVE + +ignore define CEC_OP_CDC_ERROR_CODE_NONE +ignore define CEC_OP_CDC_ERROR_CODE_CAP_UNSUPPORTED +ignore define CEC_OP_CDC_ERROR_CODE_WRONG_STATE +ignore define CEC_OP_CDC_ERROR_CODE_OTHER + +ignore define CEC_OP_HEC_SUPPORT_NO +ignore define CEC_OP_HEC_SUPPORT_YES + +ignore define CEC_OP_HEC_ACTIVATION_ON +ignore define CEC_OP_HEC_ACTIVATION_OFF + +ignore define CEC_MSG_CDC_HEC_SET_STATE_ADJACENT +ignore define CEC_MSG_CDC_HEC_SET_STATE + +ignore define CEC_OP_HEC_SET_STATE_DEACTIVATE +ignore define CEC_OP_HEC_SET_STATE_ACTIVATE + +ignore define CEC_MSG_CDC_HEC_REQUEST_DEACTIVATION +ignore define CEC_MSG_CDC_HEC_NOTIFY_ALIVE +ignore define CEC_MSG_CDC_HEC_DISCOVER + +ignore define CEC_MSG_CDC_HPD_SET_STATE + +ignore define CEC_OP_HPD_STATE_CP_EDID_DISABLE +ignore define CEC_OP_HPD_STATE_CP_EDID_ENABLE +ignore define CEC_OP_HPD_STATE_CP_EDID_DISABLE_ENABLE +ignore define CEC_OP_HPD_STATE_EDID_DISABLE +ignore define CEC_OP_HPD_STATE_EDID_ENABLE +ignore define CEC_OP_HPD_STATE_EDID_DISABLE_ENABLE +ignore define CEC_MSG_CDC_HPD_REPORT_STATE + +ignore define CEC_OP_HPD_ERROR_NONE +ignore define CEC_OP_HPD_ERROR_INITIATOR_NOT_CAPABLE +ignore define CEC_OP_HPD_ERROR_INITIATOR_WRONG_STATE +ignore define CEC_OP_HPD_ERROR_OTHER +ignore define CEC_OP_HPD_ERROR_NONE_NO_VIDEO diff --git a/Documentation/userspace-api/media/dmx.h.rst.exceptions b/Documentation/userspace-api/media/dmx.h.rst.exceptions deleted file mode 100644 index afc14d384b83..000000000000 --- a/Documentation/userspace-api/media/dmx.h.rst.exceptions +++ /dev/null @@ -1,66 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -# Ignore header name -ignore define _UAPI_DVBDMX_H_ - -# Ignore limit constants -ignore define DMX_FILTER_SIZE - -# dmx_pes_type_t enum symbols -replace enum dmx_ts_pes :c:type:`dmx_pes_type` -replace symbol DMX_PES_AUDIO0 :c:type:`dmx_pes_type` -replace symbol DMX_PES_VIDEO0 :c:type:`dmx_pes_type` -replace symbol DMX_PES_TELETEXT0 :c:type:`dmx_pes_type` -replace symbol DMX_PES_SUBTITLE0 :c:type:`dmx_pes_type` -replace symbol DMX_PES_PCR0 :c:type:`dmx_pes_type` -replace symbol DMX_PES_AUDIO1 :c:type:`dmx_pes_type` -replace symbol DMX_PES_VIDEO1 :c:type:`dmx_pes_type` -replace symbol DMX_PES_TELETEXT1 :c:type:`dmx_pes_type` -replace symbol DMX_PES_SUBTITLE1 :c:type:`dmx_pes_type` -replace symbol DMX_PES_PCR1 :c:type:`dmx_pes_type` -replace symbol DMX_PES_AUDIO2 :c:type:`dmx_pes_type` -replace symbol DMX_PES_VIDEO2 :c:type:`dmx_pes_type` -replace symbol DMX_PES_TELETEXT2 :c:type:`dmx_pes_type` -replace symbol DMX_PES_SUBTITLE2 :c:type:`dmx_pes_type` -replace symbol DMX_PES_PCR2 :c:type:`dmx_pes_type` -replace symbol DMX_PES_AUDIO3 :c:type:`dmx_pes_type` -replace symbol DMX_PES_VIDEO3 :c:type:`dmx_pes_type` -replace symbol DMX_PES_TELETEXT3 :c:type:`dmx_pes_type` -replace symbol DMX_PES_SUBTITLE3 :c:type:`dmx_pes_type` -replace symbol DMX_PES_PCR3 :c:type:`dmx_pes_type` -replace symbol DMX_PES_OTHER :c:type:`dmx_pes_type` - -# Ignore obsolete symbols -ignore define DMX_PES_AUDIO -ignore define DMX_PES_VIDEO -ignore define DMX_PES_TELETEXT -ignore define DMX_PES_SUBTITLE -ignore define DMX_PES_PCR - -# dmx_input_t symbols -replace enum dmx_input :c:type:`dmx_input` -replace symbol DMX_IN_FRONTEND :c:type:`dmx_input` -replace symbol DMX_IN_DVR :c:type:`dmx_input` - -# Flags for struct dmx_sct_filter_params -replace define DMX_CHECK_CRC :c:type:`dmx_sct_filter_params` -replace define DMX_ONESHOT :c:type:`dmx_sct_filter_params` -replace define DMX_IMMEDIATE_START :c:type:`dmx_sct_filter_params` - -# some typedefs should point to struct/enums -replace typedef dmx_filter_t :c:type:`dmx_filter` -replace typedef dmx_pes_type_t :c:type:`dmx_pes_type` -replace typedef dmx_input_t :c:type:`dmx_input` - -replace symbol DMX_BUFFER_FLAG_HAD_CRC32_DISCARD :c:type:`dmx_buffer_flags` -replace symbol DMX_BUFFER_FLAG_TEI :c:type:`dmx_buffer_flags` -replace symbol DMX_BUFFER_PKT_COUNTER_MISMATCH :c:type:`dmx_buffer_flags` -replace symbol DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED :c:type:`dmx_buffer_flags` -replace symbol DMX_BUFFER_FLAG_DISCONTINUITY_INDICATOR :c:type:`dmx_buffer_flags` - -replace symbol DMX_OUT_DECODER :c:type:`dmx_output` -replace symbol DMX_OUT_TAP :c:type:`dmx_output` -replace symbol DMX_OUT_TS_TAP :c:type:`dmx_output` -replace symbol DMX_OUT_TSDEMUX_TAP :c:type:`dmx_output` - -replace ioctl DMX_DQBUF dmx_qbuf diff --git a/Documentation/userspace-api/media/dvb/ca.h.rst.exceptions b/Documentation/userspace-api/media/dvb/ca.h.rst.exceptions new file mode 100644 index 000000000000..f6828238eb48 --- /dev/null +++ b/Documentation/userspace-api/media/dvb/ca.h.rst.exceptions @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Ignore header name +ignore define _DVBCA_H_ + +# struct ca_slot_info defines +replace define CA_CI :c:type:`ca_slot_info` +replace define CA_CI_LINK :c:type:`ca_slot_info` +replace define CA_CI_PHYS :c:type:`ca_slot_info` +replace define CA_DESCR :c:type:`ca_slot_info` +replace define CA_SC :c:type:`ca_slot_info` +replace define CA_CI_MODULE_PRESENT :c:type:`ca_slot_info` +replace define CA_CI_MODULE_READY :c:type:`ca_slot_info` + +# struct ca_descr_info defines +replace define CA_ECD :c:type:`ca_descr_info` +replace define CA_NDS :c:type:`ca_descr_info` +replace define CA_DSS :c:type:`ca_descr_info` + +# some typedefs should point to struct/enums +replace typedef ca_slot_info_t :c:type:`ca_slot_info` +replace typedef ca_descr_info_t :c:type:`ca_descr_info` +replace typedef ca_caps_t :c:type:`ca_caps` +replace typedef ca_msg_t :c:type:`ca_msg` +replace typedef ca_descr_t :c:type:`ca_descr` diff --git a/Documentation/userspace-api/media/dvb/dmx.h.rst.exceptions b/Documentation/userspace-api/media/dvb/dmx.h.rst.exceptions new file mode 100644 index 000000000000..afc14d384b83 --- /dev/null +++ b/Documentation/userspace-api/media/dvb/dmx.h.rst.exceptions @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Ignore header name +ignore define _UAPI_DVBDMX_H_ + +# Ignore limit constants +ignore define DMX_FILTER_SIZE + +# dmx_pes_type_t enum symbols +replace enum dmx_ts_pes :c:type:`dmx_pes_type` +replace symbol DMX_PES_AUDIO0 :c:type:`dmx_pes_type` +replace symbol DMX_PES_VIDEO0 :c:type:`dmx_pes_type` +replace symbol DMX_PES_TELETEXT0 :c:type:`dmx_pes_type` +replace symbol DMX_PES_SUBTITLE0 :c:type:`dmx_pes_type` +replace symbol DMX_PES_PCR0 :c:type:`dmx_pes_type` +replace symbol DMX_PES_AUDIO1 :c:type:`dmx_pes_type` +replace symbol DMX_PES_VIDEO1 :c:type:`dmx_pes_type` +replace symbol DMX_PES_TELETEXT1 :c:type:`dmx_pes_type` +replace symbol DMX_PES_SUBTITLE1 :c:type:`dmx_pes_type` +replace symbol DMX_PES_PCR1 :c:type:`dmx_pes_type` +replace symbol DMX_PES_AUDIO2 :c:type:`dmx_pes_type` +replace symbol DMX_PES_VIDEO2 :c:type:`dmx_pes_type` +replace symbol DMX_PES_TELETEXT2 :c:type:`dmx_pes_type` +replace symbol DMX_PES_SUBTITLE2 :c:type:`dmx_pes_type` +replace symbol DMX_PES_PCR2 :c:type:`dmx_pes_type` +replace symbol DMX_PES_AUDIO3 :c:type:`dmx_pes_type` +replace symbol DMX_PES_VIDEO3 :c:type:`dmx_pes_type` +replace symbol DMX_PES_TELETEXT3 :c:type:`dmx_pes_type` +replace symbol DMX_PES_SUBTITLE3 :c:type:`dmx_pes_type` +replace symbol DMX_PES_PCR3 :c:type:`dmx_pes_type` +replace symbol DMX_PES_OTHER :c:type:`dmx_pes_type` + +# Ignore obsolete symbols +ignore define DMX_PES_AUDIO +ignore define DMX_PES_VIDEO +ignore define DMX_PES_TELETEXT +ignore define DMX_PES_SUBTITLE +ignore define DMX_PES_PCR + +# dmx_input_t symbols +replace enum dmx_input :c:type:`dmx_input` +replace symbol DMX_IN_FRONTEND :c:type:`dmx_input` +replace symbol DMX_IN_DVR :c:type:`dmx_input` + +# Flags for struct dmx_sct_filter_params +replace define DMX_CHECK_CRC :c:type:`dmx_sct_filter_params` +replace define DMX_ONESHOT :c:type:`dmx_sct_filter_params` +replace define DMX_IMMEDIATE_START :c:type:`dmx_sct_filter_params` + +# some typedefs should point to struct/enums +replace typedef dmx_filter_t :c:type:`dmx_filter` +replace typedef dmx_pes_type_t :c:type:`dmx_pes_type` +replace typedef dmx_input_t :c:type:`dmx_input` + +replace symbol DMX_BUFFER_FLAG_HAD_CRC32_DISCARD :c:type:`dmx_buffer_flags` +replace symbol DMX_BUFFER_FLAG_TEI :c:type:`dmx_buffer_flags` +replace symbol DMX_BUFFER_PKT_COUNTER_MISMATCH :c:type:`dmx_buffer_flags` +replace symbol DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED :c:type:`dmx_buffer_flags` +replace symbol DMX_BUFFER_FLAG_DISCONTINUITY_INDICATOR :c:type:`dmx_buffer_flags` + +replace symbol DMX_OUT_DECODER :c:type:`dmx_output` +replace symbol DMX_OUT_TAP :c:type:`dmx_output` +replace symbol DMX_OUT_TS_TAP :c:type:`dmx_output` +replace symbol DMX_OUT_TSDEMUX_TAP :c:type:`dmx_output` + +replace ioctl DMX_DQBUF dmx_qbuf diff --git a/Documentation/userspace-api/media/dvb/frontend.h.rst.exceptions b/Documentation/userspace-api/media/dvb/frontend.h.rst.exceptions new file mode 100644 index 000000000000..dcaf5740de7e --- /dev/null +++ b/Documentation/userspace-api/media/dvb/frontend.h.rst.exceptions @@ -0,0 +1,246 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Ignore header name +ignore define _DVBFRONTEND_H_ + +# Group layer A-C symbols together +replace define DTV_ISDBT_LAYERA_FEC dtv-isdbt-layer-fec +replace define DTV_ISDBT_LAYERB_FEC dtv-isdbt-layer-fec +replace define DTV_ISDBT_LAYERC_FEC dtv-isdbt-layer-fec +replace define DTV_ISDBT_LAYERA_MODULATION dtv-isdbt-layer-modulation +replace define DTV_ISDBT_LAYERB_MODULATION dtv-isdbt-layer-modulation +replace define DTV_ISDBT_LAYERC_MODULATION dtv-isdbt-layer-modulation +replace define DTV_ISDBT_LAYERA_SEGMENT_COUNT dtv-isdbt-layer-segment-count +replace define DTV_ISDBT_LAYERB_SEGMENT_COUNT dtv-isdbt-layer-segment-count +replace define DTV_ISDBT_LAYERC_SEGMENT_COUNT dtv-isdbt-layer-segment-count +replace define DTV_ISDBT_LAYERA_TIME_INTERLEAVING dtv-isdbt-layer-time-interleaving +replace define DTV_ISDBT_LAYERB_TIME_INTERLEAVING dtv-isdbt-layer-time-interleaving +replace define DTV_ISDBT_LAYERC_TIME_INTERLEAVING dtv-isdbt-layer-time-interleaving + +# Ignore legacy defines +ignore define DTV_ISDBS_TS_ID_LEGACY +ignore define SYS_DVBC_ANNEX_AC +ignore define SYS_DMBTH + +# Ignore limits +ignore define DTV_MAX_COMMAND +ignore define MAX_DTV_STATS +ignore define DTV_IOCTL_MAX_MSGS + +# the same reference is used for both get and set ioctls +replace ioctl FE_SET_PROPERTY :c:type:`FE_GET_PROPERTY` + +# Typedefs that use the enum reference +replace typedef fe_sec_voltage_t :c:type:`fe_sec_voltage` + +# Replaces for flag constants +replace define FE_TUNE_MODE_ONESHOT :c:func:`FE_SET_FRONTEND_TUNE_MODE` +replace define LNA_AUTO dtv-lna +replace define NO_STREAM_ID_FILTER dtv-stream-id + +# Those enums are defined at the frontend.h header, and not externally + +ignore symbol FE_IS_STUPID +ignore symbol FE_CAN_INVERSION_AUTO +ignore symbol FE_CAN_FEC_1_2 +ignore symbol FE_CAN_FEC_2_3 +ignore symbol FE_CAN_FEC_3_4 +ignore symbol FE_CAN_FEC_4_5 +ignore symbol FE_CAN_FEC_5_6 +ignore symbol FE_CAN_FEC_6_7 +ignore symbol FE_CAN_FEC_7_8 +ignore symbol FE_CAN_FEC_8_9 +ignore symbol FE_CAN_FEC_AUTO +ignore symbol FE_CAN_QPSK +ignore symbol FE_CAN_QAM_16 +ignore symbol FE_CAN_QAM_32 +ignore symbol FE_CAN_QAM_64 +ignore symbol FE_CAN_QAM_128 +ignore symbol FE_CAN_QAM_256 +ignore symbol FE_CAN_QAM_AUTO +ignore symbol FE_CAN_TRANSMISSION_MODE_AUTO +ignore symbol FE_CAN_BANDWIDTH_AUTO +ignore symbol FE_CAN_GUARD_INTERVAL_AUTO +ignore symbol FE_CAN_HIERARCHY_AUTO +ignore symbol FE_CAN_8VSB +ignore symbol FE_CAN_16VSB +ignore symbol FE_HAS_EXTENDED_CAPS +ignore symbol FE_CAN_MULTISTREAM +ignore symbol FE_CAN_TURBO_FEC +ignore symbol FE_CAN_2G_MODULATION +ignore symbol FE_NEEDS_BENDING +ignore symbol FE_CAN_RECOVER +ignore symbol FE_CAN_MUTE_TS + +ignore symbol QPSK +ignore symbol QAM_16 +ignore symbol QAM_32 +ignore symbol QAM_64 +ignore symbol QAM_128 +ignore symbol QAM_256 +ignore symbol QAM_AUTO +ignore symbol VSB_8 +ignore symbol VSB_16 +ignore symbol PSK_8 +ignore symbol APSK_16 +ignore symbol APSK_32 +ignore symbol DQPSK +ignore symbol QAM_4_NR +ignore symbol QAM_1024 +ignore symbol QAM_4096 +ignore symbol APSK_8_L +ignore symbol APSK_16_L +ignore symbol APSK_32_L +ignore symbol APSK_64 +ignore symbol APSK_64_L + +ignore symbol SEC_VOLTAGE_13 +ignore symbol SEC_VOLTAGE_18 +ignore symbol SEC_VOLTAGE_OFF + +ignore symbol SEC_TONE_ON +ignore symbol SEC_TONE_OFF + +ignore symbol SEC_MINI_A +ignore symbol SEC_MINI_B + +ignore symbol FE_NONE +ignore symbol FE_HAS_SIGNAL +ignore symbol FE_HAS_CARRIER +ignore symbol FE_HAS_VITERBI +ignore symbol FE_HAS_SYNC +ignore symbol FE_HAS_LOCK +ignore symbol FE_REINIT +ignore symbol FE_TIMEDOUT + +ignore symbol FEC_NONE +ignore symbol FEC_1_2 +ignore symbol FEC_2_3 +ignore symbol FEC_3_4 +ignore symbol FEC_4_5 +ignore symbol FEC_5_6 +ignore symbol FEC_6_7 +ignore symbol FEC_7_8 +ignore symbol FEC_8_9 +ignore symbol FEC_AUTO +ignore symbol FEC_3_5 +ignore symbol FEC_9_10 +ignore symbol FEC_2_5 +ignore symbol FEC_1_3 +ignore symbol FEC_1_4 +ignore symbol FEC_5_9 +ignore symbol FEC_7_9 +ignore symbol FEC_8_15 +ignore symbol FEC_11_15 +ignore symbol FEC_13_18 +ignore symbol FEC_9_20 +ignore symbol FEC_11_20 +ignore symbol FEC_23_36 +ignore symbol FEC_25_36 +ignore symbol FEC_13_45 +ignore symbol FEC_26_45 +ignore symbol FEC_28_45 +ignore symbol FEC_32_45 +ignore symbol FEC_77_90 +ignore symbol FEC_11_45 +ignore symbol FEC_4_15 +ignore symbol FEC_14_45 +ignore symbol FEC_7_15 + +ignore symbol TRANSMISSION_MODE_AUTO +ignore symbol TRANSMISSION_MODE_1K +ignore symbol TRANSMISSION_MODE_2K +ignore symbol TRANSMISSION_MODE_8K +ignore symbol TRANSMISSION_MODE_4K +ignore symbol TRANSMISSION_MODE_16K +ignore symbol TRANSMISSION_MODE_32K +ignore symbol TRANSMISSION_MODE_C1 +ignore symbol TRANSMISSION_MODE_C3780 +ignore symbol TRANSMISSION_MODE_2K +ignore symbol TRANSMISSION_MODE_8K + +ignore symbol GUARD_INTERVAL_AUTO +ignore symbol GUARD_INTERVAL_1_128 +ignore symbol GUARD_INTERVAL_1_32 +ignore symbol GUARD_INTERVAL_1_16 +ignore symbol GUARD_INTERVAL_1_8 +ignore symbol GUARD_INTERVAL_1_4 +ignore symbol GUARD_INTERVAL_19_128 +ignore symbol GUARD_INTERVAL_19_256 +ignore symbol GUARD_INTERVAL_PN420 +ignore symbol GUARD_INTERVAL_PN595 +ignore symbol GUARD_INTERVAL_PN945 +ignore symbol GUARD_INTERVAL_1_64 + +ignore symbol HIERARCHY_NONE +ignore symbol HIERARCHY_AUTO +ignore symbol HIERARCHY_1 +ignore symbol HIERARCHY_2 +ignore symbol HIERARCHY_4 + +ignore symbol INTERLEAVING_NONE +ignore symbol INTERLEAVING_AUTO +ignore symbol INTERLEAVING_240 +ignore symbol INTERLEAVING_720 + +ignore symbol PILOT_ON +ignore symbol PILOT_OFF +ignore symbol PILOT_AUTO + +ignore symbol ROLLOFF_35 +ignore symbol ROLLOFF_20 +ignore symbol ROLLOFF_25 +ignore symbol ROLLOFF_AUTO +ignore symbol ROLLOFF_15 +ignore symbol ROLLOFF_10 +ignore symbol ROLLOFF_5 + +ignore symbol INVERSION_ON +ignore symbol INVERSION_OFF +ignore symbol INVERSION_AUTO + +ignore symbol SYS_UNDEFINED +ignore symbol SYS_DVBC_ANNEX_A +ignore symbol SYS_DVBC_ANNEX_B +ignore symbol SYS_DVBC_ANNEX_C +ignore symbol SYS_ISDBC +ignore symbol SYS_DVBT +ignore symbol SYS_DVBT2 +ignore symbol SYS_ISDBT +ignore symbol SYS_ATSC +ignore symbol SYS_ATSCMH +ignore symbol SYS_DTMB +ignore symbol SYS_DVBS +ignore symbol SYS_DVBS2 +ignore symbol SYS_TURBO +ignore symbol SYS_ISDBS +ignore symbol SYS_DAB +ignore symbol SYS_DSS +ignore symbol SYS_CMMB +ignore symbol SYS_DVBH +ignore symbol SYS_DVBC2 + +ignore symbol ATSCMH_SCCC_BLK_SEP +ignore symbol ATSCMH_SCCC_BLK_COMB +ignore symbol ATSCMH_SCCC_BLK_RES + +ignore symbol ATSCMH_SCCC_CODE_HLF +ignore symbol ATSCMH_SCCC_CODE_QTR +ignore symbol ATSCMH_SCCC_CODE_RES + +ignore symbol ATSCMH_RSFRAME_ENS_PRI +ignore symbol ATSCMH_RSFRAME_ENS_SEC + +ignore symbol ATSCMH_RSFRAME_PRI_ONLY +ignore symbol ATSCMH_RSFRAME_PRI_SEC +ignore symbol ATSCMH_RSFRAME_RES + +ignore symbol ATSCMH_RSCODE_211_187 +ignore symbol ATSCMH_RSCODE_223_187 +ignore symbol ATSCMH_RSCODE_235_187 +ignore symbol ATSCMH_RSCODE_RES + +ignore symbol FE_SCALE_NOT_AVAILABLE +ignore symbol FE_SCALE_DECIBEL +ignore symbol FE_SCALE_RELATIVE +ignore symbol FE_SCALE_COUNTER diff --git a/Documentation/userspace-api/media/dvb/headers.rst b/Documentation/userspace-api/media/dvb/headers.rst index 88c3eb33a89e..c75f64cf21d5 100644 --- a/Documentation/userspace-api/media/dvb/headers.rst +++ b/Documentation/userspace-api/media/dvb/headers.rst @@ -7,10 +7,19 @@ Digital TV uAPI header files Digital TV uAPI headers *********************** -.. kernel-include:: $BUILDDIR/frontend.h.rst +.. kernel-include:: include/uapi/linux/dvb/frontend.h + :generate-cross-refs: + :exception-file: frontend.h.rst.exceptions -.. kernel-include:: $BUILDDIR/dmx.h.rst +.. kernel-include:: include/uapi/linux/dvb/dmx.h + :generate-cross-refs: + :exception-file: dmx.h.rst.exceptions -.. kernel-include:: $BUILDDIR/ca.h.rst +.. kernel-include:: include/uapi/linux/dvb/ca.h + :generate-cross-refs: + :exception-file: ca.h.rst.exceptions + +.. kernel-include:: include/uapi/linux/dvb/net.h + :generate-cross-refs: + :exception-file: net.h.rst.exceptions -.. kernel-include:: $BUILDDIR/net.h.rst diff --git a/Documentation/userspace-api/media/dvb/net.h.rst.exceptions b/Documentation/userspace-api/media/dvb/net.h.rst.exceptions new file mode 100644 index 000000000000..5159aa4bbbb9 --- /dev/null +++ b/Documentation/userspace-api/media/dvb/net.h.rst.exceptions @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Ignore header name +ignore define _DVBNET_H_ + +# Ignore old ioctls/structs +ignore ioctl __NET_ADD_IF_OLD +ignore ioctl __NET_GET_IF_OLD +ignore struct __dvb_net_if_old + +# Macros used at struct dvb_net_if +replace define DVB_NET_FEEDTYPE_MPE :c:type:`dvb_net_if` +replace define DVB_NET_FEEDTYPE_ULE :c:type:`dvb_net_if` diff --git a/Documentation/userspace-api/media/frontend.h.rst.exceptions b/Documentation/userspace-api/media/frontend.h.rst.exceptions deleted file mode 100644 index dcaf5740de7e..000000000000 --- a/Documentation/userspace-api/media/frontend.h.rst.exceptions +++ /dev/null @@ -1,246 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -# Ignore header name -ignore define _DVBFRONTEND_H_ - -# Group layer A-C symbols together -replace define DTV_ISDBT_LAYERA_FEC dtv-isdbt-layer-fec -replace define DTV_ISDBT_LAYERB_FEC dtv-isdbt-layer-fec -replace define DTV_ISDBT_LAYERC_FEC dtv-isdbt-layer-fec -replace define DTV_ISDBT_LAYERA_MODULATION dtv-isdbt-layer-modulation -replace define DTV_ISDBT_LAYERB_MODULATION dtv-isdbt-layer-modulation -replace define DTV_ISDBT_LAYERC_MODULATION dtv-isdbt-layer-modulation -replace define DTV_ISDBT_LAYERA_SEGMENT_COUNT dtv-isdbt-layer-segment-count -replace define DTV_ISDBT_LAYERB_SEGMENT_COUNT dtv-isdbt-layer-segment-count -replace define DTV_ISDBT_LAYERC_SEGMENT_COUNT dtv-isdbt-layer-segment-count -replace define DTV_ISDBT_LAYERA_TIME_INTERLEAVING dtv-isdbt-layer-time-interleaving -replace define DTV_ISDBT_LAYERB_TIME_INTERLEAVING dtv-isdbt-layer-time-interleaving -replace define DTV_ISDBT_LAYERC_TIME_INTERLEAVING dtv-isdbt-layer-time-interleaving - -# Ignore legacy defines -ignore define DTV_ISDBS_TS_ID_LEGACY -ignore define SYS_DVBC_ANNEX_AC -ignore define SYS_DMBTH - -# Ignore limits -ignore define DTV_MAX_COMMAND -ignore define MAX_DTV_STATS -ignore define DTV_IOCTL_MAX_MSGS - -# the same reference is used for both get and set ioctls -replace ioctl FE_SET_PROPERTY :c:type:`FE_GET_PROPERTY` - -# Typedefs that use the enum reference -replace typedef fe_sec_voltage_t :c:type:`fe_sec_voltage` - -# Replaces for flag constants -replace define FE_TUNE_MODE_ONESHOT :c:func:`FE_SET_FRONTEND_TUNE_MODE` -replace define LNA_AUTO dtv-lna -replace define NO_STREAM_ID_FILTER dtv-stream-id - -# Those enums are defined at the frontend.h header, and not externally - -ignore symbol FE_IS_STUPID -ignore symbol FE_CAN_INVERSION_AUTO -ignore symbol FE_CAN_FEC_1_2 -ignore symbol FE_CAN_FEC_2_3 -ignore symbol FE_CAN_FEC_3_4 -ignore symbol FE_CAN_FEC_4_5 -ignore symbol FE_CAN_FEC_5_6 -ignore symbol FE_CAN_FEC_6_7 -ignore symbol FE_CAN_FEC_7_8 -ignore symbol FE_CAN_FEC_8_9 -ignore symbol FE_CAN_FEC_AUTO -ignore symbol FE_CAN_QPSK -ignore symbol FE_CAN_QAM_16 -ignore symbol FE_CAN_QAM_32 -ignore symbol FE_CAN_QAM_64 -ignore symbol FE_CAN_QAM_128 -ignore symbol FE_CAN_QAM_256 -ignore symbol FE_CAN_QAM_AUTO -ignore symbol FE_CAN_TRANSMISSION_MODE_AUTO -ignore symbol FE_CAN_BANDWIDTH_AUTO -ignore symbol FE_CAN_GUARD_INTERVAL_AUTO -ignore symbol FE_CAN_HIERARCHY_AUTO -ignore symbol FE_CAN_8VSB -ignore symbol FE_CAN_16VSB -ignore symbol FE_HAS_EXTENDED_CAPS -ignore symbol FE_CAN_MULTISTREAM -ignore symbol FE_CAN_TURBO_FEC -ignore symbol FE_CAN_2G_MODULATION -ignore symbol FE_NEEDS_BENDING -ignore symbol FE_CAN_RECOVER -ignore symbol FE_CAN_MUTE_TS - -ignore symbol QPSK -ignore symbol QAM_16 -ignore symbol QAM_32 -ignore symbol QAM_64 -ignore symbol QAM_128 -ignore symbol QAM_256 -ignore symbol QAM_AUTO -ignore symbol VSB_8 -ignore symbol VSB_16 -ignore symbol PSK_8 -ignore symbol APSK_16 -ignore symbol APSK_32 -ignore symbol DQPSK -ignore symbol QAM_4_NR -ignore symbol QAM_1024 -ignore symbol QAM_4096 -ignore symbol APSK_8_L -ignore symbol APSK_16_L -ignore symbol APSK_32_L -ignore symbol APSK_64 -ignore symbol APSK_64_L - -ignore symbol SEC_VOLTAGE_13 -ignore symbol SEC_VOLTAGE_18 -ignore symbol SEC_VOLTAGE_OFF - -ignore symbol SEC_TONE_ON -ignore symbol SEC_TONE_OFF - -ignore symbol SEC_MINI_A -ignore symbol SEC_MINI_B - -ignore symbol FE_NONE -ignore symbol FE_HAS_SIGNAL -ignore symbol FE_HAS_CARRIER -ignore symbol FE_HAS_VITERBI -ignore symbol FE_HAS_SYNC -ignore symbol FE_HAS_LOCK -ignore symbol FE_REINIT -ignore symbol FE_TIMEDOUT - -ignore symbol FEC_NONE -ignore symbol FEC_1_2 -ignore symbol FEC_2_3 -ignore symbol FEC_3_4 -ignore symbol FEC_4_5 -ignore symbol FEC_5_6 -ignore symbol FEC_6_7 -ignore symbol FEC_7_8 -ignore symbol FEC_8_9 -ignore symbol FEC_AUTO -ignore symbol FEC_3_5 -ignore symbol FEC_9_10 -ignore symbol FEC_2_5 -ignore symbol FEC_1_3 -ignore symbol FEC_1_4 -ignore symbol FEC_5_9 -ignore symbol FEC_7_9 -ignore symbol FEC_8_15 -ignore symbol FEC_11_15 -ignore symbol FEC_13_18 -ignore symbol FEC_9_20 -ignore symbol FEC_11_20 -ignore symbol FEC_23_36 -ignore symbol FEC_25_36 -ignore symbol FEC_13_45 -ignore symbol FEC_26_45 -ignore symbol FEC_28_45 -ignore symbol FEC_32_45 -ignore symbol FEC_77_90 -ignore symbol FEC_11_45 -ignore symbol FEC_4_15 -ignore symbol FEC_14_45 -ignore symbol FEC_7_15 - -ignore symbol TRANSMISSION_MODE_AUTO -ignore symbol TRANSMISSION_MODE_1K -ignore symbol TRANSMISSION_MODE_2K -ignore symbol TRANSMISSION_MODE_8K -ignore symbol TRANSMISSION_MODE_4K -ignore symbol TRANSMISSION_MODE_16K -ignore symbol TRANSMISSION_MODE_32K -ignore symbol TRANSMISSION_MODE_C1 -ignore symbol TRANSMISSION_MODE_C3780 -ignore symbol TRANSMISSION_MODE_2K -ignore symbol TRANSMISSION_MODE_8K - -ignore symbol GUARD_INTERVAL_AUTO -ignore symbol GUARD_INTERVAL_1_128 -ignore symbol GUARD_INTERVAL_1_32 -ignore symbol GUARD_INTERVAL_1_16 -ignore symbol GUARD_INTERVAL_1_8 -ignore symbol GUARD_INTERVAL_1_4 -ignore symbol GUARD_INTERVAL_19_128 -ignore symbol GUARD_INTERVAL_19_256 -ignore symbol GUARD_INTERVAL_PN420 -ignore symbol GUARD_INTERVAL_PN595 -ignore symbol GUARD_INTERVAL_PN945 -ignore symbol GUARD_INTERVAL_1_64 - -ignore symbol HIERARCHY_NONE -ignore symbol HIERARCHY_AUTO -ignore symbol HIERARCHY_1 -ignore symbol HIERARCHY_2 -ignore symbol HIERARCHY_4 - -ignore symbol INTERLEAVING_NONE -ignore symbol INTERLEAVING_AUTO -ignore symbol INTERLEAVING_240 -ignore symbol INTERLEAVING_720 - -ignore symbol PILOT_ON -ignore symbol PILOT_OFF -ignore symbol PILOT_AUTO - -ignore symbol ROLLOFF_35 -ignore symbol ROLLOFF_20 -ignore symbol ROLLOFF_25 -ignore symbol ROLLOFF_AUTO -ignore symbol ROLLOFF_15 -ignore symbol ROLLOFF_10 -ignore symbol ROLLOFF_5 - -ignore symbol INVERSION_ON -ignore symbol INVERSION_OFF -ignore symbol INVERSION_AUTO - -ignore symbol SYS_UNDEFINED -ignore symbol SYS_DVBC_ANNEX_A -ignore symbol SYS_DVBC_ANNEX_B -ignore symbol SYS_DVBC_ANNEX_C -ignore symbol SYS_ISDBC -ignore symbol SYS_DVBT -ignore symbol SYS_DVBT2 -ignore symbol SYS_ISDBT -ignore symbol SYS_ATSC -ignore symbol SYS_ATSCMH -ignore symbol SYS_DTMB -ignore symbol SYS_DVBS -ignore symbol SYS_DVBS2 -ignore symbol SYS_TURBO -ignore symbol SYS_ISDBS -ignore symbol SYS_DAB -ignore symbol SYS_DSS -ignore symbol SYS_CMMB -ignore symbol SYS_DVBH -ignore symbol SYS_DVBC2 - -ignore symbol ATSCMH_SCCC_BLK_SEP -ignore symbol ATSCMH_SCCC_BLK_COMB -ignore symbol ATSCMH_SCCC_BLK_RES - -ignore symbol ATSCMH_SCCC_CODE_HLF -ignore symbol ATSCMH_SCCC_CODE_QTR -ignore symbol ATSCMH_SCCC_CODE_RES - -ignore symbol ATSCMH_RSFRAME_ENS_PRI -ignore symbol ATSCMH_RSFRAME_ENS_SEC - -ignore symbol ATSCMH_RSFRAME_PRI_ONLY -ignore symbol ATSCMH_RSFRAME_PRI_SEC -ignore symbol ATSCMH_RSFRAME_RES - -ignore symbol ATSCMH_RSCODE_211_187 -ignore symbol ATSCMH_RSCODE_223_187 -ignore symbol ATSCMH_RSCODE_235_187 -ignore symbol ATSCMH_RSCODE_RES - -ignore symbol FE_SCALE_NOT_AVAILABLE -ignore symbol FE_SCALE_DECIBEL -ignore symbol FE_SCALE_RELATIVE -ignore symbol FE_SCALE_COUNTER diff --git a/Documentation/userspace-api/media/lirc.h.rst.exceptions b/Documentation/userspace-api/media/lirc.h.rst.exceptions deleted file mode 100644 index 1aeb7d7afe13..000000000000 --- a/Documentation/userspace-api/media/lirc.h.rst.exceptions +++ /dev/null @@ -1,87 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -# Ignore header name -ignore define _LINUX_LIRC_H - -# Ignore helper macros - -ignore define lirc_t - -ignore define LIRC_SPACE -ignore define LIRC_PULSE -ignore define LIRC_FREQUENCY -ignore define LIRC_TIMEOUT -ignore define LIRC_OVERFLOW -ignore define LIRC_VALUE -ignore define LIRC_MODE2 -ignore define LIRC_IS_SPACE -ignore define LIRC_IS_PULSE -ignore define LIRC_IS_FREQUENCY -ignore define LIRC_IS_TIMEOUT -ignore define LIRC_IS_OVERFLOW - -ignore define LIRC_MODE2SEND -ignore define LIRC_SEND2MODE -ignore define LIRC_MODE2REC -ignore define LIRC_REC2MODE - -ignore define LIRC_CAN_SEND -ignore define LIRC_CAN_REC - -ignore define LIRC_CAN_SEND_MASK -ignore define LIRC_CAN_REC_MASK -ignore define LIRC_CAN_SET_REC_FILTER -ignore define LIRC_CAN_NOTIFY_DECODE - -# Obsolete ioctls - -ignore ioctl LIRC_GET_LENGTH -ignore ioctl LIRC_SET_REC_TIMEOUT_REPORTS - -# rc protocols - -ignore symbol RC_PROTO_UNKNOWN -ignore symbol RC_PROTO_OTHER -ignore symbol RC_PROTO_RC5 -ignore symbol RC_PROTO_RC5X_20 -ignore symbol RC_PROTO_RC5_SZ -ignore symbol RC_PROTO_JVC -ignore symbol RC_PROTO_SONY12 -ignore symbol RC_PROTO_SONY15 -ignore symbol RC_PROTO_SONY20 -ignore symbol RC_PROTO_NEC -ignore symbol RC_PROTO_NECX -ignore symbol RC_PROTO_NEC32 -ignore symbol RC_PROTO_SANYO -ignore symbol RC_PROTO_MCIR2_KBD -ignore symbol RC_PROTO_MCIR2_MSE -ignore symbol RC_PROTO_RC6_0 -ignore symbol RC_PROTO_RC6_6A_20 -ignore symbol RC_PROTO_RC6_6A_24 -ignore symbol RC_PROTO_RC6_6A_32 -ignore symbol RC_PROTO_RC6_MCE -ignore symbol RC_PROTO_SHARP -ignore symbol RC_PROTO_XMP -ignore symbol RC_PROTO_CEC -ignore symbol RC_PROTO_IMON -ignore symbol RC_PROTO_RCMM12 -ignore symbol RC_PROTO_RCMM24 -ignore symbol RC_PROTO_RCMM32 -ignore symbol RC_PROTO_XBOX_DVD -ignore symbol RC_PROTO_MAX - -# Undocumented macros - -ignore define PULSE_BIT -ignore define PULSE_MASK - -ignore define LIRC_MODE2_SPACE -ignore define LIRC_MODE2_PULSE -ignore define LIRC_MODE2_TIMEOUT -ignore define LIRC_MODE2_OVERFLOW - -ignore define LIRC_VALUE_MASK -ignore define LIRC_MODE2_MASK - -ignore define LIRC_MODE_RAW -ignore define LIRC_MODE_LIRCCODE diff --git a/Documentation/userspace-api/media/media.h.rst.exceptions b/Documentation/userspace-api/media/media.h.rst.exceptions deleted file mode 100644 index 9b4c26502d95..000000000000 --- a/Documentation/userspace-api/media/media.h.rst.exceptions +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -# Ignore header name -ignore define __LINUX_MEDIA_H - -# Ignore macros -ignore define MEDIA_API_VERSION -ignore define MEDIA_ENT_F_BASE -ignore define MEDIA_ENT_F_OLD_BASE -ignore define MEDIA_ENT_F_OLD_SUBDEV_BASE -ignore define MEDIA_ENT_F_DTV_DECODER -ignore define MEDIA_INTF_T_DVB_BASE -ignore define MEDIA_INTF_T_V4L_BASE -ignore define MEDIA_INTF_T_ALSA_BASE -#ignore legacy entity type macros -ignore define MEDIA_ENT_TYPE_SHIFT -ignore define MEDIA_ENT_TYPE_MASK -ignore define MEDIA_ENT_SUBTYPE_MASK -ignore define MEDIA_ENT_T_DEVNODE_UNKNOWN -ignore define MEDIA_ENT_T_DEVNODE -ignore define MEDIA_ENT_T_DEVNODE_V4L -ignore define MEDIA_ENT_T_DEVNODE_FB -ignore define MEDIA_ENT_T_DEVNODE_ALSA -ignore define MEDIA_ENT_T_DEVNODE_DVB -ignore define MEDIA_ENT_T_UNKNOWN -ignore define MEDIA_ENT_T_V4L2_VIDEO -ignore define MEDIA_ENT_T_V4L2_SUBDEV -ignore define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR -ignore define MEDIA_ENT_T_V4L2_SUBDEV_FLASH -ignore define MEDIA_ENT_T_V4L2_SUBDEV_LENS -ignore define MEDIA_ENT_T_V4L2_SUBDEV_DECODER -ignore define MEDIA_ENT_T_V4L2_SUBDEV_TUNER diff --git a/Documentation/userspace-api/media/mediactl/media-header.rst b/Documentation/userspace-api/media/mediactl/media-header.rst index c674271c93f5..d561d2845f3d 100644 --- a/Documentation/userspace-api/media/mediactl/media-header.rst +++ b/Documentation/userspace-api/media/mediactl/media-header.rst @@ -6,5 +6,6 @@ Media Controller Header File **************************** -.. kernel-include:: $BUILDDIR/media.h.rst - +.. kernel-include:: include/uapi/linux/media.h + :generate-cross-refs: + :exception-file: media.h.rst.exceptions diff --git a/Documentation/userspace-api/media/mediactl/media.h.rst.exceptions b/Documentation/userspace-api/media/mediactl/media.h.rst.exceptions new file mode 100644 index 000000000000..9b4c26502d95 --- /dev/null +++ b/Documentation/userspace-api/media/mediactl/media.h.rst.exceptions @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Ignore header name +ignore define __LINUX_MEDIA_H + +# Ignore macros +ignore define MEDIA_API_VERSION +ignore define MEDIA_ENT_F_BASE +ignore define MEDIA_ENT_F_OLD_BASE +ignore define MEDIA_ENT_F_OLD_SUBDEV_BASE +ignore define MEDIA_ENT_F_DTV_DECODER +ignore define MEDIA_INTF_T_DVB_BASE +ignore define MEDIA_INTF_T_V4L_BASE +ignore define MEDIA_INTF_T_ALSA_BASE +#ignore legacy entity type macros +ignore define MEDIA_ENT_TYPE_SHIFT +ignore define MEDIA_ENT_TYPE_MASK +ignore define MEDIA_ENT_SUBTYPE_MASK +ignore define MEDIA_ENT_T_DEVNODE_UNKNOWN +ignore define MEDIA_ENT_T_DEVNODE +ignore define MEDIA_ENT_T_DEVNODE_V4L +ignore define MEDIA_ENT_T_DEVNODE_FB +ignore define MEDIA_ENT_T_DEVNODE_ALSA +ignore define MEDIA_ENT_T_DEVNODE_DVB +ignore define MEDIA_ENT_T_UNKNOWN +ignore define MEDIA_ENT_T_V4L2_VIDEO +ignore define MEDIA_ENT_T_V4L2_SUBDEV +ignore define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR +ignore define MEDIA_ENT_T_V4L2_SUBDEV_FLASH +ignore define MEDIA_ENT_T_V4L2_SUBDEV_LENS +ignore define MEDIA_ENT_T_V4L2_SUBDEV_DECODER +ignore define MEDIA_ENT_T_V4L2_SUBDEV_TUNER diff --git a/Documentation/userspace-api/media/net.h.rst.exceptions b/Documentation/userspace-api/media/net.h.rst.exceptions deleted file mode 100644 index 5159aa4bbbb9..000000000000 --- a/Documentation/userspace-api/media/net.h.rst.exceptions +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -# Ignore header name -ignore define _DVBNET_H_ - -# Ignore old ioctls/structs -ignore ioctl __NET_ADD_IF_OLD -ignore ioctl __NET_GET_IF_OLD -ignore struct __dvb_net_if_old - -# Macros used at struct dvb_net_if -replace define DVB_NET_FEEDTYPE_MPE :c:type:`dvb_net_if` -replace define DVB_NET_FEEDTYPE_ULE :c:type:`dvb_net_if` diff --git a/Documentation/userspace-api/media/rc/lirc-header.rst b/Documentation/userspace-api/media/rc/lirc-header.rst index 54cb40b8a065..a53328327847 100644 --- a/Documentation/userspace-api/media/rc/lirc-header.rst +++ b/Documentation/userspace-api/media/rc/lirc-header.rst @@ -6,5 +6,7 @@ LIRC Header File **************** -.. kernel-include:: $BUILDDIR/lirc.h.rst +.. kernel-include:: include/uapi/linux/lirc.h + :generate-cross-refs: + :exception-file: lirc.h.rst.exceptions diff --git a/Documentation/userspace-api/media/rc/lirc.h.rst.exceptions b/Documentation/userspace-api/media/rc/lirc.h.rst.exceptions new file mode 100644 index 000000000000..1aeb7d7afe13 --- /dev/null +++ b/Documentation/userspace-api/media/rc/lirc.h.rst.exceptions @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Ignore header name +ignore define _LINUX_LIRC_H + +# Ignore helper macros + +ignore define lirc_t + +ignore define LIRC_SPACE +ignore define LIRC_PULSE +ignore define LIRC_FREQUENCY +ignore define LIRC_TIMEOUT +ignore define LIRC_OVERFLOW +ignore define LIRC_VALUE +ignore define LIRC_MODE2 +ignore define LIRC_IS_SPACE +ignore define LIRC_IS_PULSE +ignore define LIRC_IS_FREQUENCY +ignore define LIRC_IS_TIMEOUT +ignore define LIRC_IS_OVERFLOW + +ignore define LIRC_MODE2SEND +ignore define LIRC_SEND2MODE +ignore define LIRC_MODE2REC +ignore define LIRC_REC2MODE + +ignore define LIRC_CAN_SEND +ignore define LIRC_CAN_REC + +ignore define LIRC_CAN_SEND_MASK +ignore define LIRC_CAN_REC_MASK +ignore define LIRC_CAN_SET_REC_FILTER +ignore define LIRC_CAN_NOTIFY_DECODE + +# Obsolete ioctls + +ignore ioctl LIRC_GET_LENGTH +ignore ioctl LIRC_SET_REC_TIMEOUT_REPORTS + +# rc protocols + +ignore symbol RC_PROTO_UNKNOWN +ignore symbol RC_PROTO_OTHER +ignore symbol RC_PROTO_RC5 +ignore symbol RC_PROTO_RC5X_20 +ignore symbol RC_PROTO_RC5_SZ +ignore symbol RC_PROTO_JVC +ignore symbol RC_PROTO_SONY12 +ignore symbol RC_PROTO_SONY15 +ignore symbol RC_PROTO_SONY20 +ignore symbol RC_PROTO_NEC +ignore symbol RC_PROTO_NECX +ignore symbol RC_PROTO_NEC32 +ignore symbol RC_PROTO_SANYO +ignore symbol RC_PROTO_MCIR2_KBD +ignore symbol RC_PROTO_MCIR2_MSE +ignore symbol RC_PROTO_RC6_0 +ignore symbol RC_PROTO_RC6_6A_20 +ignore symbol RC_PROTO_RC6_6A_24 +ignore symbol RC_PROTO_RC6_6A_32 +ignore symbol RC_PROTO_RC6_MCE +ignore symbol RC_PROTO_SHARP +ignore symbol RC_PROTO_XMP +ignore symbol RC_PROTO_CEC +ignore symbol RC_PROTO_IMON +ignore symbol RC_PROTO_RCMM12 +ignore symbol RC_PROTO_RCMM24 +ignore symbol RC_PROTO_RCMM32 +ignore symbol RC_PROTO_XBOX_DVD +ignore symbol RC_PROTO_MAX + +# Undocumented macros + +ignore define PULSE_BIT +ignore define PULSE_MASK + +ignore define LIRC_MODE2_SPACE +ignore define LIRC_MODE2_PULSE +ignore define LIRC_MODE2_TIMEOUT +ignore define LIRC_MODE2_OVERFLOW + +ignore define LIRC_VALUE_MASK +ignore define LIRC_MODE2_MASK + +ignore define LIRC_MODE_RAW +ignore define LIRC_MODE_LIRCCODE diff --git a/Documentation/userspace-api/media/v4l/videodev.rst b/Documentation/userspace-api/media/v4l/videodev.rst index c866fec417eb..cde485bc9a5f 100644 --- a/Documentation/userspace-api/media/v4l/videodev.rst +++ b/Documentation/userspace-api/media/v4l/videodev.rst @@ -6,4 +6,6 @@ Video For Linux Two Header File ******************************* -.. kernel-include:: $BUILDDIR/videodev2.h.rst +.. kernel-include:: include/uapi/linux/videodev2.h + :generate-cross-refs: + :exception-file: videodev2.h.rst.exceptions diff --git a/Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions new file mode 100644 index 000000000000..35d3456cc812 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions @@ -0,0 +1,610 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Ignore header name +ignore define _UAPI__LINUX_VIDEODEV2_H + +# +# The cross reference valitator for videodev2.h DocBook never cared +# about enum symbols or defines. Yet, they're all (or almost all?) +# handled inside V4L API sections. So, for now, it is safe to just +# ignore. This should be revisited, as validating it helps to avoid +# having something not documented at the uAPI. +# + +# Those symbols should not be used by uAPI - don't document them +ignore symbol V4L2_BUF_TYPE_PRIVATE +ignore symbol V4L2_TUNER_DIGITAL_TV +ignore symbol V4L2_COLORSPACE_BT878 + +# Documented enum v4l2_field +replace symbol V4L2_FIELD_ALTERNATE :c:type:`v4l2_field` +replace symbol V4L2_FIELD_ANY :c:type:`v4l2_field` +replace symbol V4L2_FIELD_BOTTOM :c:type:`v4l2_field` +replace symbol V4L2_FIELD_INTERLACED :c:type:`v4l2_field` +replace symbol V4L2_FIELD_INTERLACED_BT :c:type:`v4l2_field` +replace symbol V4L2_FIELD_INTERLACED_TB :c:type:`v4l2_field` +replace symbol V4L2_FIELD_NONE :c:type:`v4l2_field` +replace symbol V4L2_FIELD_SEQ_BT :c:type:`v4l2_field` +replace symbol V4L2_FIELD_SEQ_TB :c:type:`v4l2_field` +replace symbol V4L2_FIELD_TOP :c:type:`v4l2_field` + +# Documented enum v4l2_buf_type +replace symbol V4L2_BUF_TYPE_META_CAPTURE :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_META_OUTPUT :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_SDR_CAPTURE :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_SDR_OUTPUT :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_SLICED_VBI_CAPTURE :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_SLICED_VBI_OUTPUT :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_VBI_CAPTURE :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_VBI_OUTPUT :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_VIDEO_CAPTURE :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY :c:type:`v4l2_buf_type` +replace symbol V4L2_BUF_TYPE_VIDEO_OVERLAY :c:type:`v4l2_buf_type` + +# Documented enum v4l2_tuner_type +replace symbol V4L2_TUNER_ANALOG_TV :c:type:`v4l2_tuner_type` +replace symbol V4L2_TUNER_RADIO :c:type:`v4l2_tuner_type` +replace symbol V4L2_TUNER_RF :c:type:`v4l2_tuner_type` +replace symbol V4L2_TUNER_SDR :c:type:`v4l2_tuner_type` + +# Documented enum v4l2_memory +replace symbol V4L2_MEMORY_DMABUF :c:type:`v4l2_memory` +replace symbol V4L2_MEMORY_MMAP :c:type:`v4l2_memory` +replace symbol V4L2_MEMORY_OVERLAY :c:type:`v4l2_memory` +replace symbol V4L2_MEMORY_USERPTR :c:type:`v4l2_memory` + +# Documented enum v4l2_colorspace +replace symbol V4L2_COLORSPACE_470_SYSTEM_BG :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_470_SYSTEM_M :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_OPRGB :c:type:`v4l2_colorspace` +replace define V4L2_COLORSPACE_ADOBERGB :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_BT2020 :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_DCI_P3 :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_DEFAULT :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_JPEG :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_RAW :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_REC709 :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_SMPTE170M :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_SMPTE240M :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_SRGB :c:type:`v4l2_colorspace` +replace symbol V4L2_COLORSPACE_LAST :c:type:`v4l2_colorspace` + +# Documented enum v4l2_xfer_func +replace symbol V4L2_XFER_FUNC_709 :c:type:`v4l2_xfer_func` +replace symbol V4L2_XFER_FUNC_OPRGB :c:type:`v4l2_xfer_func` +replace define V4L2_XFER_FUNC_ADOBERGB :c:type:`v4l2_xfer_func` +replace symbol V4L2_XFER_FUNC_DCI_P3 :c:type:`v4l2_xfer_func` +replace symbol V4L2_XFER_FUNC_DEFAULT :c:type:`v4l2_xfer_func` +replace symbol V4L2_XFER_FUNC_NONE :c:type:`v4l2_xfer_func` +replace symbol V4L2_XFER_FUNC_SMPTE2084 :c:type:`v4l2_xfer_func` +replace symbol V4L2_XFER_FUNC_SMPTE240M :c:type:`v4l2_xfer_func` +replace symbol V4L2_XFER_FUNC_SRGB :c:type:`v4l2_xfer_func` +replace symbol V4L2_XFER_FUNC_LAST :c:type:`v4l2_xfer_func` + +# Documented enum v4l2_ycbcr_encoding +replace symbol V4L2_YCBCR_ENC_601 :c:type:`v4l2_ycbcr_encoding` +replace symbol V4L2_YCBCR_ENC_709 :c:type:`v4l2_ycbcr_encoding` +replace symbol V4L2_YCBCR_ENC_BT2020 :c:type:`v4l2_ycbcr_encoding` +replace symbol V4L2_YCBCR_ENC_BT2020_CONST_LUM :c:type:`v4l2_ycbcr_encoding` +replace symbol V4L2_YCBCR_ENC_DEFAULT :c:type:`v4l2_ycbcr_encoding` +replace symbol V4L2_YCBCR_ENC_SYCC :c:type:`v4l2_ycbcr_encoding` +replace symbol V4L2_YCBCR_ENC_XV601 :c:type:`v4l2_ycbcr_encoding` +replace symbol V4L2_YCBCR_ENC_XV709 :c:type:`v4l2_ycbcr_encoding` +replace symbol V4L2_YCBCR_ENC_SMPTE240M :c:type:`v4l2_ycbcr_encoding` +replace symbol V4L2_YCBCR_ENC_LAST :c:type:`v4l2_ycbcr_encoding` + +# Documented enum v4l2_hsv_encoding +replace symbol V4L2_HSV_ENC_180 :c:type:`v4l2_hsv_encoding` +replace symbol V4L2_HSV_ENC_256 :c:type:`v4l2_hsv_encoding` + +# Documented enum v4l2_quantization +replace symbol V4L2_QUANTIZATION_DEFAULT :c:type:`v4l2_quantization` +replace symbol V4L2_QUANTIZATION_FULL_RANGE :c:type:`v4l2_quantization` +replace symbol V4L2_QUANTIZATION_LIM_RANGE :c:type:`v4l2_quantization` + +# Documented enum v4l2_priority +replace symbol V4L2_PRIORITY_BACKGROUND :c:type:`v4l2_priority` +replace symbol V4L2_PRIORITY_DEFAULT :c:type:`v4l2_priority` +replace symbol V4L2_PRIORITY_INTERACTIVE :c:type:`v4l2_priority` +replace symbol V4L2_PRIORITY_RECORD :c:type:`v4l2_priority` +replace symbol V4L2_PRIORITY_UNSET :c:type:`v4l2_priority` + +# Documented enum v4l2_frmsizetypes +replace symbol V4L2_FRMSIZE_TYPE_CONTINUOUS :c:type:`v4l2_frmsizetypes` +replace symbol V4L2_FRMSIZE_TYPE_DISCRETE :c:type:`v4l2_frmsizetypes` +replace symbol V4L2_FRMSIZE_TYPE_STEPWISE :c:type:`v4l2_frmsizetypes` + +# Documented enum frmivaltypes +replace symbol V4L2_FRMIVAL_TYPE_CONTINUOUS :c:type:`v4l2_frmivaltypes` +replace symbol V4L2_FRMIVAL_TYPE_DISCRETE :c:type:`v4l2_frmivaltypes` +replace symbol V4L2_FRMIVAL_TYPE_STEPWISE :c:type:`v4l2_frmivaltypes` + +# Documented enum :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_COMPOUND_TYPES vidioc_queryctrl + +replace symbol V4L2_CTRL_TYPE_BITMASK :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_BOOLEAN :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_BUTTON :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_CTRL_CLASS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_INTEGER :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_INTEGER64 :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_INTEGER_MENU :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_MENU :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_STRING :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_U16 :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_U32 :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_U8 :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_MPEG2_SEQUENCE :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_MPEG2_PICTURE :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_MPEG2_QUANTISATION :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_SPS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_PPS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_SCALING_MATRIX :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_PRED_WEIGHTS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_SLICE_PARAMS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_DECODE_PARAMS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_VP9_FRAME :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HDR10_CLL_INFO :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_AV1_SEQUENCE :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_AV1_FRAME :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_AV1_FILM_GRAIN :c:type:`v4l2_ctrl_type` + +# V4L2 capability defines +replace define V4L2_CAP_VIDEO_CAPTURE device-capabilities +replace define V4L2_CAP_VIDEO_CAPTURE_MPLANE device-capabilities +replace define V4L2_CAP_VIDEO_OUTPUT device-capabilities +replace define V4L2_CAP_VIDEO_OUTPUT_MPLANE device-capabilities +replace define V4L2_CAP_VIDEO_M2M device-capabilities +replace define V4L2_CAP_VIDEO_M2M_MPLANE device-capabilities +replace define V4L2_CAP_VIDEO_OVERLAY device-capabilities +replace define V4L2_CAP_VBI_CAPTURE device-capabilities +replace define V4L2_CAP_VBI_OUTPUT device-capabilities +replace define V4L2_CAP_SLICED_VBI_CAPTURE device-capabilities +replace define V4L2_CAP_SLICED_VBI_OUTPUT device-capabilities +replace define V4L2_CAP_RDS_CAPTURE device-capabilities +replace define V4L2_CAP_VIDEO_OUTPUT_OVERLAY device-capabilities +replace define V4L2_CAP_HW_FREQ_SEEK device-capabilities +replace define V4L2_CAP_RDS_OUTPUT device-capabilities +replace define V4L2_CAP_TUNER device-capabilities +replace define V4L2_CAP_AUDIO device-capabilities +replace define V4L2_CAP_RADIO device-capabilities +replace define V4L2_CAP_MODULATOR device-capabilities +replace define V4L2_CAP_SDR_CAPTURE device-capabilities +replace define V4L2_CAP_EXT_PIX_FORMAT device-capabilities +replace define V4L2_CAP_SDR_OUTPUT device-capabilities +replace define V4L2_CAP_META_CAPTURE device-capabilities +replace define V4L2_CAP_READWRITE device-capabilities +replace define V4L2_CAP_ASYNCIO device-capabilities +replace define V4L2_CAP_STREAMING device-capabilities +replace define V4L2_CAP_META_OUTPUT device-capabilities +replace define V4L2_CAP_DEVICE_CAPS device-capabilities +replace define V4L2_CAP_TOUCH device-capabilities +replace define V4L2_CAP_IO_MC device-capabilities +replace define V4L2_CAP_EDID device-capabilities + +# V4L2 pix flags +replace define V4L2_PIX_FMT_PRIV_MAGIC :c:type:`v4l2_pix_format` +replace define V4L2_PIX_FMT_FLAG_PREMUL_ALPHA format-flags +replace define V4L2_PIX_FMT_HM12 :c:type:`v4l2_pix_format` +replace define V4L2_PIX_FMT_SUNXI_TILED_NV12 :c:type:`v4l2_pix_format` + +# V4L2 format flags +replace define V4L2_FMT_FLAG_COMPRESSED fmtdesc-flags +replace define V4L2_FMT_FLAG_EMULATED fmtdesc-flags +replace define V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM fmtdesc-flags +replace define V4L2_FMT_FLAG_DYN_RESOLUTION fmtdesc-flags +replace define V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL fmtdesc-flags +replace define V4L2_FMT_FLAG_CSC_COLORSPACE fmtdesc-flags +replace define V4L2_FMT_FLAG_CSC_XFER_FUNC fmtdesc-flags +replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags +replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags +replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags +replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags +replace define V4L2_FMTDESC_FLAG_ENUM_ALL fmtdesc-flags + +# V4L2 timecode types +replace define V4L2_TC_TYPE_24FPS timecode-type +replace define V4L2_TC_TYPE_25FPS timecode-type +replace define V4L2_TC_TYPE_30FPS timecode-type +replace define V4L2_TC_TYPE_50FPS timecode-type +replace define V4L2_TC_TYPE_60FPS timecode-type + +# V4L2 timecode flags +replace define V4L2_TC_FLAG_DROPFRAME timecode-flags +replace define V4L2_TC_FLAG_COLORFRAME timecode-flags +replace define V4L2_TC_USERBITS_field timecode-flags +replace define V4L2_TC_USERBITS_USERDEFINED timecode-flags +replace define V4L2_TC_USERBITS_8BITCHARS timecode-flags + +# V4L2 JPEG markers +replace define V4L2_JPEG_MARKER_DHT jpeg-markers +replace define V4L2_JPEG_MARKER_DQT jpeg-markers +replace define V4L2_JPEG_MARKER_DRI jpeg-markers +replace define V4L2_JPEG_MARKER_COM jpeg-markers +replace define V4L2_JPEG_MARKER_APP jpeg-markers + +# V4L2 framebuffer caps and flags + +replace define V4L2_FBUF_CAP_EXTERNOVERLAY framebuffer-cap +replace define V4L2_FBUF_CAP_CHROMAKEY framebuffer-cap +replace define V4L2_FBUF_CAP_LIST_CLIPPING framebuffer-cap +replace define V4L2_FBUF_CAP_BITMAP_CLIPPING framebuffer-cap +replace define V4L2_FBUF_CAP_LOCAL_ALPHA framebuffer-cap +replace define V4L2_FBUF_CAP_GLOBAL_ALPHA framebuffer-cap +replace define V4L2_FBUF_CAP_LOCAL_INV_ALPHA framebuffer-cap +replace define V4L2_FBUF_CAP_SRC_CHROMAKEY framebuffer-cap + +replace define V4L2_FBUF_FLAG_PRIMARY framebuffer-flags +replace define V4L2_FBUF_FLAG_OVERLAY framebuffer-flags +replace define V4L2_FBUF_FLAG_CHROMAKEY framebuffer-flags +replace define V4L2_FBUF_FLAG_LOCAL_ALPHA framebuffer-flags +replace define V4L2_FBUF_FLAG_GLOBAL_ALPHA framebuffer-flags +replace define V4L2_FBUF_FLAG_LOCAL_INV_ALPHA framebuffer-flags +replace define V4L2_FBUF_FLAG_SRC_CHROMAKEY framebuffer-flags + +# Used on VIDIOC_G_PARM + +replace define V4L2_MODE_HIGHQUALITY parm-flags +replace define V4L2_CAP_TIMEPERFRAME :c:type:`v4l2_captureparm` + +# The V4L2_STD_foo are all defined at v4l2_std_id table + +replace define V4L2_STD_PAL_B v4l2-std-id +replace define V4L2_STD_PAL_B1 v4l2-std-id +replace define V4L2_STD_PAL_G v4l2-std-id +replace define V4L2_STD_PAL_H v4l2-std-id +replace define V4L2_STD_PAL_I v4l2-std-id +replace define V4L2_STD_PAL_D v4l2-std-id +replace define V4L2_STD_PAL_D1 v4l2-std-id +replace define V4L2_STD_PAL_K v4l2-std-id +replace define V4L2_STD_PAL_M v4l2-std-id +replace define V4L2_STD_PAL_N v4l2-std-id +replace define V4L2_STD_PAL_Nc v4l2-std-id +replace define V4L2_STD_PAL_60 v4l2-std-id +replace define V4L2_STD_NTSC_M v4l2-std-id +replace define V4L2_STD_NTSC_M_JP v4l2-std-id +replace define V4L2_STD_NTSC_443 v4l2-std-id +replace define V4L2_STD_NTSC_M_KR v4l2-std-id +replace define V4L2_STD_SECAM_B v4l2-std-id +replace define V4L2_STD_SECAM_D v4l2-std-id +replace define V4L2_STD_SECAM_G v4l2-std-id +replace define V4L2_STD_SECAM_H v4l2-std-id +replace define V4L2_STD_SECAM_K v4l2-std-id +replace define V4L2_STD_SECAM_K1 v4l2-std-id +replace define V4L2_STD_SECAM_L v4l2-std-id +replace define V4L2_STD_SECAM_LC v4l2-std-id +replace define V4L2_STD_ATSC_8_VSB v4l2-std-id +replace define V4L2_STD_ATSC_16_VSB v4l2-std-id +replace define V4L2_STD_NTSC v4l2-std-id +replace define V4L2_STD_SECAM_DK v4l2-std-id +replace define V4L2_STD_SECAM v4l2-std-id +replace define V4L2_STD_PAL_BG v4l2-std-id +replace define V4L2_STD_PAL_DK v4l2-std-id +replace define V4L2_STD_PAL v4l2-std-id +replace define V4L2_STD_B v4l2-std-id +replace define V4L2_STD_G v4l2-std-id +replace define V4L2_STD_H v4l2-std-id +replace define V4L2_STD_L v4l2-std-id +replace define V4L2_STD_GH v4l2-std-id +replace define V4L2_STD_DK v4l2-std-id +replace define V4L2_STD_BG v4l2-std-id +replace define V4L2_STD_MN v4l2-std-id +replace define V4L2_STD_MTS v4l2-std-id +replace define V4L2_STD_525_60 v4l2-std-id +replace define V4L2_STD_625_50 v4l2-std-id +replace define V4L2_STD_ATSC v4l2-std-id +replace define V4L2_STD_UNKNOWN v4l2-std-id +replace define V4L2_STD_ALL v4l2-std-id + +# V4L2 DT BT timings definitions + +replace define V4L2_DV_PROGRESSIVE :c:type:`v4l2_bt_timings` +replace define V4L2_DV_INTERLACED :c:type:`v4l2_bt_timings` + +replace define V4L2_DV_VSYNC_POS_POL :c:type:`v4l2_bt_timings` +replace define V4L2_DV_HSYNC_POS_POL :c:type:`v4l2_bt_timings` + +replace define V4L2_DV_BT_STD_CEA861 dv-bt-standards +replace define V4L2_DV_BT_STD_DMT dv-bt-standards +replace define V4L2_DV_BT_STD_CVT dv-bt-standards +replace define V4L2_DV_BT_STD_GTF dv-bt-standards +replace define V4L2_DV_BT_STD_SDI dv-bt-standards + +replace define V4L2_DV_FL_REDUCED_BLANKING dv-bt-standards +replace define V4L2_DV_FL_CAN_REDUCE_FPS dv-bt-standards +replace define V4L2_DV_FL_CAN_DETECT_REDUCED_FPS dv-bt-standards +replace define V4L2_DV_FL_REDUCED_FPS dv-bt-standards +replace define V4L2_DV_FL_HALF_LINE dv-bt-standards +replace define V4L2_DV_FL_IS_CE_VIDEO dv-bt-standards +replace define V4L2_DV_FL_FIRST_FIELD_EXTRA_LINE dv-bt-standards +replace define V4L2_DV_FL_HAS_PICTURE_ASPECT dv-bt-standards +replace define V4L2_DV_FL_HAS_CEA861_VIC dv-bt-standards +replace define V4L2_DV_FL_HAS_HDMI_VIC dv-bt-standards + +replace define V4L2_DV_BT_656_1120 dv-timing-types + +replace define V4L2_DV_BT_CAP_INTERLACED framebuffer-cap +replace define V4L2_DV_BT_CAP_PROGRESSIVE framebuffer-cap +replace define V4L2_DV_BT_CAP_REDUCED_BLANKING framebuffer-cap +replace define V4L2_DV_BT_CAP_CUSTOM framebuffer-cap + +# V4L2 input + +replace define V4L2_INPUT_TYPE_TUNER input-type +replace define V4L2_INPUT_TYPE_CAMERA input-type +replace define V4L2_INPUT_TYPE_TOUCH input-type + +replace define V4L2_IN_ST_NO_POWER input-status +replace define V4L2_IN_ST_NO_SIGNAL input-status +replace define V4L2_IN_ST_NO_COLOR input-status +replace define V4L2_IN_ST_HFLIP input-status +replace define V4L2_IN_ST_VFLIP input-status +replace define V4L2_IN_ST_NO_H_LOCK input-status +replace define V4L2_IN_ST_COLOR_KILL input-status +replace define V4L2_IN_ST_NO_SYNC input-status +replace define V4L2_IN_ST_NO_EQU input-status +replace define V4L2_IN_ST_NO_CARRIER input-status +replace define V4L2_IN_ST_MACROVISION input-status +replace define V4L2_IN_ST_NO_ACCESS input-status +replace define V4L2_IN_ST_VTR input-status +replace define V4L2_IN_ST_NO_V_LOCK input-status +replace define V4L2_IN_ST_NO_STD_LOCK input-status + +replace define V4L2_IN_CAP_DV_TIMINGS input-capabilities +replace define V4L2_IN_CAP_STD input-capabilities +replace define V4L2_IN_CAP_NATIVE_SIZE input-capabilities + +# V4L2 output + +replace define V4L2_OUTPUT_TYPE_MODULATOR output-type +replace define V4L2_OUTPUT_TYPE_ANALOG output-type +replace define V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY output-type + +replace define V4L2_OUT_CAP_DV_TIMINGS output-capabilities +replace define V4L2_OUT_CAP_STD output-capabilities +replace define V4L2_OUT_CAP_NATIVE_SIZE output-capabilities + +# V4L2 control flags + +replace define V4L2_CTRL_FLAG_DISABLED control-flags +replace define V4L2_CTRL_FLAG_GRABBED control-flags +replace define V4L2_CTRL_FLAG_READ_ONLY control-flags +replace define V4L2_CTRL_FLAG_UPDATE control-flags +replace define V4L2_CTRL_FLAG_INACTIVE control-flags +replace define V4L2_CTRL_FLAG_SLIDER control-flags +replace define V4L2_CTRL_FLAG_WRITE_ONLY control-flags +replace define V4L2_CTRL_FLAG_VOLATILE control-flags +replace define V4L2_CTRL_FLAG_HAS_PAYLOAD control-flags +replace define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE control-flags +replace define V4L2_CTRL_FLAG_MODIFY_LAYOUT control-flags +replace define V4L2_CTRL_FLAG_DYNAMIC_ARRAY control-flags +replace define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX control-flags + +replace define V4L2_CTRL_FLAG_NEXT_CTRL control +replace define V4L2_CTRL_FLAG_NEXT_COMPOUND control +replace define V4L2_CID_PRIVATE_BASE control + +# V4L2 tuner + +replace define V4L2_TUNER_CAP_LOW tuner-capability +replace define V4L2_TUNER_CAP_NORM tuner-capability +replace define V4L2_TUNER_CAP_HWSEEK_BOUNDED tuner-capability +replace define V4L2_TUNER_CAP_HWSEEK_WRAP tuner-capability +replace define V4L2_TUNER_CAP_STEREO tuner-capability +replace define V4L2_TUNER_CAP_LANG2 tuner-capability +replace define V4L2_TUNER_CAP_SAP tuner-capability +replace define V4L2_TUNER_CAP_LANG1 tuner-capability +replace define V4L2_TUNER_CAP_RDS tuner-capability +replace define V4L2_TUNER_CAP_RDS_BLOCK_IO tuner-capability +replace define V4L2_TUNER_CAP_RDS_CONTROLS tuner-capability +replace define V4L2_TUNER_CAP_FREQ_BANDS tuner-capability +replace define V4L2_TUNER_CAP_HWSEEK_PROG_LIM tuner-capability +replace define V4L2_TUNER_CAP_1HZ tuner-capability + +replace define V4L2_TUNER_SUB_MONO tuner-rxsubchans +replace define V4L2_TUNER_SUB_STEREO tuner-rxsubchans +replace define V4L2_TUNER_SUB_LANG2 tuner-rxsubchans +replace define V4L2_TUNER_SUB_SAP tuner-rxsubchans +replace define V4L2_TUNER_SUB_LANG1 tuner-rxsubchans +replace define V4L2_TUNER_SUB_RDS tuner-rxsubchans + +replace define V4L2_TUNER_MODE_MONO tuner-audmode +replace define V4L2_TUNER_MODE_STEREO tuner-audmode +replace define V4L2_TUNER_MODE_LANG2 tuner-audmode +replace define V4L2_TUNER_MODE_SAP tuner-audmode +replace define V4L2_TUNER_MODE_LANG1 tuner-audmode +replace define V4L2_TUNER_MODE_LANG1_LANG2 tuner-audmode + +replace define V4L2_BAND_MODULATION_VSB band-modulation +replace define V4L2_BAND_MODULATION_FM band-modulation +replace define V4L2_BAND_MODULATION_AM band-modulation + +replace define V4L2_RDS_BLOCK_MSK v4l2-rds-block +replace define V4L2_RDS_BLOCK_A v4l2-rds-block +replace define V4L2_RDS_BLOCK_B v4l2-rds-block +replace define V4L2_RDS_BLOCK_C v4l2-rds-block +replace define V4L2_RDS_BLOCK_D v4l2-rds-block +replace define V4L2_RDS_BLOCK_C_ALT v4l2-rds-block +replace define V4L2_RDS_BLOCK_INVALID v4l2-rds-block +replace define V4L2_RDS_BLOCK_CORRECTED v4l2-rds-block +replace define V4L2_RDS_BLOCK_ERROR v4l2-rds-block + +# V4L2 audio + +replace define V4L2_AUDCAP_STEREO audio-capability +replace define V4L2_AUDCAP_AVL audio-capability + +replace define V4L2_AUDMODE_AVL audio-mode + +# MPEG + +replace define V4L2_ENC_IDX_FRAME_I :c:type:`v4l2_enc_idx` +replace define V4L2_ENC_IDX_FRAME_P :c:type:`v4l2_enc_idx` +replace define V4L2_ENC_IDX_FRAME_B :c:type:`v4l2_enc_idx` +replace define V4L2_ENC_IDX_FRAME_MASK :c:type:`v4l2_enc_idx` +replace define V4L2_ENC_IDX_ENTRIES :c:type:`v4l2_enc_idx` + +replace define V4L2_ENC_CMD_START encoder-cmds +replace define V4L2_ENC_CMD_STOP encoder-cmds +replace define V4L2_ENC_CMD_PAUSE encoder-cmds +replace define V4L2_ENC_CMD_RESUME encoder-cmds + +replace define V4L2_ENC_CMD_STOP_AT_GOP_END encoder-flags + +replace define V4L2_DEC_CMD_START decoder-cmds +replace define V4L2_DEC_CMD_STOP decoder-cmds +replace define V4L2_DEC_CMD_PAUSE decoder-cmds +replace define V4L2_DEC_CMD_RESUME decoder-cmds +replace define V4L2_DEC_CMD_FLUSH decoder-cmds + +replace define V4L2_DEC_CMD_START_MUTE_AUDIO decoder-cmds +replace define V4L2_DEC_CMD_PAUSE_TO_BLACK decoder-cmds +replace define V4L2_DEC_CMD_STOP_TO_BLACK decoder-cmds +replace define V4L2_DEC_CMD_STOP_IMMEDIATELY decoder-cmds + +replace define V4L2_DEC_START_FMT_NONE decoder-cmds +replace define V4L2_DEC_START_FMT_GOP decoder-cmds + +# V4L2 VBI + +replace define V4L2_VBI_UNSYNC vbifmt-flags +replace define V4L2_VBI_INTERLACED vbifmt-flags + +replace define V4L2_VBI_ITU_525_F1_START :c:type:`v4l2_vbi_format` +replace define V4L2_VBI_ITU_525_F2_START :c:type:`v4l2_vbi_format` +replace define V4L2_VBI_ITU_625_F1_START :c:type:`v4l2_vbi_format` +replace define V4L2_VBI_ITU_625_F2_START :c:type:`v4l2_vbi_format` + + +replace define V4L2_SLICED_TELETEXT_B vbi-services +replace define V4L2_SLICED_VPS vbi-services +replace define V4L2_SLICED_CAPTION_525 vbi-services +replace define V4L2_SLICED_WSS_625 vbi-services +replace define V4L2_SLICED_VBI_525 vbi-services +replace define V4L2_SLICED_VBI_625 vbi-services + +replace define V4L2_MPEG_VBI_IVTV_TELETEXT_B ITV0-Line-Identifier-Constants +replace define V4L2_MPEG_VBI_IVTV_CAPTION_525 ITV0-Line-Identifier-Constants +replace define V4L2_MPEG_VBI_IVTV_WSS_625 ITV0-Line-Identifier-Constants +replace define V4L2_MPEG_VBI_IVTV_VPS ITV0-Line-Identifier-Constants + +replace define V4L2_MPEG_VBI_IVTV_MAGIC0 v4l2-mpeg-vbi-fmt-ivtv-magic +replace define V4L2_MPEG_VBI_IVTV_MAGIC1 v4l2-mpeg-vbi-fmt-ivtv-magic + +# V4L2 events + +replace define V4L2_EVENT_ALL event-type +replace define V4L2_EVENT_VSYNC event-type +replace define V4L2_EVENT_EOS event-type +replace define V4L2_EVENT_CTRL event-type +replace define V4L2_EVENT_FRAME_SYNC event-type +replace define V4L2_EVENT_SOURCE_CHANGE event-type +replace define V4L2_EVENT_MOTION_DET event-type +replace define V4L2_EVENT_PRIVATE_START event-type + +replace define V4L2_EVENT_CTRL_CH_VALUE ctrl-changes-flags +replace define V4L2_EVENT_CTRL_CH_FLAGS ctrl-changes-flags +replace define V4L2_EVENT_CTRL_CH_RANGE ctrl-changes-flags +replace define V4L2_EVENT_CTRL_CH_DIMENSIONS ctrl-changes-flags + +replace define V4L2_EVENT_SRC_CH_RESOLUTION src-changes-flags + +replace define V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ :c:type:`v4l2_event_motion_det` + +replace define V4L2_EVENT_SUB_FL_SEND_INITIAL event-flags +replace define V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK event-flags + +# V4L2 debugging +replace define V4L2_CHIP_MATCH_BRIDGE vidioc_dbg_g_register +replace define V4L2_CHIP_MATCH_SUBDEV vidioc_dbg_g_register +replace define V4L2_CHIP_MATCH_HOST vidioc_dbg_g_register +replace define V4L2_CHIP_MATCH_I2C_DRIVER vidioc_dbg_g_register +replace define V4L2_CHIP_MATCH_I2C_ADDR vidioc_dbg_g_register +replace define V4L2_CHIP_MATCH_AC97 vidioc_dbg_g_register + +replace define V4L2_CHIP_FL_READABLE vidioc_dbg_g_register +replace define V4L2_CHIP_FL_WRITABLE vidioc_dbg_g_register + +# Ignore reserved ioctl and ancillary macros + +ignore define VIDEO_MAX_FRAME +ignore define VIDEO_MAX_PLANES +ignore define v4l2_fourcc +ignore define v4l2_fourcc_be +ignore define V4L2_FIELD_HAS_TOP +ignore define V4L2_FIELD_HAS_BOTTOM +ignore define V4L2_FIELD_HAS_BOTH +ignore define V4L2_FIELD_HAS_T_OR_B +ignore define V4L2_TYPE_IS_MULTIPLANAR +ignore define V4L2_TYPE_IS_OUTPUT +ignore define V4L2_TUNER_ADC +ignore define V4L2_MAP_COLORSPACE_DEFAULT +ignore define V4L2_MAP_XFER_FUNC_DEFAULT +ignore define V4L2_MAP_YCBCR_ENC_DEFAULT +ignore define V4L2_DV_BT_BLANKING_WIDTH +ignore define V4L2_DV_BT_FRAME_WIDTH +ignore define V4L2_DV_BT_BLANKING_HEIGHT +ignore define V4L2_DV_BT_FRAME_HEIGHT +ignore define V4L2_IN_CAP_CUSTOM_TIMINGS +ignore define V4L2_CTRL_ID_MASK +ignore define V4L2_CTRL_ID2CLASS +ignore define V4L2_CTRL_ID2WHICH +ignore define V4L2_CTRL_DRIVER_PRIV +ignore define V4L2_CTRL_MAX_DIMS +ignore define V4L2_CTRL_WHICH_CUR_VAL +ignore define V4L2_CTRL_WHICH_DEF_VAL +ignore define V4L2_CTRL_WHICH_MIN_VAL +ignore define V4L2_CTRL_WHICH_MAX_VAL +ignore define V4L2_CTRL_WHICH_REQUEST_VAL +ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS +ignore define V4L2_CID_MAX_CTRLS + +ignore define BASE_VIDIOC_PRIVATE + +# Associate ioctls with their counterparts +replace ioctl VIDIOC_DBG_S_REGISTER vidioc_dbg_g_register +replace ioctl VIDIOC_DQBUF vidioc_qbuf +replace ioctl VIDIOC_S_AUDOUT vidioc_g_audout +replace ioctl VIDIOC_S_CROP vidioc_g_crop +replace ioctl VIDIOC_S_CTRL vidioc_g_ctrl +replace ioctl VIDIOC_S_DV_TIMINGS vidioc_g_dv_timings +replace ioctl VIDIOC_S_EDID vidioc_g_edid +replace ioctl VIDIOC_S_EXT_CTRLS vidioc_g_ext_ctrls +replace ioctl VIDIOC_S_FBUF vidioc_g_fbuf +replace ioctl VIDIOC_S_FMT vidioc_g_fmt +replace ioctl VIDIOC_S_FREQUENCY vidioc_g_frequency +replace ioctl VIDIOC_S_INPUT vidioc_g_input +replace ioctl VIDIOC_S_JPEGCOMP vidioc_g_jpegcomp +replace ioctl VIDIOC_S_MODULATOR vidioc_g_modulator +replace ioctl VIDIOC_S_OUTPUT vidioc_g_output +replace ioctl VIDIOC_S_PARM vidioc_g_parm +replace ioctl VIDIOC_S_PRIORITY vidioc_g_priority +replace ioctl VIDIOC_S_SELECTION vidioc_g_selection +replace ioctl VIDIOC_S_STD vidioc_g_std +replace ioctl VIDIOC_S_AUDIO vidioc_g_audio +replace ioctl VIDIOC_S_TUNER vidioc_g_tuner +replace ioctl VIDIOC_TRY_DECODER_CMD vidioc_decoder_cmd +replace ioctl VIDIOC_TRY_ENCODER_CMD vidioc_encoder_cmd +replace ioctl VIDIOC_TRY_EXT_CTRLS vidioc_g_ext_ctrls +replace ioctl VIDIOC_TRY_FMT vidioc_g_fmt +replace ioctl VIDIOC_STREAMOFF vidioc_streamon +replace ioctl VIDIOC_QUERY_EXT_CTRL vidioc_queryctrl +replace ioctl VIDIOC_QUERYMENU vidioc_queryctrl diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions deleted file mode 100644 index 35d3456cc812..000000000000 --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions +++ /dev/null @@ -1,610 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -# Ignore header name -ignore define _UAPI__LINUX_VIDEODEV2_H - -# -# The cross reference valitator for videodev2.h DocBook never cared -# about enum symbols or defines. Yet, they're all (or almost all?) -# handled inside V4L API sections. So, for now, it is safe to just -# ignore. This should be revisited, as validating it helps to avoid -# having something not documented at the uAPI. -# - -# Those symbols should not be used by uAPI - don't document them -ignore symbol V4L2_BUF_TYPE_PRIVATE -ignore symbol V4L2_TUNER_DIGITAL_TV -ignore symbol V4L2_COLORSPACE_BT878 - -# Documented enum v4l2_field -replace symbol V4L2_FIELD_ALTERNATE :c:type:`v4l2_field` -replace symbol V4L2_FIELD_ANY :c:type:`v4l2_field` -replace symbol V4L2_FIELD_BOTTOM :c:type:`v4l2_field` -replace symbol V4L2_FIELD_INTERLACED :c:type:`v4l2_field` -replace symbol V4L2_FIELD_INTERLACED_BT :c:type:`v4l2_field` -replace symbol V4L2_FIELD_INTERLACED_TB :c:type:`v4l2_field` -replace symbol V4L2_FIELD_NONE :c:type:`v4l2_field` -replace symbol V4L2_FIELD_SEQ_BT :c:type:`v4l2_field` -replace symbol V4L2_FIELD_SEQ_TB :c:type:`v4l2_field` -replace symbol V4L2_FIELD_TOP :c:type:`v4l2_field` - -# Documented enum v4l2_buf_type -replace symbol V4L2_BUF_TYPE_META_CAPTURE :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_META_OUTPUT :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_SDR_CAPTURE :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_SDR_OUTPUT :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_SLICED_VBI_CAPTURE :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_SLICED_VBI_OUTPUT :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_VBI_CAPTURE :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_VBI_OUTPUT :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_VIDEO_CAPTURE :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY :c:type:`v4l2_buf_type` -replace symbol V4L2_BUF_TYPE_VIDEO_OVERLAY :c:type:`v4l2_buf_type` - -# Documented enum v4l2_tuner_type -replace symbol V4L2_TUNER_ANALOG_TV :c:type:`v4l2_tuner_type` -replace symbol V4L2_TUNER_RADIO :c:type:`v4l2_tuner_type` -replace symbol V4L2_TUNER_RF :c:type:`v4l2_tuner_type` -replace symbol V4L2_TUNER_SDR :c:type:`v4l2_tuner_type` - -# Documented enum v4l2_memory -replace symbol V4L2_MEMORY_DMABUF :c:type:`v4l2_memory` -replace symbol V4L2_MEMORY_MMAP :c:type:`v4l2_memory` -replace symbol V4L2_MEMORY_OVERLAY :c:type:`v4l2_memory` -replace symbol V4L2_MEMORY_USERPTR :c:type:`v4l2_memory` - -# Documented enum v4l2_colorspace -replace symbol V4L2_COLORSPACE_470_SYSTEM_BG :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_470_SYSTEM_M :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_OPRGB :c:type:`v4l2_colorspace` -replace define V4L2_COLORSPACE_ADOBERGB :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_BT2020 :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_DCI_P3 :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_DEFAULT :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_JPEG :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_RAW :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_REC709 :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_SMPTE170M :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_SMPTE240M :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_SRGB :c:type:`v4l2_colorspace` -replace symbol V4L2_COLORSPACE_LAST :c:type:`v4l2_colorspace` - -# Documented enum v4l2_xfer_func -replace symbol V4L2_XFER_FUNC_709 :c:type:`v4l2_xfer_func` -replace symbol V4L2_XFER_FUNC_OPRGB :c:type:`v4l2_xfer_func` -replace define V4L2_XFER_FUNC_ADOBERGB :c:type:`v4l2_xfer_func` -replace symbol V4L2_XFER_FUNC_DCI_P3 :c:type:`v4l2_xfer_func` -replace symbol V4L2_XFER_FUNC_DEFAULT :c:type:`v4l2_xfer_func` -replace symbol V4L2_XFER_FUNC_NONE :c:type:`v4l2_xfer_func` -replace symbol V4L2_XFER_FUNC_SMPTE2084 :c:type:`v4l2_xfer_func` -replace symbol V4L2_XFER_FUNC_SMPTE240M :c:type:`v4l2_xfer_func` -replace symbol V4L2_XFER_FUNC_SRGB :c:type:`v4l2_xfer_func` -replace symbol V4L2_XFER_FUNC_LAST :c:type:`v4l2_xfer_func` - -# Documented enum v4l2_ycbcr_encoding -replace symbol V4L2_YCBCR_ENC_601 :c:type:`v4l2_ycbcr_encoding` -replace symbol V4L2_YCBCR_ENC_709 :c:type:`v4l2_ycbcr_encoding` -replace symbol V4L2_YCBCR_ENC_BT2020 :c:type:`v4l2_ycbcr_encoding` -replace symbol V4L2_YCBCR_ENC_BT2020_CONST_LUM :c:type:`v4l2_ycbcr_encoding` -replace symbol V4L2_YCBCR_ENC_DEFAULT :c:type:`v4l2_ycbcr_encoding` -replace symbol V4L2_YCBCR_ENC_SYCC :c:type:`v4l2_ycbcr_encoding` -replace symbol V4L2_YCBCR_ENC_XV601 :c:type:`v4l2_ycbcr_encoding` -replace symbol V4L2_YCBCR_ENC_XV709 :c:type:`v4l2_ycbcr_encoding` -replace symbol V4L2_YCBCR_ENC_SMPTE240M :c:type:`v4l2_ycbcr_encoding` -replace symbol V4L2_YCBCR_ENC_LAST :c:type:`v4l2_ycbcr_encoding` - -# Documented enum v4l2_hsv_encoding -replace symbol V4L2_HSV_ENC_180 :c:type:`v4l2_hsv_encoding` -replace symbol V4L2_HSV_ENC_256 :c:type:`v4l2_hsv_encoding` - -# Documented enum v4l2_quantization -replace symbol V4L2_QUANTIZATION_DEFAULT :c:type:`v4l2_quantization` -replace symbol V4L2_QUANTIZATION_FULL_RANGE :c:type:`v4l2_quantization` -replace symbol V4L2_QUANTIZATION_LIM_RANGE :c:type:`v4l2_quantization` - -# Documented enum v4l2_priority -replace symbol V4L2_PRIORITY_BACKGROUND :c:type:`v4l2_priority` -replace symbol V4L2_PRIORITY_DEFAULT :c:type:`v4l2_priority` -replace symbol V4L2_PRIORITY_INTERACTIVE :c:type:`v4l2_priority` -replace symbol V4L2_PRIORITY_RECORD :c:type:`v4l2_priority` -replace symbol V4L2_PRIORITY_UNSET :c:type:`v4l2_priority` - -# Documented enum v4l2_frmsizetypes -replace symbol V4L2_FRMSIZE_TYPE_CONTINUOUS :c:type:`v4l2_frmsizetypes` -replace symbol V4L2_FRMSIZE_TYPE_DISCRETE :c:type:`v4l2_frmsizetypes` -replace symbol V4L2_FRMSIZE_TYPE_STEPWISE :c:type:`v4l2_frmsizetypes` - -# Documented enum frmivaltypes -replace symbol V4L2_FRMIVAL_TYPE_CONTINUOUS :c:type:`v4l2_frmivaltypes` -replace symbol V4L2_FRMIVAL_TYPE_DISCRETE :c:type:`v4l2_frmivaltypes` -replace symbol V4L2_FRMIVAL_TYPE_STEPWISE :c:type:`v4l2_frmivaltypes` - -# Documented enum :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_COMPOUND_TYPES vidioc_queryctrl - -replace symbol V4L2_CTRL_TYPE_BITMASK :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_BOOLEAN :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_BUTTON :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_CTRL_CLASS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_INTEGER :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_INTEGER64 :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_INTEGER_MENU :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_MENU :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_STRING :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_U16 :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_U32 :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_U8 :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_MPEG2_SEQUENCE :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_MPEG2_PICTURE :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_MPEG2_QUANTISATION :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_H264_SPS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_H264_PPS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_H264_SCALING_MATRIX :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_H264_PRED_WEIGHTS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_H264_SLICE_PARAMS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_H264_DECODE_PARAMS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_VP9_FRAME :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_HDR10_CLL_INFO :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_AV1_SEQUENCE :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_AV1_FRAME :c:type:`v4l2_ctrl_type` -replace symbol V4L2_CTRL_TYPE_AV1_FILM_GRAIN :c:type:`v4l2_ctrl_type` - -# V4L2 capability defines -replace define V4L2_CAP_VIDEO_CAPTURE device-capabilities -replace define V4L2_CAP_VIDEO_CAPTURE_MPLANE device-capabilities -replace define V4L2_CAP_VIDEO_OUTPUT device-capabilities -replace define V4L2_CAP_VIDEO_OUTPUT_MPLANE device-capabilities -replace define V4L2_CAP_VIDEO_M2M device-capabilities -replace define V4L2_CAP_VIDEO_M2M_MPLANE device-capabilities -replace define V4L2_CAP_VIDEO_OVERLAY device-capabilities -replace define V4L2_CAP_VBI_CAPTURE device-capabilities -replace define V4L2_CAP_VBI_OUTPUT device-capabilities -replace define V4L2_CAP_SLICED_VBI_CAPTURE device-capabilities -replace define V4L2_CAP_SLICED_VBI_OUTPUT device-capabilities -replace define V4L2_CAP_RDS_CAPTURE device-capabilities -replace define V4L2_CAP_VIDEO_OUTPUT_OVERLAY device-capabilities -replace define V4L2_CAP_HW_FREQ_SEEK device-capabilities -replace define V4L2_CAP_RDS_OUTPUT device-capabilities -replace define V4L2_CAP_TUNER device-capabilities -replace define V4L2_CAP_AUDIO device-capabilities -replace define V4L2_CAP_RADIO device-capabilities -replace define V4L2_CAP_MODULATOR device-capabilities -replace define V4L2_CAP_SDR_CAPTURE device-capabilities -replace define V4L2_CAP_EXT_PIX_FORMAT device-capabilities -replace define V4L2_CAP_SDR_OUTPUT device-capabilities -replace define V4L2_CAP_META_CAPTURE device-capabilities -replace define V4L2_CAP_READWRITE device-capabilities -replace define V4L2_CAP_ASYNCIO device-capabilities -replace define V4L2_CAP_STREAMING device-capabilities -replace define V4L2_CAP_META_OUTPUT device-capabilities -replace define V4L2_CAP_DEVICE_CAPS device-capabilities -replace define V4L2_CAP_TOUCH device-capabilities -replace define V4L2_CAP_IO_MC device-capabilities -replace define V4L2_CAP_EDID device-capabilities - -# V4L2 pix flags -replace define V4L2_PIX_FMT_PRIV_MAGIC :c:type:`v4l2_pix_format` -replace define V4L2_PIX_FMT_FLAG_PREMUL_ALPHA format-flags -replace define V4L2_PIX_FMT_HM12 :c:type:`v4l2_pix_format` -replace define V4L2_PIX_FMT_SUNXI_TILED_NV12 :c:type:`v4l2_pix_format` - -# V4L2 format flags -replace define V4L2_FMT_FLAG_COMPRESSED fmtdesc-flags -replace define V4L2_FMT_FLAG_EMULATED fmtdesc-flags -replace define V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM fmtdesc-flags -replace define V4L2_FMT_FLAG_DYN_RESOLUTION fmtdesc-flags -replace define V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL fmtdesc-flags -replace define V4L2_FMT_FLAG_CSC_COLORSPACE fmtdesc-flags -replace define V4L2_FMT_FLAG_CSC_XFER_FUNC fmtdesc-flags -replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags -replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags -replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags -replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags -replace define V4L2_FMTDESC_FLAG_ENUM_ALL fmtdesc-flags - -# V4L2 timecode types -replace define V4L2_TC_TYPE_24FPS timecode-type -replace define V4L2_TC_TYPE_25FPS timecode-type -replace define V4L2_TC_TYPE_30FPS timecode-type -replace define V4L2_TC_TYPE_50FPS timecode-type -replace define V4L2_TC_TYPE_60FPS timecode-type - -# V4L2 timecode flags -replace define V4L2_TC_FLAG_DROPFRAME timecode-flags -replace define V4L2_TC_FLAG_COLORFRAME timecode-flags -replace define V4L2_TC_USERBITS_field timecode-flags -replace define V4L2_TC_USERBITS_USERDEFINED timecode-flags -replace define V4L2_TC_USERBITS_8BITCHARS timecode-flags - -# V4L2 JPEG markers -replace define V4L2_JPEG_MARKER_DHT jpeg-markers -replace define V4L2_JPEG_MARKER_DQT jpeg-markers -replace define V4L2_JPEG_MARKER_DRI jpeg-markers -replace define V4L2_JPEG_MARKER_COM jpeg-markers -replace define V4L2_JPEG_MARKER_APP jpeg-markers - -# V4L2 framebuffer caps and flags - -replace define V4L2_FBUF_CAP_EXTERNOVERLAY framebuffer-cap -replace define V4L2_FBUF_CAP_CHROMAKEY framebuffer-cap -replace define V4L2_FBUF_CAP_LIST_CLIPPING framebuffer-cap -replace define V4L2_FBUF_CAP_BITMAP_CLIPPING framebuffer-cap -replace define V4L2_FBUF_CAP_LOCAL_ALPHA framebuffer-cap -replace define V4L2_FBUF_CAP_GLOBAL_ALPHA framebuffer-cap -replace define V4L2_FBUF_CAP_LOCAL_INV_ALPHA framebuffer-cap -replace define V4L2_FBUF_CAP_SRC_CHROMAKEY framebuffer-cap - -replace define V4L2_FBUF_FLAG_PRIMARY framebuffer-flags -replace define V4L2_FBUF_FLAG_OVERLAY framebuffer-flags -replace define V4L2_FBUF_FLAG_CHROMAKEY framebuffer-flags -replace define V4L2_FBUF_FLAG_LOCAL_ALPHA framebuffer-flags -replace define V4L2_FBUF_FLAG_GLOBAL_ALPHA framebuffer-flags -replace define V4L2_FBUF_FLAG_LOCAL_INV_ALPHA framebuffer-flags -replace define V4L2_FBUF_FLAG_SRC_CHROMAKEY framebuffer-flags - -# Used on VIDIOC_G_PARM - -replace define V4L2_MODE_HIGHQUALITY parm-flags -replace define V4L2_CAP_TIMEPERFRAME :c:type:`v4l2_captureparm` - -# The V4L2_STD_foo are all defined at v4l2_std_id table - -replace define V4L2_STD_PAL_B v4l2-std-id -replace define V4L2_STD_PAL_B1 v4l2-std-id -replace define V4L2_STD_PAL_G v4l2-std-id -replace define V4L2_STD_PAL_H v4l2-std-id -replace define V4L2_STD_PAL_I v4l2-std-id -replace define V4L2_STD_PAL_D v4l2-std-id -replace define V4L2_STD_PAL_D1 v4l2-std-id -replace define V4L2_STD_PAL_K v4l2-std-id -replace define V4L2_STD_PAL_M v4l2-std-id -replace define V4L2_STD_PAL_N v4l2-std-id -replace define V4L2_STD_PAL_Nc v4l2-std-id -replace define V4L2_STD_PAL_60 v4l2-std-id -replace define V4L2_STD_NTSC_M v4l2-std-id -replace define V4L2_STD_NTSC_M_JP v4l2-std-id -replace define V4L2_STD_NTSC_443 v4l2-std-id -replace define V4L2_STD_NTSC_M_KR v4l2-std-id -replace define V4L2_STD_SECAM_B v4l2-std-id -replace define V4L2_STD_SECAM_D v4l2-std-id -replace define V4L2_STD_SECAM_G v4l2-std-id -replace define V4L2_STD_SECAM_H v4l2-std-id -replace define V4L2_STD_SECAM_K v4l2-std-id -replace define V4L2_STD_SECAM_K1 v4l2-std-id -replace define V4L2_STD_SECAM_L v4l2-std-id -replace define V4L2_STD_SECAM_LC v4l2-std-id -replace define V4L2_STD_ATSC_8_VSB v4l2-std-id -replace define V4L2_STD_ATSC_16_VSB v4l2-std-id -replace define V4L2_STD_NTSC v4l2-std-id -replace define V4L2_STD_SECAM_DK v4l2-std-id -replace define V4L2_STD_SECAM v4l2-std-id -replace define V4L2_STD_PAL_BG v4l2-std-id -replace define V4L2_STD_PAL_DK v4l2-std-id -replace define V4L2_STD_PAL v4l2-std-id -replace define V4L2_STD_B v4l2-std-id -replace define V4L2_STD_G v4l2-std-id -replace define V4L2_STD_H v4l2-std-id -replace define V4L2_STD_L v4l2-std-id -replace define V4L2_STD_GH v4l2-std-id -replace define V4L2_STD_DK v4l2-std-id -replace define V4L2_STD_BG v4l2-std-id -replace define V4L2_STD_MN v4l2-std-id -replace define V4L2_STD_MTS v4l2-std-id -replace define V4L2_STD_525_60 v4l2-std-id -replace define V4L2_STD_625_50 v4l2-std-id -replace define V4L2_STD_ATSC v4l2-std-id -replace define V4L2_STD_UNKNOWN v4l2-std-id -replace define V4L2_STD_ALL v4l2-std-id - -# V4L2 DT BT timings definitions - -replace define V4L2_DV_PROGRESSIVE :c:type:`v4l2_bt_timings` -replace define V4L2_DV_INTERLACED :c:type:`v4l2_bt_timings` - -replace define V4L2_DV_VSYNC_POS_POL :c:type:`v4l2_bt_timings` -replace define V4L2_DV_HSYNC_POS_POL :c:type:`v4l2_bt_timings` - -replace define V4L2_DV_BT_STD_CEA861 dv-bt-standards -replace define V4L2_DV_BT_STD_DMT dv-bt-standards -replace define V4L2_DV_BT_STD_CVT dv-bt-standards -replace define V4L2_DV_BT_STD_GTF dv-bt-standards -replace define V4L2_DV_BT_STD_SDI dv-bt-standards - -replace define V4L2_DV_FL_REDUCED_BLANKING dv-bt-standards -replace define V4L2_DV_FL_CAN_REDUCE_FPS dv-bt-standards -replace define V4L2_DV_FL_CAN_DETECT_REDUCED_FPS dv-bt-standards -replace define V4L2_DV_FL_REDUCED_FPS dv-bt-standards -replace define V4L2_DV_FL_HALF_LINE dv-bt-standards -replace define V4L2_DV_FL_IS_CE_VIDEO dv-bt-standards -replace define V4L2_DV_FL_FIRST_FIELD_EXTRA_LINE dv-bt-standards -replace define V4L2_DV_FL_HAS_PICTURE_ASPECT dv-bt-standards -replace define V4L2_DV_FL_HAS_CEA861_VIC dv-bt-standards -replace define V4L2_DV_FL_HAS_HDMI_VIC dv-bt-standards - -replace define V4L2_DV_BT_656_1120 dv-timing-types - -replace define V4L2_DV_BT_CAP_INTERLACED framebuffer-cap -replace define V4L2_DV_BT_CAP_PROGRESSIVE framebuffer-cap -replace define V4L2_DV_BT_CAP_REDUCED_BLANKING framebuffer-cap -replace define V4L2_DV_BT_CAP_CUSTOM framebuffer-cap - -# V4L2 input - -replace define V4L2_INPUT_TYPE_TUNER input-type -replace define V4L2_INPUT_TYPE_CAMERA input-type -replace define V4L2_INPUT_TYPE_TOUCH input-type - -replace define V4L2_IN_ST_NO_POWER input-status -replace define V4L2_IN_ST_NO_SIGNAL input-status -replace define V4L2_IN_ST_NO_COLOR input-status -replace define V4L2_IN_ST_HFLIP input-status -replace define V4L2_IN_ST_VFLIP input-status -replace define V4L2_IN_ST_NO_H_LOCK input-status -replace define V4L2_IN_ST_COLOR_KILL input-status -replace define V4L2_IN_ST_NO_SYNC input-status -replace define V4L2_IN_ST_NO_EQU input-status -replace define V4L2_IN_ST_NO_CARRIER input-status -replace define V4L2_IN_ST_MACROVISION input-status -replace define V4L2_IN_ST_NO_ACCESS input-status -replace define V4L2_IN_ST_VTR input-status -replace define V4L2_IN_ST_NO_V_LOCK input-status -replace define V4L2_IN_ST_NO_STD_LOCK input-status - -replace define V4L2_IN_CAP_DV_TIMINGS input-capabilities -replace define V4L2_IN_CAP_STD input-capabilities -replace define V4L2_IN_CAP_NATIVE_SIZE input-capabilities - -# V4L2 output - -replace define V4L2_OUTPUT_TYPE_MODULATOR output-type -replace define V4L2_OUTPUT_TYPE_ANALOG output-type -replace define V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY output-type - -replace define V4L2_OUT_CAP_DV_TIMINGS output-capabilities -replace define V4L2_OUT_CAP_STD output-capabilities -replace define V4L2_OUT_CAP_NATIVE_SIZE output-capabilities - -# V4L2 control flags - -replace define V4L2_CTRL_FLAG_DISABLED control-flags -replace define V4L2_CTRL_FLAG_GRABBED control-flags -replace define V4L2_CTRL_FLAG_READ_ONLY control-flags -replace define V4L2_CTRL_FLAG_UPDATE control-flags -replace define V4L2_CTRL_FLAG_INACTIVE control-flags -replace define V4L2_CTRL_FLAG_SLIDER control-flags -replace define V4L2_CTRL_FLAG_WRITE_ONLY control-flags -replace define V4L2_CTRL_FLAG_VOLATILE control-flags -replace define V4L2_CTRL_FLAG_HAS_PAYLOAD control-flags -replace define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE control-flags -replace define V4L2_CTRL_FLAG_MODIFY_LAYOUT control-flags -replace define V4L2_CTRL_FLAG_DYNAMIC_ARRAY control-flags -replace define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX control-flags - -replace define V4L2_CTRL_FLAG_NEXT_CTRL control -replace define V4L2_CTRL_FLAG_NEXT_COMPOUND control -replace define V4L2_CID_PRIVATE_BASE control - -# V4L2 tuner - -replace define V4L2_TUNER_CAP_LOW tuner-capability -replace define V4L2_TUNER_CAP_NORM tuner-capability -replace define V4L2_TUNER_CAP_HWSEEK_BOUNDED tuner-capability -replace define V4L2_TUNER_CAP_HWSEEK_WRAP tuner-capability -replace define V4L2_TUNER_CAP_STEREO tuner-capability -replace define V4L2_TUNER_CAP_LANG2 tuner-capability -replace define V4L2_TUNER_CAP_SAP tuner-capability -replace define V4L2_TUNER_CAP_LANG1 tuner-capability -replace define V4L2_TUNER_CAP_RDS tuner-capability -replace define V4L2_TUNER_CAP_RDS_BLOCK_IO tuner-capability -replace define V4L2_TUNER_CAP_RDS_CONTROLS tuner-capability -replace define V4L2_TUNER_CAP_FREQ_BANDS tuner-capability -replace define V4L2_TUNER_CAP_HWSEEK_PROG_LIM tuner-capability -replace define V4L2_TUNER_CAP_1HZ tuner-capability - -replace define V4L2_TUNER_SUB_MONO tuner-rxsubchans -replace define V4L2_TUNER_SUB_STEREO tuner-rxsubchans -replace define V4L2_TUNER_SUB_LANG2 tuner-rxsubchans -replace define V4L2_TUNER_SUB_SAP tuner-rxsubchans -replace define V4L2_TUNER_SUB_LANG1 tuner-rxsubchans -replace define V4L2_TUNER_SUB_RDS tuner-rxsubchans - -replace define V4L2_TUNER_MODE_MONO tuner-audmode -replace define V4L2_TUNER_MODE_STEREO tuner-audmode -replace define V4L2_TUNER_MODE_LANG2 tuner-audmode -replace define V4L2_TUNER_MODE_SAP tuner-audmode -replace define V4L2_TUNER_MODE_LANG1 tuner-audmode -replace define V4L2_TUNER_MODE_LANG1_LANG2 tuner-audmode - -replace define V4L2_BAND_MODULATION_VSB band-modulation -replace define V4L2_BAND_MODULATION_FM band-modulation -replace define V4L2_BAND_MODULATION_AM band-modulation - -replace define V4L2_RDS_BLOCK_MSK v4l2-rds-block -replace define V4L2_RDS_BLOCK_A v4l2-rds-block -replace define V4L2_RDS_BLOCK_B v4l2-rds-block -replace define V4L2_RDS_BLOCK_C v4l2-rds-block -replace define V4L2_RDS_BLOCK_D v4l2-rds-block -replace define V4L2_RDS_BLOCK_C_ALT v4l2-rds-block -replace define V4L2_RDS_BLOCK_INVALID v4l2-rds-block -replace define V4L2_RDS_BLOCK_CORRECTED v4l2-rds-block -replace define V4L2_RDS_BLOCK_ERROR v4l2-rds-block - -# V4L2 audio - -replace define V4L2_AUDCAP_STEREO audio-capability -replace define V4L2_AUDCAP_AVL audio-capability - -replace define V4L2_AUDMODE_AVL audio-mode - -# MPEG - -replace define V4L2_ENC_IDX_FRAME_I :c:type:`v4l2_enc_idx` -replace define V4L2_ENC_IDX_FRAME_P :c:type:`v4l2_enc_idx` -replace define V4L2_ENC_IDX_FRAME_B :c:type:`v4l2_enc_idx` -replace define V4L2_ENC_IDX_FRAME_MASK :c:type:`v4l2_enc_idx` -replace define V4L2_ENC_IDX_ENTRIES :c:type:`v4l2_enc_idx` - -replace define V4L2_ENC_CMD_START encoder-cmds -replace define V4L2_ENC_CMD_STOP encoder-cmds -replace define V4L2_ENC_CMD_PAUSE encoder-cmds -replace define V4L2_ENC_CMD_RESUME encoder-cmds - -replace define V4L2_ENC_CMD_STOP_AT_GOP_END encoder-flags - -replace define V4L2_DEC_CMD_START decoder-cmds -replace define V4L2_DEC_CMD_STOP decoder-cmds -replace define V4L2_DEC_CMD_PAUSE decoder-cmds -replace define V4L2_DEC_CMD_RESUME decoder-cmds -replace define V4L2_DEC_CMD_FLUSH decoder-cmds - -replace define V4L2_DEC_CMD_START_MUTE_AUDIO decoder-cmds -replace define V4L2_DEC_CMD_PAUSE_TO_BLACK decoder-cmds -replace define V4L2_DEC_CMD_STOP_TO_BLACK decoder-cmds -replace define V4L2_DEC_CMD_STOP_IMMEDIATELY decoder-cmds - -replace define V4L2_DEC_START_FMT_NONE decoder-cmds -replace define V4L2_DEC_START_FMT_GOP decoder-cmds - -# V4L2 VBI - -replace define V4L2_VBI_UNSYNC vbifmt-flags -replace define V4L2_VBI_INTERLACED vbifmt-flags - -replace define V4L2_VBI_ITU_525_F1_START :c:type:`v4l2_vbi_format` -replace define V4L2_VBI_ITU_525_F2_START :c:type:`v4l2_vbi_format` -replace define V4L2_VBI_ITU_625_F1_START :c:type:`v4l2_vbi_format` -replace define V4L2_VBI_ITU_625_F2_START :c:type:`v4l2_vbi_format` - - -replace define V4L2_SLICED_TELETEXT_B vbi-services -replace define V4L2_SLICED_VPS vbi-services -replace define V4L2_SLICED_CAPTION_525 vbi-services -replace define V4L2_SLICED_WSS_625 vbi-services -replace define V4L2_SLICED_VBI_525 vbi-services -replace define V4L2_SLICED_VBI_625 vbi-services - -replace define V4L2_MPEG_VBI_IVTV_TELETEXT_B ITV0-Line-Identifier-Constants -replace define V4L2_MPEG_VBI_IVTV_CAPTION_525 ITV0-Line-Identifier-Constants -replace define V4L2_MPEG_VBI_IVTV_WSS_625 ITV0-Line-Identifier-Constants -replace define V4L2_MPEG_VBI_IVTV_VPS ITV0-Line-Identifier-Constants - -replace define V4L2_MPEG_VBI_IVTV_MAGIC0 v4l2-mpeg-vbi-fmt-ivtv-magic -replace define V4L2_MPEG_VBI_IVTV_MAGIC1 v4l2-mpeg-vbi-fmt-ivtv-magic - -# V4L2 events - -replace define V4L2_EVENT_ALL event-type -replace define V4L2_EVENT_VSYNC event-type -replace define V4L2_EVENT_EOS event-type -replace define V4L2_EVENT_CTRL event-type -replace define V4L2_EVENT_FRAME_SYNC event-type -replace define V4L2_EVENT_SOURCE_CHANGE event-type -replace define V4L2_EVENT_MOTION_DET event-type -replace define V4L2_EVENT_PRIVATE_START event-type - -replace define V4L2_EVENT_CTRL_CH_VALUE ctrl-changes-flags -replace define V4L2_EVENT_CTRL_CH_FLAGS ctrl-changes-flags -replace define V4L2_EVENT_CTRL_CH_RANGE ctrl-changes-flags -replace define V4L2_EVENT_CTRL_CH_DIMENSIONS ctrl-changes-flags - -replace define V4L2_EVENT_SRC_CH_RESOLUTION src-changes-flags - -replace define V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ :c:type:`v4l2_event_motion_det` - -replace define V4L2_EVENT_SUB_FL_SEND_INITIAL event-flags -replace define V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK event-flags - -# V4L2 debugging -replace define V4L2_CHIP_MATCH_BRIDGE vidioc_dbg_g_register -replace define V4L2_CHIP_MATCH_SUBDEV vidioc_dbg_g_register -replace define V4L2_CHIP_MATCH_HOST vidioc_dbg_g_register -replace define V4L2_CHIP_MATCH_I2C_DRIVER vidioc_dbg_g_register -replace define V4L2_CHIP_MATCH_I2C_ADDR vidioc_dbg_g_register -replace define V4L2_CHIP_MATCH_AC97 vidioc_dbg_g_register - -replace define V4L2_CHIP_FL_READABLE vidioc_dbg_g_register -replace define V4L2_CHIP_FL_WRITABLE vidioc_dbg_g_register - -# Ignore reserved ioctl and ancillary macros - -ignore define VIDEO_MAX_FRAME -ignore define VIDEO_MAX_PLANES -ignore define v4l2_fourcc -ignore define v4l2_fourcc_be -ignore define V4L2_FIELD_HAS_TOP -ignore define V4L2_FIELD_HAS_BOTTOM -ignore define V4L2_FIELD_HAS_BOTH -ignore define V4L2_FIELD_HAS_T_OR_B -ignore define V4L2_TYPE_IS_MULTIPLANAR -ignore define V4L2_TYPE_IS_OUTPUT -ignore define V4L2_TUNER_ADC -ignore define V4L2_MAP_COLORSPACE_DEFAULT -ignore define V4L2_MAP_XFER_FUNC_DEFAULT -ignore define V4L2_MAP_YCBCR_ENC_DEFAULT -ignore define V4L2_DV_BT_BLANKING_WIDTH -ignore define V4L2_DV_BT_FRAME_WIDTH -ignore define V4L2_DV_BT_BLANKING_HEIGHT -ignore define V4L2_DV_BT_FRAME_HEIGHT -ignore define V4L2_IN_CAP_CUSTOM_TIMINGS -ignore define V4L2_CTRL_ID_MASK -ignore define V4L2_CTRL_ID2CLASS -ignore define V4L2_CTRL_ID2WHICH -ignore define V4L2_CTRL_DRIVER_PRIV -ignore define V4L2_CTRL_MAX_DIMS -ignore define V4L2_CTRL_WHICH_CUR_VAL -ignore define V4L2_CTRL_WHICH_DEF_VAL -ignore define V4L2_CTRL_WHICH_MIN_VAL -ignore define V4L2_CTRL_WHICH_MAX_VAL -ignore define V4L2_CTRL_WHICH_REQUEST_VAL -ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS -ignore define V4L2_CID_MAX_CTRLS - -ignore define BASE_VIDIOC_PRIVATE - -# Associate ioctls with their counterparts -replace ioctl VIDIOC_DBG_S_REGISTER vidioc_dbg_g_register -replace ioctl VIDIOC_DQBUF vidioc_qbuf -replace ioctl VIDIOC_S_AUDOUT vidioc_g_audout -replace ioctl VIDIOC_S_CROP vidioc_g_crop -replace ioctl VIDIOC_S_CTRL vidioc_g_ctrl -replace ioctl VIDIOC_S_DV_TIMINGS vidioc_g_dv_timings -replace ioctl VIDIOC_S_EDID vidioc_g_edid -replace ioctl VIDIOC_S_EXT_CTRLS vidioc_g_ext_ctrls -replace ioctl VIDIOC_S_FBUF vidioc_g_fbuf -replace ioctl VIDIOC_S_FMT vidioc_g_fmt -replace ioctl VIDIOC_S_FREQUENCY vidioc_g_frequency -replace ioctl VIDIOC_S_INPUT vidioc_g_input -replace ioctl VIDIOC_S_JPEGCOMP vidioc_g_jpegcomp -replace ioctl VIDIOC_S_MODULATOR vidioc_g_modulator -replace ioctl VIDIOC_S_OUTPUT vidioc_g_output -replace ioctl VIDIOC_S_PARM vidioc_g_parm -replace ioctl VIDIOC_S_PRIORITY vidioc_g_priority -replace ioctl VIDIOC_S_SELECTION vidioc_g_selection -replace ioctl VIDIOC_S_STD vidioc_g_std -replace ioctl VIDIOC_S_AUDIO vidioc_g_audio -replace ioctl VIDIOC_S_TUNER vidioc_g_tuner -replace ioctl VIDIOC_TRY_DECODER_CMD vidioc_decoder_cmd -replace ioctl VIDIOC_TRY_ENCODER_CMD vidioc_encoder_cmd -replace ioctl VIDIOC_TRY_EXT_CTRLS vidioc_g_ext_ctrls -replace ioctl VIDIOC_TRY_FMT vidioc_g_fmt -replace ioctl VIDIOC_STREAMOFF vidioc_streamon -replace ioctl VIDIOC_QUERY_EXT_CTRL vidioc_queryctrl -replace ioctl VIDIOC_QUERYMENU vidioc_queryctrl diff --git a/scripts/sphinx-build-wrapper b/scripts/sphinx-build-wrapper new file mode 100755 index 000000000000..abe8c26ae137 --- /dev/null +++ b/scripts/sphinx-build-wrapper @@ -0,0 +1,719 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2025 Mauro Carvalho Chehab +# +# pylint: disable=R0902, R0912, R0913, R0914, R0915, R0917, C0103 +# +# Converted from docs Makefile and parallel-wrapper.sh, both under +# GPLv2, copyrighted since 2008 by the following authors: +# +# Akira Yokosawa +# Arnd Bergmann +# Breno Leitao +# Carlos Bilbao +# Dave Young +# Donald Hunter +# Geert Uytterhoeven +# Jani Nikula +# Jan Stancek +# Jonathan Corbet +# Joshua Clayton +# Kees Cook +# Linus Torvalds +# Magnus Damm +# Masahiro Yamada +# Mauro Carvalho Chehab +# Maxim Cournoyer +# Peter Foley +# Randy Dunlap +# Rob Herring +# Shuah Khan +# Thorsten Blum +# Tomas Winkler + + +""" +Sphinx build wrapper that handles Kernel-specific business rules: + +- it gets the Kernel build environment vars; +- it determines what's the best parallelism; +- it handles SPHINXDIRS + +This tool ensures that MIN_PYTHON_VERSION is satisfied. If version is +below that, it seeks for a new Python version. If found, it re-runs using +the newer version. +""" + +import argparse +import locale +import os +import re +import shlex +import shutil +import subprocess +import sys + +from concurrent import futures +from glob import glob + +LIB_DIR = "lib" +SRC_DIR = os.path.dirname(os.path.realpath(__file__)) + +sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) + +from jobserver import JobserverExec # pylint: disable=C0413 + + +def parse_version(version): + """Convert a major.minor.patch version into a tuple""" + return tuple(int(x) for x in version.split(".")) + +def ver_str(version): + """Returns a version tuple as major.minor.patch""" + + return ".".join([str(x) for x in version]) + +# Minimal supported Python version needed by Sphinx and its extensions +MIN_PYTHON_VERSION = parse_version("3.7") + +# Default value for --venv parameter +VENV_DEFAULT = "sphinx_latest" + +# List of make targets and its corresponding builder and output directory +TARGETS = { + "cleandocs": { + "builder": "clean", + }, + "htmldocs": { + "builder": "html", + }, + "epubdocs": { + "builder": "epub", + "out_dir": "epub", + }, + "texinfodocs": { + "builder": "texinfo", + "out_dir": "texinfo", + }, + "infodocs": { + "builder": "texinfo", + "out_dir": "texinfo", + }, + "latexdocs": { + "builder": "latex", + "out_dir": "latex", + }, + "pdfdocs": { + "builder": "latex", + "out_dir": "latex", + }, + "xmldocs": { + "builder": "xml", + "out_dir": "xml", + }, + "linkcheckdocs": { + "builder": "linkcheck" + }, +} + +# Paper sizes. An empty value will pick the default +PAPER = ["", "a4", "letter"] + +class SphinxBuilder: + """ + Handles a sphinx-build target, adding needed arguments to build + with the Kernel. + """ + + def is_rust_enabled(self): + """Check if rust is enabled at .config""" + config_path = os.path.join(self.srctree, ".config") + if os.path.isfile(config_path): + with open(config_path, "r", encoding="utf-8") as f: + return "CONFIG_RUST=y" in f.read() + return False + + def get_path(self, path, abs_path=False): + """ + Ancillary routine to handle patches the right way, as shell does. + + It first expands "~" and "~user". Then, if patch is not absolute, + join self.srctree. Finally, if requested, convert to abspath. + """ + + path = os.path.expanduser(path) + if not path.startswith("/"): + path = os.path.join(self.srctree, path) + + if abs_path: + return os.path.abspath(path) + + return path + + def __init__(self, venv=None, verbose=False, n_jobs=None, interactive=None): + """Initialize internal variables""" + self.venv = venv + self.verbose = None + + # Normal variables passed from Kernel's makefile + self.kernelversion = os.environ.get("KERNELVERSION", "unknown") + self.kernelrelease = os.environ.get("KERNELRELEASE", "unknown") + self.pdflatex = os.environ.get("PDFLATEX", "xelatex") + + if not interactive: + self.latexopts = os.environ.get("LATEXOPTS", "-interaction=batchmode -no-shell-escape") + else: + self.latexopts = os.environ.get("LATEXOPTS", "") + + if not verbose: + verbose = bool(os.environ.get("KBUILD_VERBOSE", "") != "") + + # Handle SPHINXOPTS evironment + sphinxopts = shlex.split(os.environ.get("SPHINXOPTS", "")) + + # As we handle number of jobs and quiet in separate, we need to pick + # it the same way as sphinx-build would pick, so let's use argparse + # do to the right argument expansion + parser = argparse.ArgumentParser() + parser.add_argument('-j', '--jobs', type=int) + parser.add_argument('-q', '--quiet', type=int) + + # Other sphinx-build arguments go as-is, so place them + # at self.sphinxopts + sphinx_args, self.sphinxopts = parser.parse_known_args(sphinxopts) + if sphinx_args.quiet == True: + self.verbose = False + + if sphinx_args.jobs: + self.n_jobs = sphinx_args.jobs + + # Command line arguments was passed, override SPHINXOPTS + if verbose is not None: + self.verbose = verbose + + self.n_jobs = n_jobs + + # Source tree directory. This needs to be at os.environ, as + # Sphinx extensions and media uAPI makefile needs it + self.srctree = os.environ.get("srctree") + if not self.srctree: + self.srctree = "." + os.environ["srctree"] = self.srctree + + # Now that we can expand srctree, get other directories as well + self.sphinxbuild = os.environ.get("SPHINXBUILD", "sphinx-build") + self.kerneldoc = self.get_path(os.environ.get("KERNELDOC", + "scripts/kernel-doc.py")) + self.obj = os.environ.get("obj", "Documentation") + self.builddir = self.get_path(os.path.join(self.obj, "output"), + abs_path=True) + + # Media uAPI needs it + os.environ["BUILDDIR"] = self.builddir + + # Detect if rust is enabled + self.config_rust = self.is_rust_enabled() + + # Get directory locations for LaTeX build toolchain + self.pdflatex_cmd = shutil.which(self.pdflatex) + self.latexmk_cmd = shutil.which("latexmk") + + self.env = os.environ.copy() + + # If venv parameter is specified, run Sphinx from venv + if venv: + bin_dir = os.path.join(venv, "bin") + if os.path.isfile(os.path.join(bin_dir, "activate")): + # "activate" virtual env + self.env["PATH"] = bin_dir + ":" + self.env["PATH"] + self.env["VIRTUAL_ENV"] = venv + if "PYTHONHOME" in self.env: + del self.env["PYTHONHOME"] + print(f"Setting venv to {venv}") + else: + sys.exit(f"Venv {venv} not found.") + + def run_sphinx(self, sphinx_build, build_args, *args, **pwargs): + """ + Executes sphinx-build using current python3 command and setting + -j parameter if possible to run the build in parallel. + """ + + with JobserverExec() as jobserver: + if jobserver.claim: + n_jobs = str(jobserver.claim) + else: + n_jobs = "auto" # Supported since Sphinx 1.7 + + cmd = [] + + if self.venv: + cmd.append("python") + else: + cmd.append(sys.executable) + + cmd.append(sphinx_build) + + # if present, SPHINXOPTS or command line --jobs overrides default + if self.n_jobs: + n_jobs = str(self.n_jobs) + + if n_jobs: + cmd += [f"-j{n_jobs}"] + + if not self.verbose: + cmd.append("-q") + + cmd += self.sphinxopts + + cmd += build_args + + if self.verbose: + print(" ".join(cmd)) + + rc = subprocess.call(cmd, *args, **pwargs) + + def handle_html(self, css, output_dir): + """ + Extra steps for HTML and epub output. + + For such targets, we need to ensure that CSS will be properly + copied to the output _static directory + """ + + if not css: + return + + css = os.path.expanduser(css) + if not css.startswith("/"): + css = os.path.join(self.srctree, css) + + static_dir = os.path.join(output_dir, "_static") + os.makedirs(static_dir, exist_ok=True) + + try: + shutil.copy2(css, static_dir) + except (OSError, IOError) as e: + print(f"Warning: Failed to copy CSS: {e}", file=sys.stderr) + + def build_pdf_file(self, latex_cmd, from_dir, path): + """Builds a single pdf file using latex_cmd""" + try: + subprocess.run(latex_cmd + [path], + cwd=from_dir, check=True) + + return True + except subprocess.CalledProcessError: + # LaTeX PDF error code is almost useless: it returns + # error codes even when build succeeds but has warnings. + # So, we'll ignore the results + return False + + def pdf_parallel_build(self, tex_suffix, latex_cmd, tex_files, n_jobs): + """Build PDF files in parallel if possible""" + builds = {} + build_failed = False + max_len = 0 + has_tex = False + + # Process files in parallel + with futures.ThreadPoolExecutor(max_workers=n_jobs) as executor: + jobs = {} + + for from_dir, pdf_dir, entry in tex_files: + name = entry.name + + if not name.endswith(tex_suffix): + continue + + name = name[:-len(tex_suffix)] + + max_len = max(max_len, len(name)) + + has_tex = True + + future = executor.submit(self.build_pdf_file, latex_cmd, + from_dir, entry.path) + jobs[future] = (from_dir, name, entry.path) + + for future in futures.as_completed(jobs): + from_dir, name, path = jobs[future] + + pdf_name = name + ".pdf" + pdf_from = os.path.join(from_dir, pdf_name) + + try: + success = future.result() + + if success and os.path.exists(pdf_from): + pdf_to = os.path.join(pdf_dir, pdf_name) + + os.rename(pdf_from, pdf_to) + builds[name] = os.path.relpath(pdf_to, self.builddir) + else: + builds[name] = "FAILED" + build_failed = True + except Exception as e: + builds[name] = f"FAILED ({str(e)})" + build_failed = True + + # Handle case where no .tex files were found + if not has_tex: + name = "Sphinx LaTeX builder" + max_len = max(max_len, len(name)) + builds[name] = "FAILED (no .tex file was generated)" + build_failed = True + + return builds, build_failed, max_len + + def handle_pdf(self, output_dirs): + """ + Extra steps for PDF output. + + As PDF is handled via a LaTeX output, after building the .tex file, + a new build is needed to create the PDF output from the latex + directory. + """ + builds = {} + max_len = 0 + tex_suffix = ".tex" + + # Get all tex files that will be used for PDF build + tex_files = [] + for from_dir in output_dirs: + pdf_dir = os.path.join(from_dir, "../pdf") + os.makedirs(pdf_dir, exist_ok=True) + + if self.latexmk_cmd: + latex_cmd = [self.latexmk_cmd, f"-{self.pdflatex}"] + else: + latex_cmd = [self.pdflatex] + + latex_cmd.extend(shlex.split(self.latexopts)) + + # Get a list of tex files to process + with os.scandir(from_dir) as it: + for entry in it: + if entry.name.endswith(tex_suffix): + tex_files.append((from_dir, pdf_dir, entry)) + + # When using make, this won't be used, as the number of jobs comes + # from POSIX jobserver. So, this covers the case where build comes + # from command line. On such case, serialize by default, except if + # the user explicitly sets the number of jobs. + n_jobs = 1 + + # n_jobs is either an integer or "auto". Only use it if it is a number + if self.n_jobs: + try: + n_jobs = int(self.n_jobs) + except ValueError: + pass + + # When using make, jobserver.claim is the number of jobs that were + # used with "-j" and that aren't used by other make targets + with JobserverExec() as jobserver: + n_jobs = 1 + + # Handle the case when a parameter is passed via command line, + # using it as default, if jobserver doesn't claim anything + if self.n_jobs: + try: + n_jobs = int(self.n_jobs) + except ValueError: + pass + + if jobserver.claim: + n_jobs = jobserver.claim + + # Build files in parallel + builds, build_failed, max_len = self.pdf_parallel_build(tex_suffix, + latex_cmd, + tex_files, + n_jobs) + + msg = "Summary" + msg += "\n" + "=" * len(msg) + print() + print(msg) + + for pdf_name, pdf_file in builds.items(): + print(f"{pdf_name:<{max_len}}: {pdf_file}") + + print() + + # return an error if a PDF file is missing + + if build_failed: + sys.exit(f"PDF build failed: not all PDF files were created.") + else: + print("All PDF files were built.") + + def handle_info(self, output_dirs): + """ + Extra steps for Info output. + + For texinfo generation, an additional make is needed from the + texinfo directory. + """ + + for output_dir in output_dirs: + try: + subprocess.run(["make", "info"], cwd=output_dir, check=True) + except subprocess.CalledProcessError as e: + sys.exit(f"Error generating info docs: {e}") + + def cleandocs(self, builder): + + shutil.rmtree(self.builddir, ignore_errors=True) + + def build(self, target, sphinxdirs=None, conf="conf.py", + theme=None, css=None, paper=None): + """ + Build documentation using Sphinx. This is the core function of this + module. It prepares all arguments required by sphinx-build. + """ + + builder = TARGETS[target]["builder"] + out_dir = TARGETS[target].get("out_dir", "") + + # Cleandocs doesn't require sphinx-build + if target == "cleandocs": + self.cleandocs(builder) + return + + # Other targets require sphinx-build + sphinxbuild = shutil.which(self.sphinxbuild, path=self.env["PATH"]) + if not sphinxbuild: + sys.exit(f"Error: {self.sphinxbuild} not found in PATH.\n") + + if builder == "latex": + if not self.pdflatex_cmd and not self.latexmk_cmd: + sys.exit("Error: pdflatex or latexmk required for PDF generation") + + docs_dir = os.path.abspath(os.path.join(self.srctree, "Documentation")) + + # Prepare base arguments for Sphinx build + kerneldoc = self.kerneldoc + if kerneldoc.startswith(self.srctree): + kerneldoc = os.path.relpath(kerneldoc, self.srctree) + + # Prepare common Sphinx options + args = [ + "-b", builder, + "-c", docs_dir, + ] + + if builder == "latex": + if not paper: + paper = PAPER[1] + + args.extend(["-D", f"latex_elements.papersize={paper}paper"]) + + if self.config_rust: + args.extend(["-t", "rustdoc"]) + + if conf: + self.env["SPHINX_CONF"] = self.get_path(conf, abs_path=True) + + if not sphinxdirs: + sphinxdirs = os.environ.get("SPHINXDIRS", ".") + + # The sphinx-build tool has a bug: internally, it tries to set + # locale with locale.setlocale(locale.LC_ALL, ''). This causes a + # crash if language is not set. Detect and fix it. + try: + locale.setlocale(locale.LC_ALL, '') + except Exception: + self.env["LC_ALL"] = "C" + self.env["LANG"] = "C" + + # sphinxdirs can be a list or a whitespace-separated string + sphinxdirs_list = [] + for sphinxdir in sphinxdirs: + if isinstance(sphinxdir, list): + sphinxdirs_list += sphinxdir + else: + for name in sphinxdir.split(" "): + sphinxdirs_list.append(name) + + # Build each directory + output_dirs = [] + for sphinxdir in sphinxdirs_list: + src_dir = os.path.join(docs_dir, sphinxdir) + doctree_dir = os.path.join(self.builddir, ".doctrees") + output_dir = os.path.join(self.builddir, sphinxdir, out_dir) + + # Make directory names canonical + src_dir = os.path.normpath(src_dir) + doctree_dir = os.path.normpath(doctree_dir) + output_dir = os.path.normpath(output_dir) + + os.makedirs(doctree_dir, exist_ok=True) + os.makedirs(output_dir, exist_ok=True) + + output_dirs.append(output_dir) + + build_args = args + [ + "-d", doctree_dir, + "-D", f"kerneldoc_bin={kerneldoc}", + "-D", f"version={self.kernelversion}", + "-D", f"release={self.kernelrelease}", + "-D", f"kerneldoc_srctree={self.srctree}", + src_dir, + output_dir, + ] + + # Execute sphinx-build + try: + self.run_sphinx(sphinxbuild, build_args, env=self.env) + except Exception as e: + sys.exit(f"Build failed: {e}") + + # Ensure that html/epub will have needed static files + if target in ["htmldocs", "epubdocs"]: + self.handle_html(css, output_dir) + + # PDF and Info require a second build step + if target == "pdfdocs": + self.handle_pdf(output_dirs) + elif target == "infodocs": + self.handle_info(output_dirs) + + @staticmethod + def get_python_version(cmd): + """ + Get python version from a Python binary. As we need to detect if + are out there newer python binaries, we can't rely on sys.release here. + """ + + result = subprocess.run([cmd, "--version"], check=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True) + version = result.stdout.strip() + + match = re.search(r"(\d+\.\d+\.\d+)", version) + if match: + return parse_version(match.group(1)) + + print(f"Can't parse version {version}") + return (0, 0, 0) + + @staticmethod + def find_python(): + """ + Detect if are out there any python 3.xy version newer than the + current one. + + Note: this routine is limited to up to 2 digits for python3. We + may need to update it one day, hopefully on a distant future. + """ + patterns = [ + "python3.[0-9]", + "python3.[0-9][0-9]", + ] + + # Seek for a python binary newer than MIN_PYTHON_VERSION + for path in os.getenv("PATH", "").split(":"): + for pattern in patterns: + for cmd in glob(os.path.join(path, pattern)): + if os.path.isfile(cmd) and os.access(cmd, os.X_OK): + version = SphinxBuilder.get_python_version(cmd) + if version >= MIN_PYTHON_VERSION: + return cmd + + return None + + @staticmethod + def check_python(): + """ + Check if the current python binary satisfies our minimal requirement + for Sphinx build. If not, re-run with a newer version if found. + """ + cur_ver = sys.version_info[:3] + if cur_ver >= MIN_PYTHON_VERSION: + return + + python_ver = ver_str(cur_ver) + + new_python_cmd = SphinxBuilder.find_python() + if not new_python_cmd: + sys.exit(f"Python version {python_ver} is not supported anymore.") + + # Restart script using the newer version + script_path = os.path.abspath(sys.argv[0]) + args = [new_python_cmd, script_path] + sys.argv[1:] + + print(f"Python {python_ver} not supported. Changing to {new_python_cmd}") + + try: + os.execv(new_python_cmd, args) + except OSError as e: + sys.exit(f"Failed to restart with {new_python_cmd}: {e}") + +def jobs_type(value): + """ + Handle valid values for -j. Accepts Sphinx "-jauto", plus a number + equal or bigger than one. + """ + if value is None: + return None + + if value.lower() == 'auto': + return value.lower() + + try: + if int(value) >= 1: + return value + + raise argparse.ArgumentTypeError(f"Minimum jobs is 1, got {value}") + except ValueError: + raise argparse.ArgumentTypeError(f"Must be 'auto' or positive integer, got {value}") + +def main(): + """ + Main function. The only mandatory argument is the target. If not + specified, the other arguments will use default values if not + specified at os.environ. + """ + parser = argparse.ArgumentParser(description="Kernel documentation builder") + + parser.add_argument("target", choices=list(TARGETS.keys()), + help="Documentation target to build") + parser.add_argument("--sphinxdirs", nargs="+", + help="Specific directories to build") + parser.add_argument("--conf", default="conf.py", + help="Sphinx configuration file") + + parser.add_argument("--theme", help="Sphinx theme to use") + + parser.add_argument("--css", help="Custom CSS file for HTML/EPUB") + + parser.add_argument("--paper", choices=PAPER, default=PAPER[0], + help="Paper size for LaTeX/PDF output") + + parser.add_argument("-v", "--verbose", action='store_true', + help="place build in verbose mode") + + parser.add_argument('-j', '--jobs', type=jobs_type, + help="Sets number of jobs to use with sphinx-build") + + parser.add_argument('-i', '--interactive', action='store_true', + help="Change latex default to run in interactive mode") + + parser.add_argument("-V", "--venv", nargs='?', const=f'{VENV_DEFAULT}', + default=None, + help=f'If used, run Sphinx from a venv dir (default dir: {VENV_DEFAULT})') + + args = parser.parse_args() + + SphinxBuilder.check_python() + + builder = SphinxBuilder(venv=args.venv, verbose=args.verbose, + n_jobs=args.jobs, interactive=args.interactive) + + builder.build(args.target, sphinxdirs=args.sphinxdirs, conf=args.conf, + theme=args.theme, css=args.css, paper=args.paper) + +if __name__ == "__main__": + main() -- cgit v1.2.3 From 8851e27d2cb947ea8bbbe8e812068f7bf5cbd00b Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Fri, 29 Aug 2025 21:55:25 +0200 Subject: rust: support Rust >= 1.91.0 target spec Starting with Rust 1.91.0 (expected 2025-10-30), the target spec format has changed the type of the `target-pointer-width` key from string to integer [1]. Thus conditionally use one or the other depending on the version. Cc: Waffle Maybe Link: https://github.com/rust-lang/rust/pull/144443 [1] Link: https://lore.kernel.org/r/20250829195525.721664-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- scripts/generate_rust_target.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs index 39c82908ff3a..38b3416bb979 100644 --- a/scripts/generate_rust_target.rs +++ b/scripts/generate_rust_target.rs @@ -225,7 +225,11 @@ fn main() { ts.push("features", features); ts.push("llvm-target", "x86_64-linux-gnu"); ts.push("supported-sanitizers", ["kcfi", "kernel-address"]); - ts.push("target-pointer-width", "64"); + if cfg.rustc_version_atleast(1, 91, 0) { + ts.push("target-pointer-width", 64); + } else { + ts.push("target-pointer-width", "64"); + } } else if cfg.has("X86_32") { // This only works on UML, as i386 otherwise needs regparm support in rustc if !cfg.has("UML") { @@ -245,7 +249,11 @@ fn main() { } ts.push("features", features); ts.push("llvm-target", "i386-unknown-linux-gnu"); - ts.push("target-pointer-width", "32"); + if cfg.rustc_version_atleast(1, 91, 0) { + ts.push("target-pointer-width", 32); + } else { + ts.push("target-pointer-width", "32"); + } } else if cfg.has("LOONGARCH") { panic!("loongarch uses the builtin rustc loongarch64-unknown-none-softfloat target"); } else { -- cgit v1.2.3 From 3c847e17225aa9481fc3f6c948f5c718dea526f1 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Thu, 14 Aug 2025 11:30:28 +0200 Subject: rust: add `pin-init` as a dependency to `bindings` and `uapi` This allows `bindings` and `uapi` to implement `Zeroable` and use other items from pin-init. Co-developed-by: Miguel Ojeda Signed-off-by: Miguel Ojeda Link: https://rust-for-linux.zulipchat.com/#narrow/channel/291565-Help/topic/Zeroable.20trait.20for.20C.20structs/near/510264158 Signed-off-by: Benno Lossin Reviewed-by: Alice Ryhl Signed-off-by: Miguel Ojeda --- rust/Makefile | 14 ++++++++------ scripts/generate_rust_analyzer.py | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/rust/Makefile b/rust/Makefile index 29c941024e6f..23c7ae905bd2 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -199,12 +199,12 @@ rusttestlib-kernel: $(src)/kernel/lib.rs rusttestlib-bindings rusttestlib-uapi \ $(obj)/bindings.o FORCE +$(call if_changed,rustc_test_library) -rusttestlib-bindings: private rustc_target_flags = --extern ffi -rusttestlib-bindings: $(src)/bindings/lib.rs rusttestlib-ffi FORCE +rusttestlib-bindings: private rustc_target_flags = --extern ffi --extern pin_init +rusttestlib-bindings: $(src)/bindings/lib.rs rusttestlib-ffi rusttestlib-pin_init FORCE +$(call if_changed,rustc_test_library) -rusttestlib-uapi: private rustc_target_flags = --extern ffi -rusttestlib-uapi: $(src)/uapi/lib.rs rusttestlib-ffi FORCE +rusttestlib-uapi: private rustc_target_flags = --extern ffi --extern pin_init +rusttestlib-uapi: $(src)/uapi/lib.rs rusttestlib-ffi rusttestlib-pin_init FORCE +$(call if_changed,rustc_test_library) quiet_cmd_rustdoc_test = RUSTDOC T $< @@ -530,17 +530,19 @@ $(obj)/ffi.o: private skip_gendwarfksyms = 1 $(obj)/ffi.o: $(src)/ffi.rs $(obj)/compiler_builtins.o FORCE +$(call if_changed_rule,rustc_library) -$(obj)/bindings.o: private rustc_target_flags = --extern ffi +$(obj)/bindings.o: private rustc_target_flags = --extern ffi --extern pin_init $(obj)/bindings.o: $(src)/bindings/lib.rs \ $(obj)/ffi.o \ + $(obj)/pin_init.o \ $(obj)/bindings/bindings_generated.rs \ $(obj)/bindings/bindings_helpers_generated.rs FORCE +$(call if_changed_rule,rustc_library) -$(obj)/uapi.o: private rustc_target_flags = --extern ffi +$(obj)/uapi.o: private rustc_target_flags = --extern ffi --extern pin_init $(obj)/uapi.o: private skip_gendwarfksyms = 1 $(obj)/uapi.o: $(src)/uapi/lib.rs \ $(obj)/ffi.o \ + $(obj)/pin_init.o \ $(obj)/uapi/uapi_generated.rs FORCE +$(call if_changed_rule,rustc_library) diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py index 7c3ea2b55041..fc27f0cca752 100755 --- a/scripts/generate_rust_analyzer.py +++ b/scripts/generate_rust_analyzer.py @@ -139,8 +139,8 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit "exclude_dirs": [], } - append_crate_with_generated("bindings", ["core", "ffi"]) - append_crate_with_generated("uapi", ["core", "ffi"]) + append_crate_with_generated("bindings", ["core", "ffi", "pin_init"]) + append_crate_with_generated("uapi", ["core", "ffi", "pin_init"]) append_crate_with_generated("kernel", ["core", "macros", "build_error", "pin_init", "ffi", "bindings", "uapi"]) def is_root_crate(build_file, target): -- cgit v1.2.3 From 009eb5da29a91016e3ebb988e6401e79411be7a1 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Thu, 21 Aug 2025 15:28:15 +0200 Subject: hrtimer: Remove hrtimer_clock_base:: Get_time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The get_time() callbacks always need to match the bases clockid. Instead of maintaining that association twice in hrtimer_bases, use a helper. Signed-off-by: Thomas Weißschuh Signed-off-by: Thomas Gleixner Acked-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/all/20250821-hrtimer-cleanup-get_time-v2-8-3ae822e5bfbd@linutronix.de --- include/linux/hrtimer.h | 5 +---- include/linux/hrtimer_defs.h | 2 -- kernel/time/hrtimer.c | 34 +++++++++++++++++++++++++--------- kernel/time/timer_list.c | 2 -- scripts/gdb/linux/timerlist.py | 2 -- 5 files changed, 26 insertions(+), 19 deletions(-) (limited to 'scripts') diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index e655502b14e6..2cf1bf65b225 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -154,10 +154,7 @@ static inline s64 hrtimer_get_expires_ns(const struct hrtimer *timer) return ktime_to_ns(timer->node.expires); } -static inline ktime_t hrtimer_cb_get_time(const struct hrtimer *timer) -{ - return timer->base->get_time(); -} +ktime_t hrtimer_cb_get_time(const struct hrtimer *timer); static inline ktime_t hrtimer_expires_remaining(const struct hrtimer *timer) { diff --git a/include/linux/hrtimer_defs.h b/include/linux/hrtimer_defs.h index 84a5045f80f3..aa49ffa130e5 100644 --- a/include/linux/hrtimer_defs.h +++ b/include/linux/hrtimer_defs.h @@ -41,7 +41,6 @@ * @seq: seqcount around __run_hrtimer * @running: pointer to the currently running hrtimer * @active: red black tree root node for the active timers - * @get_time: function to retrieve the current time of the clock * @offset: offset of this clock to the monotonic base */ struct hrtimer_clock_base { @@ -51,7 +50,6 @@ struct hrtimer_clock_base { seqcount_raw_spinlock_t seq; struct hrtimer *running; struct timerqueue_head active; - ktime_t (*get_time)(void); ktime_t offset; } __hrtimer_clock_base_align; diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 30899a8cc52c..fedd1d793f6c 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -59,6 +59,7 @@ #define HRTIMER_ACTIVE_ALL (HRTIMER_ACTIVE_SOFT | HRTIMER_ACTIVE_HARD) static void retrigger_next_event(void *arg); +static ktime_t __hrtimer_cb_get_time(clockid_t clock_id); /* * The timer bases: @@ -76,42 +77,34 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = { .index = HRTIMER_BASE_MONOTONIC, .clockid = CLOCK_MONOTONIC, - .get_time = &ktime_get, }, { .index = HRTIMER_BASE_REALTIME, .clockid = CLOCK_REALTIME, - .get_time = &ktime_get_real, }, { .index = HRTIMER_BASE_BOOTTIME, .clockid = CLOCK_BOOTTIME, - .get_time = &ktime_get_boottime, }, { .index = HRTIMER_BASE_TAI, .clockid = CLOCK_TAI, - .get_time = &ktime_get_clocktai, }, { .index = HRTIMER_BASE_MONOTONIC_SOFT, .clockid = CLOCK_MONOTONIC, - .get_time = &ktime_get, }, { .index = HRTIMER_BASE_REALTIME_SOFT, .clockid = CLOCK_REALTIME, - .get_time = &ktime_get_real, }, { .index = HRTIMER_BASE_BOOTTIME_SOFT, .clockid = CLOCK_BOOTTIME, - .get_time = &ktime_get_boottime, }, { .index = HRTIMER_BASE_TAI_SOFT, .clockid = CLOCK_TAI, - .get_time = &ktime_get_clocktai, }, }, .csd = CSD_INIT(retrigger_next_event, NULL) @@ -1253,7 +1246,7 @@ static int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, remove_hrtimer(timer, base, true, force_local); if (mode & HRTIMER_MODE_REL) - tim = ktime_add_safe(tim, base->get_time()); + tim = ktime_add_safe(tim, __hrtimer_cb_get_time(base->clockid)); tim = hrtimer_update_lowres(timer, tim, mode); @@ -1588,6 +1581,29 @@ static inline int hrtimer_clockid_to_base(clockid_t clock_id) } } +static ktime_t __hrtimer_cb_get_time(clockid_t clock_id) +{ + switch (clock_id) { + case CLOCK_MONOTONIC: + return ktime_get(); + case CLOCK_REALTIME: + return ktime_get_real(); + case CLOCK_BOOTTIME: + return ktime_get_boottime(); + case CLOCK_TAI: + return ktime_get_clocktai(); + default: + WARN(1, "Invalid clockid %d. Using MONOTONIC\n", clock_id); + return ktime_get(); + } +} + +ktime_t hrtimer_cb_get_time(const struct hrtimer *timer) +{ + return __hrtimer_cb_get_time(timer->base->clockid); +} +EXPORT_SYMBOL_GPL(hrtimer_cb_get_time); + static void __hrtimer_setup(struct hrtimer *timer, enum hrtimer_restart (*function)(struct hrtimer *), clockid_t clock_id, enum hrtimer_mode mode) diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c index b03d0ada6469..488e47e96e93 100644 --- a/kernel/time/timer_list.c +++ b/kernel/time/timer_list.c @@ -102,8 +102,6 @@ print_base(struct seq_file *m, struct hrtimer_clock_base *base, u64 now) SEQ_printf(m, " .index: %d\n", base->index); SEQ_printf(m, " .resolution: %u nsecs\n", hrtimer_resolution); - - SEQ_printf(m, " .get_time: %ps\n", base->get_time); #ifdef CONFIG_HIGH_RES_TIMERS SEQ_printf(m, " .offset: %Lu nsecs\n", (unsigned long long) ktime_to_ns(base->offset)); diff --git a/scripts/gdb/linux/timerlist.py b/scripts/gdb/linux/timerlist.py index 98445671fe83..ccc24d30de80 100644 --- a/scripts/gdb/linux/timerlist.py +++ b/scripts/gdb/linux/timerlist.py @@ -56,8 +56,6 @@ def print_base(base): text += " .index: {}\n".format(base['index']) text += " .resolution: {} nsecs\n".format(constants.LX_hrtimer_resolution) - - text += " .get_time: {}\n".format(base['get_time']) if constants.LX_CONFIG_HIGH_RES_TIMERS: text += " .offset: {} nsecs\n".format(base['offset']) text += "active timers:\n" -- cgit v1.2.3 From a6cf527e66b8ea7b3119a354a873ebfa57ea225e Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 9 Aug 2025 21:31:10 -0700 Subject: checkpatch: allow http links of any length in commit logs Dave Gilbert noticed that checkpatch warns about URL links over 75 chars in length in commit logs. Fix that. Link: https://lkml.kernel.org/r/3529faaf84a5a9a96c5c0ec4183ae0ba6e97673c.camel@perches.com Signed-off-by: Joe Perches Cc: Dave Gilbert Signed-off-by: Andrew Morton --- scripts/checkpatch.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index e722dd6fa8ef..319cc5f85885 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3294,7 +3294,7 @@ sub process { # file delta changes $line =~ /^\s*(?:[\w\.\-\+]*\/)++[\w\.\-\+]+:/ || # filename then : - $line =~ /^\s*(?:Fixes:|$link_tags_search|$signature_tags)/i || + $line =~ /^\s*(?:Fixes:|https?:|$link_tags_search|$signature_tags)/i || # A Fixes:, link or signature tag line $commit_log_possible_stack_dump)) { WARN("COMMIT_LOG_LONG_LINE", -- cgit v1.2.3 From fdd7c7e0d2ab3987882c570612d4622f437292c7 Mon Sep 17 00:00:00 2001 From: Boqun Feng Date: Thu, 4 Sep 2025 21:41:28 -0700 Subject: rust: Introduce atomic API helpers In order to support LKMM atomics in Rust, add rust_helper_* for atomic APIs. These helpers ensure the implementation of LKMM atomics in Rust is the same as in C. This could save the maintenance burden of having two similar atomic implementations in asm. Originally-by: Mark Rutland Signed-off-by: Boqun Feng Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Alice Ryhl Link: https://lore.kernel.org/all/20250719030827.61357-2-boqun.feng@gmail.com/ --- rust/helpers/atomic.c | 1040 +++++++++++++++++++++++++++++ rust/helpers/helpers.c | 1 + scripts/atomic/gen-atomics.sh | 1 + scripts/atomic/gen-rust-atomic-helpers.sh | 67 ++ 4 files changed, 1109 insertions(+) create mode 100644 rust/helpers/atomic.c create mode 100755 scripts/atomic/gen-rust-atomic-helpers.sh (limited to 'scripts') diff --git a/rust/helpers/atomic.c b/rust/helpers/atomic.c new file mode 100644 index 000000000000..cf06b7ef9a1c --- /dev/null +++ b/rust/helpers/atomic.c @@ -0,0 +1,1040 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Generated by scripts/atomic/gen-rust-atomic-helpers.sh +// DO NOT MODIFY THIS FILE DIRECTLY + +/* + * This file provides helpers for the various atomic functions for Rust. + */ +#ifndef _RUST_ATOMIC_API_H +#define _RUST_ATOMIC_API_H + +#include + +// TODO: Remove this after INLINE_HELPERS support is added. +#ifndef __rust_helper +#define __rust_helper +#endif + +__rust_helper int +rust_helper_atomic_read(const atomic_t *v) +{ + return atomic_read(v); +} + +__rust_helper int +rust_helper_atomic_read_acquire(const atomic_t *v) +{ + return atomic_read_acquire(v); +} + +__rust_helper void +rust_helper_atomic_set(atomic_t *v, int i) +{ + atomic_set(v, i); +} + +__rust_helper void +rust_helper_atomic_set_release(atomic_t *v, int i) +{ + atomic_set_release(v, i); +} + +__rust_helper void +rust_helper_atomic_add(int i, atomic_t *v) +{ + atomic_add(i, v); +} + +__rust_helper int +rust_helper_atomic_add_return(int i, atomic_t *v) +{ + return atomic_add_return(i, v); +} + +__rust_helper int +rust_helper_atomic_add_return_acquire(int i, atomic_t *v) +{ + return atomic_add_return_acquire(i, v); +} + +__rust_helper int +rust_helper_atomic_add_return_release(int i, atomic_t *v) +{ + return atomic_add_return_release(i, v); +} + +__rust_helper int +rust_helper_atomic_add_return_relaxed(int i, atomic_t *v) +{ + return atomic_add_return_relaxed(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_add(int i, atomic_t *v) +{ + return atomic_fetch_add(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_add_acquire(int i, atomic_t *v) +{ + return atomic_fetch_add_acquire(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_add_release(int i, atomic_t *v) +{ + return atomic_fetch_add_release(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_add_relaxed(int i, atomic_t *v) +{ + return atomic_fetch_add_relaxed(i, v); +} + +__rust_helper void +rust_helper_atomic_sub(int i, atomic_t *v) +{ + atomic_sub(i, v); +} + +__rust_helper int +rust_helper_atomic_sub_return(int i, atomic_t *v) +{ + return atomic_sub_return(i, v); +} + +__rust_helper int +rust_helper_atomic_sub_return_acquire(int i, atomic_t *v) +{ + return atomic_sub_return_acquire(i, v); +} + +__rust_helper int +rust_helper_atomic_sub_return_release(int i, atomic_t *v) +{ + return atomic_sub_return_release(i, v); +} + +__rust_helper int +rust_helper_atomic_sub_return_relaxed(int i, atomic_t *v) +{ + return atomic_sub_return_relaxed(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_sub(int i, atomic_t *v) +{ + return atomic_fetch_sub(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_sub_acquire(int i, atomic_t *v) +{ + return atomic_fetch_sub_acquire(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_sub_release(int i, atomic_t *v) +{ + return atomic_fetch_sub_release(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_sub_relaxed(int i, atomic_t *v) +{ + return atomic_fetch_sub_relaxed(i, v); +} + +__rust_helper void +rust_helper_atomic_inc(atomic_t *v) +{ + atomic_inc(v); +} + +__rust_helper int +rust_helper_atomic_inc_return(atomic_t *v) +{ + return atomic_inc_return(v); +} + +__rust_helper int +rust_helper_atomic_inc_return_acquire(atomic_t *v) +{ + return atomic_inc_return_acquire(v); +} + +__rust_helper int +rust_helper_atomic_inc_return_release(atomic_t *v) +{ + return atomic_inc_return_release(v); +} + +__rust_helper int +rust_helper_atomic_inc_return_relaxed(atomic_t *v) +{ + return atomic_inc_return_relaxed(v); +} + +__rust_helper int +rust_helper_atomic_fetch_inc(atomic_t *v) +{ + return atomic_fetch_inc(v); +} + +__rust_helper int +rust_helper_atomic_fetch_inc_acquire(atomic_t *v) +{ + return atomic_fetch_inc_acquire(v); +} + +__rust_helper int +rust_helper_atomic_fetch_inc_release(atomic_t *v) +{ + return atomic_fetch_inc_release(v); +} + +__rust_helper int +rust_helper_atomic_fetch_inc_relaxed(atomic_t *v) +{ + return atomic_fetch_inc_relaxed(v); +} + +__rust_helper void +rust_helper_atomic_dec(atomic_t *v) +{ + atomic_dec(v); +} + +__rust_helper int +rust_helper_atomic_dec_return(atomic_t *v) +{ + return atomic_dec_return(v); +} + +__rust_helper int +rust_helper_atomic_dec_return_acquire(atomic_t *v) +{ + return atomic_dec_return_acquire(v); +} + +__rust_helper int +rust_helper_atomic_dec_return_release(atomic_t *v) +{ + return atomic_dec_return_release(v); +} + +__rust_helper int +rust_helper_atomic_dec_return_relaxed(atomic_t *v) +{ + return atomic_dec_return_relaxed(v); +} + +__rust_helper int +rust_helper_atomic_fetch_dec(atomic_t *v) +{ + return atomic_fetch_dec(v); +} + +__rust_helper int +rust_helper_atomic_fetch_dec_acquire(atomic_t *v) +{ + return atomic_fetch_dec_acquire(v); +} + +__rust_helper int +rust_helper_atomic_fetch_dec_release(atomic_t *v) +{ + return atomic_fetch_dec_release(v); +} + +__rust_helper int +rust_helper_atomic_fetch_dec_relaxed(atomic_t *v) +{ + return atomic_fetch_dec_relaxed(v); +} + +__rust_helper void +rust_helper_atomic_and(int i, atomic_t *v) +{ + atomic_and(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_and(int i, atomic_t *v) +{ + return atomic_fetch_and(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_and_acquire(int i, atomic_t *v) +{ + return atomic_fetch_and_acquire(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_and_release(int i, atomic_t *v) +{ + return atomic_fetch_and_release(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_and_relaxed(int i, atomic_t *v) +{ + return atomic_fetch_and_relaxed(i, v); +} + +__rust_helper void +rust_helper_atomic_andnot(int i, atomic_t *v) +{ + atomic_andnot(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_andnot(int i, atomic_t *v) +{ + return atomic_fetch_andnot(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_andnot_acquire(int i, atomic_t *v) +{ + return atomic_fetch_andnot_acquire(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_andnot_release(int i, atomic_t *v) +{ + return atomic_fetch_andnot_release(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_andnot_relaxed(int i, atomic_t *v) +{ + return atomic_fetch_andnot_relaxed(i, v); +} + +__rust_helper void +rust_helper_atomic_or(int i, atomic_t *v) +{ + atomic_or(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_or(int i, atomic_t *v) +{ + return atomic_fetch_or(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_or_acquire(int i, atomic_t *v) +{ + return atomic_fetch_or_acquire(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_or_release(int i, atomic_t *v) +{ + return atomic_fetch_or_release(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_or_relaxed(int i, atomic_t *v) +{ + return atomic_fetch_or_relaxed(i, v); +} + +__rust_helper void +rust_helper_atomic_xor(int i, atomic_t *v) +{ + atomic_xor(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_xor(int i, atomic_t *v) +{ + return atomic_fetch_xor(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_xor_acquire(int i, atomic_t *v) +{ + return atomic_fetch_xor_acquire(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_xor_release(int i, atomic_t *v) +{ + return atomic_fetch_xor_release(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_xor_relaxed(int i, atomic_t *v) +{ + return atomic_fetch_xor_relaxed(i, v); +} + +__rust_helper int +rust_helper_atomic_xchg(atomic_t *v, int new) +{ + return atomic_xchg(v, new); +} + +__rust_helper int +rust_helper_atomic_xchg_acquire(atomic_t *v, int new) +{ + return atomic_xchg_acquire(v, new); +} + +__rust_helper int +rust_helper_atomic_xchg_release(atomic_t *v, int new) +{ + return atomic_xchg_release(v, new); +} + +__rust_helper int +rust_helper_atomic_xchg_relaxed(atomic_t *v, int new) +{ + return atomic_xchg_relaxed(v, new); +} + +__rust_helper int +rust_helper_atomic_cmpxchg(atomic_t *v, int old, int new) +{ + return atomic_cmpxchg(v, old, new); +} + +__rust_helper int +rust_helper_atomic_cmpxchg_acquire(atomic_t *v, int old, int new) +{ + return atomic_cmpxchg_acquire(v, old, new); +} + +__rust_helper int +rust_helper_atomic_cmpxchg_release(atomic_t *v, int old, int new) +{ + return atomic_cmpxchg_release(v, old, new); +} + +__rust_helper int +rust_helper_atomic_cmpxchg_relaxed(atomic_t *v, int old, int new) +{ + return atomic_cmpxchg_relaxed(v, old, new); +} + +__rust_helper bool +rust_helper_atomic_try_cmpxchg(atomic_t *v, int *old, int new) +{ + return atomic_try_cmpxchg(v, old, new); +} + +__rust_helper bool +rust_helper_atomic_try_cmpxchg_acquire(atomic_t *v, int *old, int new) +{ + return atomic_try_cmpxchg_acquire(v, old, new); +} + +__rust_helper bool +rust_helper_atomic_try_cmpxchg_release(atomic_t *v, int *old, int new) +{ + return atomic_try_cmpxchg_release(v, old, new); +} + +__rust_helper bool +rust_helper_atomic_try_cmpxchg_relaxed(atomic_t *v, int *old, int new) +{ + return atomic_try_cmpxchg_relaxed(v, old, new); +} + +__rust_helper bool +rust_helper_atomic_sub_and_test(int i, atomic_t *v) +{ + return atomic_sub_and_test(i, v); +} + +__rust_helper bool +rust_helper_atomic_dec_and_test(atomic_t *v) +{ + return atomic_dec_and_test(v); +} + +__rust_helper bool +rust_helper_atomic_inc_and_test(atomic_t *v) +{ + return atomic_inc_and_test(v); +} + +__rust_helper bool +rust_helper_atomic_add_negative(int i, atomic_t *v) +{ + return atomic_add_negative(i, v); +} + +__rust_helper bool +rust_helper_atomic_add_negative_acquire(int i, atomic_t *v) +{ + return atomic_add_negative_acquire(i, v); +} + +__rust_helper bool +rust_helper_atomic_add_negative_release(int i, atomic_t *v) +{ + return atomic_add_negative_release(i, v); +} + +__rust_helper bool +rust_helper_atomic_add_negative_relaxed(int i, atomic_t *v) +{ + return atomic_add_negative_relaxed(i, v); +} + +__rust_helper int +rust_helper_atomic_fetch_add_unless(atomic_t *v, int a, int u) +{ + return atomic_fetch_add_unless(v, a, u); +} + +__rust_helper bool +rust_helper_atomic_add_unless(atomic_t *v, int a, int u) +{ + return atomic_add_unless(v, a, u); +} + +__rust_helper bool +rust_helper_atomic_inc_not_zero(atomic_t *v) +{ + return atomic_inc_not_zero(v); +} + +__rust_helper bool +rust_helper_atomic_inc_unless_negative(atomic_t *v) +{ + return atomic_inc_unless_negative(v); +} + +__rust_helper bool +rust_helper_atomic_dec_unless_positive(atomic_t *v) +{ + return atomic_dec_unless_positive(v); +} + +__rust_helper int +rust_helper_atomic_dec_if_positive(atomic_t *v) +{ + return atomic_dec_if_positive(v); +} + +__rust_helper s64 +rust_helper_atomic64_read(const atomic64_t *v) +{ + return atomic64_read(v); +} + +__rust_helper s64 +rust_helper_atomic64_read_acquire(const atomic64_t *v) +{ + return atomic64_read_acquire(v); +} + +__rust_helper void +rust_helper_atomic64_set(atomic64_t *v, s64 i) +{ + atomic64_set(v, i); +} + +__rust_helper void +rust_helper_atomic64_set_release(atomic64_t *v, s64 i) +{ + atomic64_set_release(v, i); +} + +__rust_helper void +rust_helper_atomic64_add(s64 i, atomic64_t *v) +{ + atomic64_add(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_add_return(s64 i, atomic64_t *v) +{ + return atomic64_add_return(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_add_return_acquire(s64 i, atomic64_t *v) +{ + return atomic64_add_return_acquire(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_add_return_release(s64 i, atomic64_t *v) +{ + return atomic64_add_return_release(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_add_return_relaxed(s64 i, atomic64_t *v) +{ + return atomic64_add_return_relaxed(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_add(s64 i, atomic64_t *v) +{ + return atomic64_fetch_add(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_add_acquire(s64 i, atomic64_t *v) +{ + return atomic64_fetch_add_acquire(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_add_release(s64 i, atomic64_t *v) +{ + return atomic64_fetch_add_release(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_add_relaxed(s64 i, atomic64_t *v) +{ + return atomic64_fetch_add_relaxed(i, v); +} + +__rust_helper void +rust_helper_atomic64_sub(s64 i, atomic64_t *v) +{ + atomic64_sub(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_sub_return(s64 i, atomic64_t *v) +{ + return atomic64_sub_return(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_sub_return_acquire(s64 i, atomic64_t *v) +{ + return atomic64_sub_return_acquire(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_sub_return_release(s64 i, atomic64_t *v) +{ + return atomic64_sub_return_release(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_sub_return_relaxed(s64 i, atomic64_t *v) +{ + return atomic64_sub_return_relaxed(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_sub(s64 i, atomic64_t *v) +{ + return atomic64_fetch_sub(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_sub_acquire(s64 i, atomic64_t *v) +{ + return atomic64_fetch_sub_acquire(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_sub_release(s64 i, atomic64_t *v) +{ + return atomic64_fetch_sub_release(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_sub_relaxed(s64 i, atomic64_t *v) +{ + return atomic64_fetch_sub_relaxed(i, v); +} + +__rust_helper void +rust_helper_atomic64_inc(atomic64_t *v) +{ + atomic64_inc(v); +} + +__rust_helper s64 +rust_helper_atomic64_inc_return(atomic64_t *v) +{ + return atomic64_inc_return(v); +} + +__rust_helper s64 +rust_helper_atomic64_inc_return_acquire(atomic64_t *v) +{ + return atomic64_inc_return_acquire(v); +} + +__rust_helper s64 +rust_helper_atomic64_inc_return_release(atomic64_t *v) +{ + return atomic64_inc_return_release(v); +} + +__rust_helper s64 +rust_helper_atomic64_inc_return_relaxed(atomic64_t *v) +{ + return atomic64_inc_return_relaxed(v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_inc(atomic64_t *v) +{ + return atomic64_fetch_inc(v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_inc_acquire(atomic64_t *v) +{ + return atomic64_fetch_inc_acquire(v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_inc_release(atomic64_t *v) +{ + return atomic64_fetch_inc_release(v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_inc_relaxed(atomic64_t *v) +{ + return atomic64_fetch_inc_relaxed(v); +} + +__rust_helper void +rust_helper_atomic64_dec(atomic64_t *v) +{ + atomic64_dec(v); +} + +__rust_helper s64 +rust_helper_atomic64_dec_return(atomic64_t *v) +{ + return atomic64_dec_return(v); +} + +__rust_helper s64 +rust_helper_atomic64_dec_return_acquire(atomic64_t *v) +{ + return atomic64_dec_return_acquire(v); +} + +__rust_helper s64 +rust_helper_atomic64_dec_return_release(atomic64_t *v) +{ + return atomic64_dec_return_release(v); +} + +__rust_helper s64 +rust_helper_atomic64_dec_return_relaxed(atomic64_t *v) +{ + return atomic64_dec_return_relaxed(v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_dec(atomic64_t *v) +{ + return atomic64_fetch_dec(v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_dec_acquire(atomic64_t *v) +{ + return atomic64_fetch_dec_acquire(v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_dec_release(atomic64_t *v) +{ + return atomic64_fetch_dec_release(v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_dec_relaxed(atomic64_t *v) +{ + return atomic64_fetch_dec_relaxed(v); +} + +__rust_helper void +rust_helper_atomic64_and(s64 i, atomic64_t *v) +{ + atomic64_and(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_and(s64 i, atomic64_t *v) +{ + return atomic64_fetch_and(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_and_acquire(s64 i, atomic64_t *v) +{ + return atomic64_fetch_and_acquire(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_and_release(s64 i, atomic64_t *v) +{ + return atomic64_fetch_and_release(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_and_relaxed(s64 i, atomic64_t *v) +{ + return atomic64_fetch_and_relaxed(i, v); +} + +__rust_helper void +rust_helper_atomic64_andnot(s64 i, atomic64_t *v) +{ + atomic64_andnot(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_andnot(s64 i, atomic64_t *v) +{ + return atomic64_fetch_andnot(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_andnot_acquire(s64 i, atomic64_t *v) +{ + return atomic64_fetch_andnot_acquire(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_andnot_release(s64 i, atomic64_t *v) +{ + return atomic64_fetch_andnot_release(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_andnot_relaxed(s64 i, atomic64_t *v) +{ + return atomic64_fetch_andnot_relaxed(i, v); +} + +__rust_helper void +rust_helper_atomic64_or(s64 i, atomic64_t *v) +{ + atomic64_or(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_or(s64 i, atomic64_t *v) +{ + return atomic64_fetch_or(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_or_acquire(s64 i, atomic64_t *v) +{ + return atomic64_fetch_or_acquire(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_or_release(s64 i, atomic64_t *v) +{ + return atomic64_fetch_or_release(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_or_relaxed(s64 i, atomic64_t *v) +{ + return atomic64_fetch_or_relaxed(i, v); +} + +__rust_helper void +rust_helper_atomic64_xor(s64 i, atomic64_t *v) +{ + atomic64_xor(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_xor(s64 i, atomic64_t *v) +{ + return atomic64_fetch_xor(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_xor_acquire(s64 i, atomic64_t *v) +{ + return atomic64_fetch_xor_acquire(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_xor_release(s64 i, atomic64_t *v) +{ + return atomic64_fetch_xor_release(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_xor_relaxed(s64 i, atomic64_t *v) +{ + return atomic64_fetch_xor_relaxed(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_xchg(atomic64_t *v, s64 new) +{ + return atomic64_xchg(v, new); +} + +__rust_helper s64 +rust_helper_atomic64_xchg_acquire(atomic64_t *v, s64 new) +{ + return atomic64_xchg_acquire(v, new); +} + +__rust_helper s64 +rust_helper_atomic64_xchg_release(atomic64_t *v, s64 new) +{ + return atomic64_xchg_release(v, new); +} + +__rust_helper s64 +rust_helper_atomic64_xchg_relaxed(atomic64_t *v, s64 new) +{ + return atomic64_xchg_relaxed(v, new); +} + +__rust_helper s64 +rust_helper_atomic64_cmpxchg(atomic64_t *v, s64 old, s64 new) +{ + return atomic64_cmpxchg(v, old, new); +} + +__rust_helper s64 +rust_helper_atomic64_cmpxchg_acquire(atomic64_t *v, s64 old, s64 new) +{ + return atomic64_cmpxchg_acquire(v, old, new); +} + +__rust_helper s64 +rust_helper_atomic64_cmpxchg_release(atomic64_t *v, s64 old, s64 new) +{ + return atomic64_cmpxchg_release(v, old, new); +} + +__rust_helper s64 +rust_helper_atomic64_cmpxchg_relaxed(atomic64_t *v, s64 old, s64 new) +{ + return atomic64_cmpxchg_relaxed(v, old, new); +} + +__rust_helper bool +rust_helper_atomic64_try_cmpxchg(atomic64_t *v, s64 *old, s64 new) +{ + return atomic64_try_cmpxchg(v, old, new); +} + +__rust_helper bool +rust_helper_atomic64_try_cmpxchg_acquire(atomic64_t *v, s64 *old, s64 new) +{ + return atomic64_try_cmpxchg_acquire(v, old, new); +} + +__rust_helper bool +rust_helper_atomic64_try_cmpxchg_release(atomic64_t *v, s64 *old, s64 new) +{ + return atomic64_try_cmpxchg_release(v, old, new); +} + +__rust_helper bool +rust_helper_atomic64_try_cmpxchg_relaxed(atomic64_t *v, s64 *old, s64 new) +{ + return atomic64_try_cmpxchg_relaxed(v, old, new); +} + +__rust_helper bool +rust_helper_atomic64_sub_and_test(s64 i, atomic64_t *v) +{ + return atomic64_sub_and_test(i, v); +} + +__rust_helper bool +rust_helper_atomic64_dec_and_test(atomic64_t *v) +{ + return atomic64_dec_and_test(v); +} + +__rust_helper bool +rust_helper_atomic64_inc_and_test(atomic64_t *v) +{ + return atomic64_inc_and_test(v); +} + +__rust_helper bool +rust_helper_atomic64_add_negative(s64 i, atomic64_t *v) +{ + return atomic64_add_negative(i, v); +} + +__rust_helper bool +rust_helper_atomic64_add_negative_acquire(s64 i, atomic64_t *v) +{ + return atomic64_add_negative_acquire(i, v); +} + +__rust_helper bool +rust_helper_atomic64_add_negative_release(s64 i, atomic64_t *v) +{ + return atomic64_add_negative_release(i, v); +} + +__rust_helper bool +rust_helper_atomic64_add_negative_relaxed(s64 i, atomic64_t *v) +{ + return atomic64_add_negative_relaxed(i, v); +} + +__rust_helper s64 +rust_helper_atomic64_fetch_add_unless(atomic64_t *v, s64 a, s64 u) +{ + return atomic64_fetch_add_unless(v, a, u); +} + +__rust_helper bool +rust_helper_atomic64_add_unless(atomic64_t *v, s64 a, s64 u) +{ + return atomic64_add_unless(v, a, u); +} + +__rust_helper bool +rust_helper_atomic64_inc_not_zero(atomic64_t *v) +{ + return atomic64_inc_not_zero(v); +} + +__rust_helper bool +rust_helper_atomic64_inc_unless_negative(atomic64_t *v) +{ + return atomic64_inc_unless_negative(v); +} + +__rust_helper bool +rust_helper_atomic64_dec_unless_positive(atomic64_t *v) +{ + return atomic64_dec_unless_positive(v); +} + +__rust_helper s64 +rust_helper_atomic64_dec_if_positive(atomic64_t *v) +{ + return atomic64_dec_if_positive(v); +} + +#endif /* _RUST_ATOMIC_API_H */ +// 615a0e0c98b5973a47fe4fa65e92935051ca00ed diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 7cf7fe95e41d..7053f9245759 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -7,6 +7,7 @@ * Sorted alphabetically. */ +#include "atomic.c" #include "auxiliary.c" #include "blk.c" #include "bug.c" diff --git a/scripts/atomic/gen-atomics.sh b/scripts/atomic/gen-atomics.sh index 5b98a8307693..02508d0d6fe4 100755 --- a/scripts/atomic/gen-atomics.sh +++ b/scripts/atomic/gen-atomics.sh @@ -11,6 +11,7 @@ cat < ${LINUXDIR}/include/${header} diff --git a/scripts/atomic/gen-rust-atomic-helpers.sh b/scripts/atomic/gen-rust-atomic-helpers.sh new file mode 100755 index 000000000000..45b1e100ed7c --- /dev/null +++ b/scripts/atomic/gen-rust-atomic-helpers.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +ATOMICDIR=$(dirname $0) + +. ${ATOMICDIR}/atomic-tbl.sh + +#gen_proto_order_variant(meta, pfx, name, sfx, order, atomic, int, arg...) +gen_proto_order_variant() +{ + local meta="$1"; shift + local pfx="$1"; shift + local name="$1"; shift + local sfx="$1"; shift + local order="$1"; shift + local atomic="$1"; shift + local int="$1"; shift + + local atomicname="${atomic}_${pfx}${name}${sfx}${order}" + + local ret="$(gen_ret_type "${meta}" "${int}")" + local params="$(gen_params "${int}" "${atomic}" "$@")" + local args="$(gen_args "$@")" + local retstmt="$(gen_ret_stmt "${meta}")" + +cat < + +// TODO: Remove this after INLINE_HELPERS support is added. +#ifndef __rust_helper +#define __rust_helper +#endif + +EOF + +grep '^[a-z]' "$1" | while read name meta args; do + gen_proto "${meta}" "${name}" "atomic" "int" ${args} +done + +grep '^[a-z]' "$1" | while read name meta args; do + gen_proto "${meta}" "${name}" "atomic64" "s64" ${args} +done + +cat < Date: Wed, 13 Aug 2025 11:39:51 -0400 Subject: rust: kunit: use `kernel::{fmt,prelude::fmt!}` Reduce coupling to implementation details of the formatting machinery by avoiding direct use for `core`'s formatting traits and macros. Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Signed-off-by: Tamir Duberstein Signed-off-by: Miguel Ojeda --- rust/kernel/kunit.rs | 8 ++++---- scripts/rustdoc_test_gen.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 41efd87595d6..cf58a204b222 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -6,8 +6,8 @@ //! //! Reference: +use crate::fmt; use crate::prelude::*; -use core::fmt; #[cfg(CONFIG_PRINTK)] use crate::c_str; @@ -74,14 +74,14 @@ macro_rules! kunit_assert { // mistake (it is hidden to prevent that). // // This mimics KUnit's failed assertion format. - $crate::kunit::err(format_args!( + $crate::kunit::err($crate::prelude::fmt!( " # {}: ASSERTION FAILED at {FILE}:{LINE}\n", $name )); - $crate::kunit::err(format_args!( + $crate::kunit::err($crate::prelude::fmt!( " Expected {CONDITION} to be true, but is false\n" )); - $crate::kunit::err(format_args!( + $crate::kunit::err($crate::prelude::fmt!( " Failure not reported to KUnit since this is a non-KUnit task\n" )); break 'out; diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs index abb34ada2508..c8f9dc2ab976 100644 --- a/scripts/rustdoc_test_gen.rs +++ b/scripts/rustdoc_test_gen.rs @@ -202,7 +202,7 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{ // This follows the syntax for declaring test metadata in the proposed KTAP v2 spec, which may // be used for the proposed KUnit test attributes API. Thus hopefully this will make migration // easier later on. - ::kernel::kunit::info(format_args!(" # {kunit_name}.location: {real_path}:{line}\n")); + ::kernel::kunit::info(fmt!(" # {kunit_name}.location: {real_path}:{line}\n")); /// The anchor where the test code body starts. #[allow(unused)] -- cgit v1.2.3 From 8b00d6fe96960aaba1b923d4a8c1ddb173c9c1ff Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 4 Sep 2025 13:33:56 -0600 Subject: docs: kdoc: trim __cacheline_group_* with the other annotations The special case for __cacheline_group_begin/end() can be handled by just adding another pattern to the struct_prefixes, eliminating the need for a special case in push_parameter(). One change is that these annotations no longer appear in the rendered output, just like all the other annotations that we clean out. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index a560546c1867..a90f77d6b669 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -90,6 +90,7 @@ struct_prefixes = [ (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '), (KernRe(r'\s*____cacheline_aligned', re.S), ' '), + (KernRe(r'\s*__cacheline_group_(begin|end)\([^\)]+\);'), ''), # # Unwrap struct_group macros based on this definition: # __struct_group(TAG, NAME, ATTRS, MEMBERS...) @@ -447,12 +448,6 @@ class KernelDoc: self.entry.parameterdescs[param] = "anonymous\n" self.entry.anon_struct_union = True - # Handle cache group enforcing variables: they do not need - # to be described in header files - elif "__cacheline_group" in param: - # Ignore __cacheline_group_begin and __cacheline_group_end - return - # Warn if parameter has no description # (but ignore ones starting with # as these are not parameters # but inline preprocessor statements) -- cgit v1.2.3 From e214cca38f1f35d42e63e990c610c96f993343c4 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 4 Sep 2025 14:01:20 -0600 Subject: docs: kdoc: tighten up the push_parameter() no-type case The handling of untyped parameters involved a number of redundant tests; restructure the code to remove them and be more compact. No output changes. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 44 +++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 24 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index a90f77d6b669..2118c20b3056 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -423,30 +423,26 @@ class KernelDoc: param = KernRe(r'[\[\)].*').sub('', param, count=1) - if dtype == "" and param.endswith("..."): - if KernRe(r'\w\.\.\.$').search(param): - # For named variable parameters of the form `x...`, - # remove the dots - param = param[:-3] - else: - # Handles unnamed variable parameters - param = "..." - - if param not in self.entry.parameterdescs or \ - not self.entry.parameterdescs[param]: - - self.entry.parameterdescs[param] = "variable arguments" - - elif dtype == "" and (not param or param == "void"): - param = "void" - self.entry.parameterdescs[param] = "no arguments" - - elif dtype == "" and param in ["struct", "union"]: - # Handle unnamed (anonymous) union or struct - dtype = param - param = "{unnamed_" + param + "}" - self.entry.parameterdescs[param] = "anonymous\n" - self.entry.anon_struct_union = True + # + # Look at various "anonymous type" cases. + # + if dtype == '': + if param.endswith("..."): + if len(param) > 3: # there is a name provided, use that + param = param[:-3] + if not self.entry.parameterdescs.get(param): + self.entry.parameterdescs[param] = "variable arguments" + + elif (not param) or param == "void": + param = "void" + self.entry.parameterdescs[param] = "no arguments" + + elif param in ["struct", "union"]: + # Handle unnamed (anonymous) union or struct + dtype = param + param = "{unnamed_" + param + "}" + self.entry.parameterdescs[param] = "anonymous\n" + self.entry.anon_struct_union = True # Warn if parameter has no description # (but ignore ones starting with # as these are not parameters -- cgit v1.2.3 From f853e83006ab39c3dafe085a488c14bb46906601 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 4 Sep 2025 14:27:00 -0600 Subject: docs: kdoc: remove a single-use variable struct_attribute is only used once, so just put its value there directly and drop the name. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 2118c20b3056..b25c8d80b965 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -76,13 +76,11 @@ doc_begin_func = KernRe(str(doc_com) + # initial " * ' # Here begins a long set of transformations to turn structure member prefixes # and macro invocations into something we can parse and generate kdoc for. # -struct_attribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", - flags=re.I | re.S, cache=False) struct_args_pattern = r'([^,)]+)' struct_prefixes = [ # Strip attributes - (struct_attribute, ' '), + (KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", flags=re.I | re.S, cache=False), ' '), (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), -- cgit v1.2.3 From 4c232a81b0831e7bfa7518968e431d5db29b2cac Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 4 Sep 2025 16:40:49 -0600 Subject: docs: kdoc: move the function transform patterns out of dump_function() Move these definitions to file level, where they are executed once, and don't clutter the function itself. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 78 ++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 43 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index b25c8d80b965..37811cddd55c 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -161,6 +161,37 @@ struct_nested_prefixes = [ (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), ] +# +# Transforms for function prototypes +# +function_xforms = [ + (r"^static +", "", 0), + (r"^extern +", "", 0), + (r"^asmlinkage +", "", 0), + (r"^inline +", "", 0), + (r"^__inline__ +", "", 0), + (r"^__inline +", "", 0), + (r"^__always_inline +", "", 0), + (r"^noinline +", "", 0), + (r"^__FORTIFY_INLINE +", "", 0), + (r"__init +", "", 0), + (r"__init_or_module +", "", 0), + (r"__deprecated +", "", 0), + (r"__flatten +", "", 0), + (r"__meminit +", "", 0), + (r"__must_check +", "", 0), + (r"__weak +", "", 0), + (r"__sched +", "", 0), + (r"_noprof", "", 0), + (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), + (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0), + (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), + (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0), + (r"__attribute_const__ +", "", 0), + (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0), +] + + # # A little helper to get rid of excess white space @@ -894,49 +925,10 @@ class KernelDoc: return_type = '' decl_type = 'function' - # Prefixes that would be removed - sub_prefixes = [ - (r"^static +", "", 0), - (r"^extern +", "", 0), - (r"^asmlinkage +", "", 0), - (r"^inline +", "", 0), - (r"^__inline__ +", "", 0), - (r"^__inline +", "", 0), - (r"^__always_inline +", "", 0), - (r"^noinline +", "", 0), - (r"^__FORTIFY_INLINE +", "", 0), - (r"__init +", "", 0), - (r"__init_or_module +", "", 0), - (r"__deprecated +", "", 0), - (r"__flatten +", "", 0), - (r"__meminit +", "", 0), - (r"__must_check +", "", 0), - (r"__weak +", "", 0), - (r"__sched +", "", 0), - (r"_noprof", "", 0), - (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), - (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0), - (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), - (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0), - (r"__attribute_const__ +", "", 0), - - # It seems that Python support for re.X is broken: - # At least for me (Python 3.13), this didn't work -# (r""" -# __attribute__\s*\(\( -# (?: -# [\w\s]+ # attribute name -# (?:\([^)]*\))? # attribute arguments -# \s*,? # optional comma at the end -# )+ -# \)\)\s+ -# """, "", re.X), - - # So, remove whitespaces and comments from it - (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0), - ] - - for search, sub, flags in sub_prefixes: + # + # Apply the initial transformations. + # + for search, sub, flags in function_xforms: prototype = KernRe(search, flags).sub(sub, prototype) # Macros are a special case, as they change the prototype format -- cgit v1.2.3 From a2752f8c631201e189f501fc4d320354efa3e72e Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 4 Sep 2025 16:49:52 -0600 Subject: doc: kdoc: unify transform handling Both functions and structs are passed through a set of regex-based transforms, but the two were structured differently, despite being the same thing. Create a utility function to apply transformations and use it in both cases. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 65 +++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 31 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 37811cddd55c..1a1558211acd 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -78,7 +78,7 @@ doc_begin_func = KernRe(str(doc_com) + # initial " * ' # struct_args_pattern = r'([^,)]+)' -struct_prefixes = [ +struct_xforms = [ # Strip attributes (KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", flags=re.I | re.S, cache=False), ' '), (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), @@ -165,33 +165,39 @@ struct_nested_prefixes = [ # Transforms for function prototypes # function_xforms = [ - (r"^static +", "", 0), - (r"^extern +", "", 0), - (r"^asmlinkage +", "", 0), - (r"^inline +", "", 0), - (r"^__inline__ +", "", 0), - (r"^__inline +", "", 0), - (r"^__always_inline +", "", 0), - (r"^noinline +", "", 0), - (r"^__FORTIFY_INLINE +", "", 0), - (r"__init +", "", 0), - (r"__init_or_module +", "", 0), - (r"__deprecated +", "", 0), - (r"__flatten +", "", 0), - (r"__meminit +", "", 0), - (r"__must_check +", "", 0), - (r"__weak +", "", 0), - (r"__sched +", "", 0), - (r"_noprof", "", 0), - (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), - (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0), - (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), - (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0), - (r"__attribute_const__ +", "", 0), - (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0), + (KernRe(r"^static +"), ""), + (KernRe(r"^extern +"), ""), + (KernRe(r"^asmlinkage +"), ""), + (KernRe(r"^inline +"), ""), + (KernRe(r"^__inline__ +"), ""), + (KernRe(r"^__inline +"), ""), + (KernRe(r"^__always_inline +"), ""), + (KernRe(r"^noinline +"), ""), + (KernRe(r"^__FORTIFY_INLINE +"), ""), + (KernRe(r"__init +"), ""), + (KernRe(r"__init_or_module +"), ""), + (KernRe(r"__deprecated +"), ""), + (KernRe(r"__flatten +"), ""), + (KernRe(r"__meminit +"), ""), + (KernRe(r"__must_check +"), ""), + (KernRe(r"__weak +"), ""), + (KernRe(r"__sched +"), ""), + (KernRe(r"_noprof"), ""), + (KernRe(r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +"), ""), + (KernRe(r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +"), ""), + (KernRe(r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +"), ""), + (KernRe(r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)"), r"\1, \2"), + (KernRe(r"__attribute_const__ +"), ""), + (KernRe(r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+"), ""), ] - +# +# Apply a set of transforms to a block of text. +# +def apply_transforms(xforms, text): + for search, subst in xforms: + text = search.sub(subst, text) + return text # # A little helper to get rid of excess white space @@ -807,8 +813,7 @@ class KernelDoc: # Go through the list of members applying all of our transformations. # members = trim_private_members(members) - for search, sub in struct_prefixes: - members = search.sub(sub, members) + members = apply_transforms(struct_xforms, members) nested = NestedMatch() for search, sub in struct_nested_prefixes: @@ -924,12 +929,10 @@ class KernelDoc: func_macro = False return_type = '' decl_type = 'function' - # # Apply the initial transformations. # - for search, sub, flags in function_xforms: - prototype = KernRe(search, flags).sub(sub, prototype) + prototype = apply_transforms(function_xforms, prototype) # Macros are a special case, as they change the prototype format new_proto = KernRe(r"^#\s*define\s+").sub("", prototype) -- cgit v1.2.3 From fee63c8f10c2fe77f618f9955c2f5521ff9cc622 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 5 Sep 2025 15:53:05 -0600 Subject: docs: kdoc: remove a couple of spurious regex characters The "name" regex in dump_function() includes both the tilde and colon characters, but neither has any place in function prototypes. Remove the characters, after which the regex simplifies to "\w+" No output changes. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 1a1558211acd..decd127df82e 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -958,7 +958,7 @@ class KernelDoc: # - atomic_set (macro) # - pci_match_device, __copy_to_user (long return type) - name = r'[a-zA-Z0-9_~:]+' + name = r'\w+' prototype_end1 = r'[^\(]*' prototype_end2 = r'[^\{]*' prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)' -- cgit v1.2.3 From 08b5228cf455d46a23bfb341766563d1a48e3c8f Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 5 Sep 2025 16:30:48 -0600 Subject: docs: kdoc: remove a useless empty capture group The is_define_proto case in dump_function() uses a regex with an empty capture group - () - that has no use; just take it out. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index decd127df82e..f9be5414244d 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -972,11 +972,11 @@ class KernelDoc: found = False if is_define_proto: - r = KernRe(r'^()(' + name + r')\s+') + r = KernRe(r'^(' + name + r')\s+') if r.search(prototype): return_type = '' - declaration_name = r.group(2) + declaration_name = r.group(1) func_macro = True found = True -- cgit v1.2.3 From ff1f2af341b72bd5b6b5d432da55faf2f6d24cfe Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 8 Sep 2025 13:22:42 -0600 Subject: docs: kdoc: Simplify the dump_function() prototype regexes The regexes for the parsing of function prototypes were more complicated than they needed to be and difficult to understand -- at least, I spent a fair amount of time bashing my head against them. Simplify them, and add some documentation comments as well. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index f9be5414244d..ec2e6e83df05 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -959,15 +959,15 @@ class KernelDoc: # - pci_match_device, __copy_to_user (long return type) name = r'\w+' - prototype_end1 = r'[^\(]*' - prototype_end2 = r'[^\{]*' - prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)' - - # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing group. - # So, this needs to be mapped in Python with (?:...)? or (?:...)+ - type1 = r'(?:[\w\s]+)?' type2 = r'(?:[\w\s]+\*+)+' + # + # Attempt to match first on (args) with no internal parentheses; this + # lets us easily filter out __acquires() and other post-args stuff. If + # that fails, just grab the rest of the line to the last closing + # parenthesis. + # + proto_args = r'\(([^\(]*|.*)\)' found = False @@ -983,9 +983,9 @@ class KernelDoc: if not found: patterns = [ - rf'^()({name})\s*{prototype_end}', - rf'^({type1})\s+({name})\s*{prototype_end}', - rf'^({type2})\s*({name})\s*{prototype_end}', + rf'^()({name})\s*{proto_args}', + rf'^({type1})\s+({name})\s*{proto_args}', + rf'^({type2})\s*({name})\s*{proto_args}', ] for p in patterns: -- cgit v1.2.3 From 370f430527ecd35938ad94167e45fc784f6e4d95 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 8 Sep 2025 16:01:15 -0600 Subject: docs: kdoc: consolidate some of the macro-processing logic The logic to handle macros is split in dump_function(); bring it all together into a single place and add a comment saying what's going on. Remove the unneeded is_define_proto variable, and tighten up the code a bit. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 43 +++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 23 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index ec2e6e83df05..27329ce9b5e9 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -926,21 +926,31 @@ class KernelDoc: Stores a function of function macro inside self.entries array. """ - func_macro = False + found = func_macro = False return_type = '' decl_type = 'function' # # Apply the initial transformations. # prototype = apply_transforms(function_xforms, prototype) - - # Macros are a special case, as they change the prototype format + # + # If we have a macro, remove the "#define" at the front. + # new_proto = KernRe(r"^#\s*define\s+").sub("", prototype) if new_proto != prototype: - is_define_proto = True prototype = new_proto - else: - is_define_proto = False + # + # Dispense with the simple "#define A B" case here; the key + # is the space after the name of the symbol being defined. + # NOTE that the seemingly misnamed "func_macro" indicates a + # macro *without* arguments. + # + r = KernRe(r'^(\w+)\s+') + if r.search(prototype): + return_type = '' + declaration_name = r.group(1) + func_macro = True + found = True # Yes, this truly is vile. We are looking for: # 1. Return type (may be nothing if we're looking at a macro) @@ -968,19 +978,10 @@ class KernelDoc: # parenthesis. # proto_args = r'\(([^\(]*|.*)\)' - - found = False - - if is_define_proto: - r = KernRe(r'^(' + name + r')\s+') - - if r.search(prototype): - return_type = '' - declaration_name = r.group(1) - func_macro = True - - found = True - + # + # (Except for the simple macro case) attempt to split up the prototype + # in the various ways we understand. + # if not found: patterns = [ rf'^()({name})\s*{proto_args}', @@ -990,16 +991,12 @@ class KernelDoc: for p in patterns: r = KernRe(p) - if r.match(prototype): - return_type = r.group(1) declaration_name = r.group(2) args = r.group(3) - self.create_parameter_list(ln, decl_type, args, ',', declaration_name) - found = True break if not found: -- cgit v1.2.3 From 3dff54410e56ddee2dad8824ea77e60cd5b16d5b Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 8 Sep 2025 16:21:28 -0600 Subject: docs: kdoc: final dump_function() cleanups Add some more comments to dump_function(), add some comments, and trim out an unneeded duplicate output_declaration() call. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 27329ce9b5e9..5e41acfef7b8 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -999,32 +999,28 @@ class KernelDoc: declaration_name) found = True break + # + # Parsing done; make sure that things are as we expect. + # if not found: self.emit_msg(ln, f"cannot understand function prototype: '{prototype}'") return - if self.entry.identifier != declaration_name: - self.emit_msg(ln, - f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead") + self.emit_msg(ln, f"expecting prototype for {self.entry.identifier}(). " + f"Prototype was for {declaration_name}() instead") return - self.check_sections(ln, declaration_name, "function") - self.check_return_section(ln, declaration_name, return_type) + # + # Store the result. + # + self.output_declaration(decl_type, declaration_name, + typedef=('typedef' in return_type), + functiontype=return_type, + purpose=self.entry.declaration_purpose, + func_macro=func_macro) - if 'typedef' in return_type: - self.output_declaration(decl_type, declaration_name, - typedef=True, - functiontype=return_type, - purpose=self.entry.declaration_purpose, - func_macro=func_macro) - else: - self.output_declaration(decl_type, declaration_name, - typedef=False, - functiontype=return_type, - purpose=self.entry.declaration_purpose, - func_macro=func_macro) def dump_typedef(self, ln, proto): """ -- cgit v1.2.3 From 999a642d7e7d4241cc7dba942a13c67d0685284b Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 8 Sep 2025 16:32:10 -0600 Subject: docs: kdoc: remove some dead code in dump_typedef() The regex in this block of code makes no sense, and a quick test shows that it never matches anything; simply delete the code. No output changes. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 5 ----- 1 file changed, 5 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 5e41acfef7b8..7c739b495d58 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1060,11 +1060,6 @@ class KernelDoc: purpose=self.entry.declaration_purpose) return - # Handle nested parentheses or brackets - r = KernRe(r'(\(*.\)\s*|\[*.\]\s*);$') - while r.search(proto): - proto = r.sub('', proto) - # Parse simple typedefs r = KernRe(r'typedef.*\s+(\w+)\s*;') if r.match(proto): -- cgit v1.2.3 From 00fa9bc4e93cb336575a5f2c1da90d2443ff14c8 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 8 Sep 2025 16:49:58 -0600 Subject: docs: kdoc: remove redundant comment stripping in dump_typedef() By the time we get here, comments have long since been stripped out; there is no need to do it again. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 7c739b495d58..ad9df0536bbf 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1034,9 +1034,6 @@ class KernelDoc: typedef1 = KernRe(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args) typedef2 = KernRe(r'typedef' + typedef_type + typedef_ident + typedef_args) - # Strip comments - proto = KernRe(r'/\*.*?\*/', flags=re.S).sub('', proto) - # Parse function typedef prototypes for r in [typedef1, typedef2]: if not r.match(proto): -- cgit v1.2.3 From c01878437739f86851da76235f394346c6bd8ce3 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 8 Sep 2025 17:12:14 -0600 Subject: docs: kdoc: a few more dump_typedef() tweaks Merge "typedef" into the typedef_type pattern rather than repeating it later, and add some comments. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index ad9df0536bbf..2376f180b1fa 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1026,13 +1026,15 @@ class KernelDoc: """ Stores a typedef inside self.entries array. """ - - typedef_type = r'((?:\s+[\w*]+\b){0,7}\s+(?:\w+\b|\*+))\s*' + # + # We start by looking for function typedefs. + # + typedef_type = r'typedef((?:\s+[\w*]+\b){0,7}\s+(?:\w+\b|\*+))\s*' typedef_ident = r'\*?\s*(\w\S+)\s*' typedef_args = r'\s*\((.*)\);' - typedef1 = KernRe(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args) - typedef2 = KernRe(r'typedef' + typedef_type + typedef_ident + typedef_args) + typedef1 = KernRe(typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args) + typedef2 = KernRe(typedef_type + typedef_ident + typedef_args) # Parse function typedef prototypes for r in [typedef1, typedef2]: @@ -1048,16 +1050,16 @@ class KernelDoc: f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") return - decl_type = 'function' - self.create_parameter_list(ln, decl_type, args, ',', declaration_name) + self.create_parameter_list(ln, 'function', args, ',', declaration_name) - self.output_declaration(decl_type, declaration_name, + self.output_declaration('function', declaration_name, typedef=True, functiontype=return_type, purpose=self.entry.declaration_purpose) return - - # Parse simple typedefs + # + # Not a function, try to parse a simple typedef. + # r = KernRe(r'typedef.*\s+(\w+)\s*;') if r.match(proto): declaration_name = r.group(1) -- cgit v1.2.3 From d322f6a24ee5964a58294f61bf96a1b6404c676d Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 8 Sep 2025 17:41:57 +0200 Subject: scripts/decode_stacktrace.sh: symbol: avoid trailing whitespaces A few patches slightly improving the output generated by decode_stacktrace.sh. This patch (of 3): Lines having a symbol to decode might not always have info after this symbol. It means ${info_str} might not be set, but it will always be printed after a space, causing trailing whitespaces. That's a detail, but when the output is opened with an editor marking these trailing whitespaces, that's a bit disturbing. It is easy to remove them by printing this variable with a space only if it is set. While at it, do the same with ${module} and print everything in one line. Link: https://lkml.kernel.org/r/20250908-decode_strace_indent-v1-0-28e5e4758080@kernel.org Link: https://lkml.kernel.org/r/20250908-decode_strace_indent-v1-1-28e5e4758080@kernel.org Signed-off-by: Matthieu Baerts (NGI0) Reviewed-by: Carlos Llamas Reviewed-by: Breno Leitao Reviewed-by: Luca Ceresoli Cc: Carlos Llamas Cc: Elliot Berman Cc: Stephen Boyd Signed-off-by: Andrew Morton --- scripts/decode_stacktrace.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh index 17abc4e7a985..c6b5c14412f0 100755 --- a/scripts/decode_stacktrace.sh +++ b/scripts/decode_stacktrace.sh @@ -323,12 +323,7 @@ handle_line() { parse_symbol # modifies $symbol # Add up the line number to the symbol - if [[ -z ${module} ]] - then - echo "${words[@]}" "$symbol ${info_str}" - else - echo "${words[@]}" "$symbol $module ${info_str}" - fi + echo "${words[@]}" "${symbol}${module:+ ${module}}${info_str:+ ${info_str}}" } while read line; do -- cgit v1.2.3 From 4a2fc4897b5e0ca1e7a3cb4e32f44c7db3367dee Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 8 Sep 2025 17:41:58 +0200 Subject: scripts/decode_stacktrace.sh: symbol: preserve alignment With lines having a symbol to decode, the script was only trying to preserve the alignment for the timestamps, but not the rest, nor when the caller was set (CONFIG_PRINTK_CALLER=y). With this sample ... [ 52.080924] Call Trace: [ 52.080926] [ 52.080931] dump_stack_lvl+0x6f/0xb0 ... the script was producing the following output: [ 52.080924] Call Trace: [ 52.080926] [ 52.080931] dump_stack_lvl (arch/x86/include/asm/irqflags.h:19) (dump_stack_lvl is no longer aligned with : one missing space) With this other sample ... [ 52.080924][ T48] Call Trace: [ 52.080926][ T48] [ 52.080931][ T48] dump_stack_lvl+0x6f/0xb0 ... the script was producing the following output: [ 52.080924][ T48] Call Trace: [ 52.080926][ T48] [ 52.080931][ T48] dump_stack_lvl (arch/x86/include/asm/irqflags.h:19) (the misalignment is clearer here) That's because the script had a workaround for CONFIG_PRINTK_TIME=y only, see the previous comment called "Format timestamps with tabs". To always preserve spaces, they need to be recorded along the words. That is what is now done with the new 'spaces' array. Some notes: - 'extglob' is needed only for this operation, and that's why it is set in a dedicated subshell. - 'read' is used with '-r' not to treat a character in any special way, e.g. when followed by a space. - When a word is removed from the 'words' array, the corresponding space needs to be removed from the 'spaces' array as well. With the last sample, we now have: [ 52.080924][ T48] Call Trace: [ 52.080926][ T48] [ 52.080931][ T48] dump_stack_lvl (arch/x86/include/asm/irqflags.h:19) (the alignment is preserved) Link: https://lkml.kernel.org/r/20250908-decode_strace_indent-v1-2-28e5e4758080@kernel.org Signed-off-by: Matthieu Baerts (NGI0) Tested-by: Carlos Llamas Cc: Breno Leitao Cc: Elliot Berman Cc: Luca Ceresoli Cc: Stephen Boyd Signed-off-by: Andrew Morton --- scripts/decode_stacktrace.sh | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh index c6b5c14412f0..0c92d6a7f777 100755 --- a/scripts/decode_stacktrace.sh +++ b/scripts/decode_stacktrace.sh @@ -255,10 +255,11 @@ handle_line() { basepath=${basepath%/init/main.c:*)} fi - local words + local words spaces - # Tokenize - read -a words <<<"$1" + # Tokenize: words and spaces to preserve the alignment + read -ra words <<<"$1" + IFS='#' read -ra spaces <<<"$(shopt -s extglob; echo "${1//+([^[:space:]])/#}")" # Remove hex numbers. Do it ourselves until it happens in the # kernel @@ -270,19 +271,13 @@ handle_line() { for i in "${!words[@]}"; do # Remove the address if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then - unset words[$i] - fi - - # Format timestamps with tabs - if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then - unset words[$i] - words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}") + unset words[$i] spaces[$i] fi done if [[ ${words[$last]} =~ ^[0-9a-f]+\] ]]; then words[$last-1]="${words[$last-1]} ${words[$last]}" - unset words[$last] + unset words[$last] spaces[$last] last=$(( $last - 1 )) fi @@ -294,7 +289,7 @@ handle_line() { local info_str="" if [[ ${words[$last]} =~ \([A-Z]*\) ]]; then info_str=${words[$last]} - unset words[$last] + unset words[$last] spaces[$last] last=$(( $last - 1 )) fi @@ -311,7 +306,7 @@ handle_line() { modbuildid= fi symbol=${words[$last-1]} - unset words[$last-1] + unset words[$last-1] spaces[$last-1] else # The symbol is the last element, process it symbol=${words[$last]} @@ -323,7 +318,10 @@ handle_line() { parse_symbol # modifies $symbol # Add up the line number to the symbol - echo "${words[@]}" "${symbol}${module:+ ${module}}${info_str:+ ${info_str}}" + for i in "${!words[@]}"; do + echo -n "${spaces[i]}${words[i]}" + done + echo "${spaces[$last]}${symbol}${module:+ ${module}}${info_str:+ ${info_str}}" } while read line; do -- cgit v1.2.3 From e1831e8dd1c839088692c09c97031ce467db1a2a Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 8 Sep 2025 17:41:59 +0200 Subject: scripts/decode_stacktrace.sh: code: preserve alignment With lines having a code to decode, the alignment was not preserved for the first line. With this sample ... [ 52.238089][ T55] RIP: 0010:__ip_queue_xmit+0x127c/0x1820 [ 52.238401][ T55] Code: c1 83 e0 07 48 c1 e9 03 83 c0 03 (...) ... the script was producing the following output: [ 52.238089][ T55] RIP: 0010:__ip_queue_xmit (...) [ 52.238401][ T55] Code: c1 83 e0 07 48 c1 e9 03 83 c0 03 (...) That's because scripts/decodecode doesn't preserve the alignment. No need to modify it, it is enough to give only the "Code: (...)" part to this script, and print the prefix without modifications. With the same sample, we now have: [ 52.238089][ T55] RIP: 0010:__ip_queue_xmit (...) [ 52.238401][ T55] Code: c1 83 e0 07 48 c1 e9 03 83 c0 03 (...) Link: https://lkml.kernel.org/r/20250908-decode_strace_indent-v1-3-28e5e4758080@kernel.org Signed-off-by: Matthieu Baerts (NGI0) Tested-by: Carlos Llamas Cc: Breno Leitao Cc: Elliot Berman Cc: Luca Ceresoli Cc: Stephen Boyd Signed-off-by: Andrew Morton --- scripts/decode_stacktrace.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh index 0c92d6a7f777..c73cb802a0a3 100755 --- a/scripts/decode_stacktrace.sh +++ b/scripts/decode_stacktrace.sh @@ -242,8 +242,10 @@ debuginfod_get_vmlinux() { decode_code() { local scripts=`dirname "${BASH_SOURCE[0]}"` + local lim="Code: " - echo "$1" | $scripts/decodecode + echo -n "${1%%${lim}*}" + echo "${lim}${1##*${lim}}" | $scripts/decodecode } handle_line() { -- cgit v1.2.3 From 347b564599fb01d8ae1e9f473b82820fea4c2767 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 11 Sep 2025 21:33:55 +0200 Subject: coccinelle: of_table: handle SPI device ID tables 'struct spi_device_id' tables also need to be NULL terminated. Link: https://lkml.kernel.org/r/20250911193354.56262-2-krzysztof.kozlowski@linaro.org Signed-off-by: Krzysztof Kozlowski Cc: Julia Lawall Cc: Nathan Chancellor Cc: Nicolas Palix Signed-off-by: Andrew Morton --- scripts/coccinelle/misc/of_table.cocci | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/coccinelle/misc/of_table.cocci b/scripts/coccinelle/misc/of_table.cocci index 4693ea744753..17881cb0884b 100644 --- a/scripts/coccinelle/misc/of_table.cocci +++ b/scripts/coccinelle/misc/of_table.cocci @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/// Make sure (of/i2c/platform)_device_id tables are NULL terminated +/// Make sure (of/i2c/platform/spi)_device_id tables are NULL terminated // // Keywords: of_table i2c_table platform_table // Confidence: Medium @@ -15,14 +15,14 @@ identifier var, arr; expression E; @@ ( -struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { ..., { .var = E, * } }; | -struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { ..., * { ..., E, ... }, }; @@ -33,7 +33,7 @@ identifier var, arr; expression E; @@ ( -struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { ..., { .var = E, @@ -42,7 +42,7 @@ struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { + { } }; | -struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { ..., { ..., E, ... }, + { }, @@ -55,7 +55,7 @@ identifier var, arr; expression E; @@ ( -struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { ..., { .var = E, @@ -63,7 +63,7 @@ struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { @p1 }; | -struct \(of_device_id \| i2c_device_id \| platform_device_id\) arr[] = { +struct \(of_device_id \| i2c_device_id \| platform_device_id \| spi_device_id\) arr[] = { ..., { ..., E, ... } @p1 -- cgit v1.2.3 From f23e76a32dbfc45418a1448f94886eb608b35b98 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 11 Sep 2025 20:47:27 +0200 Subject: coccinelle: platform_no_drv_owner: handle also built-in drivers builtin_platform_driver() and others also use macro platform_driver_register() which sets the .owner=THIS_MODULE, so extend the cocci script to detect these as well. Link: https://lkml.kernel.org/r/20250911184726.23154-2-krzysztof.kozlowski@linaro.org Signed-off-by: Krzysztof Kozlowski Cc: Julia Lawall Cc: Nathan Chancellor Cc: Nicolas Palix Signed-off-by: Andrew Morton --- scripts/coccinelle/api/platform_no_drv_owner.cocci | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'scripts') diff --git a/scripts/coccinelle/api/platform_no_drv_owner.cocci b/scripts/coccinelle/api/platform_no_drv_owner.cocci index 8fa050eeb7e5..5e869858bda8 100644 --- a/scripts/coccinelle/api/platform_no_drv_owner.cocci +++ b/scripts/coccinelle/api/platform_no_drv_owner.cocci @@ -10,12 +10,21 @@ virtual org virtual report @match1@ +declarer name builtin_i2c_driver; +declarer name builtin_platform_driver; +declarer name builtin_platform_driver_probe; declarer name module_i2c_driver; declarer name module_platform_driver; declarer name module_platform_driver_probe; identifier __driver; @@ ( + builtin_i2c_driver(__driver); +| + builtin_platform_driver(__driver); +| + builtin_platform_driver_probe(__driver, ...); +| module_i2c_driver(__driver); | module_platform_driver(__driver); -- cgit v1.2.3 From a40282dd3c484e6c882e93f4680e0a3ef3814453 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sat, 20 Sep 2025 16:45:23 -0700 Subject: gcc-plugins: Remove TODO_verify_il for GCC >= 16 GCC now runs TODO_verify_il automatically[1], so it is no longer exposed to plugins. Only use the flag on GCC < 16. Link: https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=9739ae9384dd7cd3bb1c7683d6b80b7a9116eaf8 [1] Suggested-by: Christopher Fore Link: https://lore.kernel.org/r/20250920234519.work.915-kees@kernel.org Signed-off-by: Kees Cook --- scripts/gcc-plugins/gcc-common.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'scripts') diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h index 6cb6d1051815..8f1b3500f8e2 100644 --- a/scripts/gcc-plugins/gcc-common.h +++ b/scripts/gcc-plugins/gcc-common.h @@ -173,10 +173,17 @@ static inline opt_pass *get_pass_for_id(int id) return g->get_passes()->get_pass_for_id(id); } +#if BUILDING_GCC_VERSION < 16000 #define TODO_verify_ssa TODO_verify_il #define TODO_verify_flow TODO_verify_il #define TODO_verify_stmts TODO_verify_il #define TODO_verify_rtl_sharing TODO_verify_il +#else +#define TODO_verify_ssa 0 +#define TODO_verify_flow 0 +#define TODO_verify_stmts 0 +#define TODO_verify_rtl_sharing 0 +#endif #define INSN_DELETED_P(insn) (insn)->deleted() -- cgit v1.2.3 From 0ce5139fd96e9d415d3faaef1c575e238f9bbd67 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 18 Sep 2025 10:05:46 +0200 Subject: kbuild: always create intermediate vmlinux.unstripped Generate the intermediate vmlinux.unstripped regardless of CONFIG_ARCH_VMLINUX_NEEDS_RELOCS. If CONFIG_ARCH_VMLINUX_NEEDS_RELOCS is unset, vmlinux.unstripped and vmlinux are identiacal. This simplifies the build rule, and allows to strip more sections by adding them to remove-section-y. Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier Link: https://patch.msgid.link/a48ca543fa2305bd17324f41606dcaed9b19f2d4.1758182101.git.legion@kernel.org Signed-off-by: Nathan Chancellor --- scripts/Makefile.vmlinux | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux index b64862dc6f08..4f2d4c3fb737 100644 --- a/scripts/Makefile.vmlinux +++ b/scripts/Makefile.vmlinux @@ -9,20 +9,6 @@ include $(srctree)/scripts/Makefile.lib targets := -ifdef CONFIG_ARCH_VMLINUX_NEEDS_RELOCS -vmlinux-final := vmlinux.unstripped - -quiet_cmd_strip_relocs = RSTRIP $@ - cmd_strip_relocs = $(OBJCOPY) --remove-section='.rel*' --remove-section=!'.rel*.dyn' $< $@ - -vmlinux: $(vmlinux-final) FORCE - $(call if_changed,strip_relocs) - -targets += vmlinux -else -vmlinux-final := vmlinux -endif - %.o: %.c FORCE $(call if_changed_rule,cc_o_c) @@ -61,19 +47,19 @@ targets += .builtin-dtbs-list ifdef CONFIG_GENERIC_BUILTIN_DTB targets += .builtin-dtbs.S .builtin-dtbs.o -$(vmlinux-final): .builtin-dtbs.o +vmlinux.unstripped: .builtin-dtbs.o endif -# vmlinux +# vmlinux.unstripped # --------------------------------------------------------------------------- ifdef CONFIG_MODULES targets += .vmlinux.export.o -$(vmlinux-final): .vmlinux.export.o +vmlinux.unstripped: .vmlinux.export.o endif ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX -$(vmlinux-final): arch/$(SRCARCH)/tools/vmlinux.arch.o +vmlinux.unstripped: arch/$(SRCARCH)/tools/vmlinux.arch.o arch/$(SRCARCH)/tools/vmlinux.arch.o: vmlinux.o FORCE $(Q)$(MAKE) $(build)=arch/$(SRCARCH)/tools $@ @@ -86,17 +72,30 @@ cmd_link_vmlinux = \ $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)" "$@"; \ $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) -targets += $(vmlinux-final) -$(vmlinux-final): scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE +targets += vmlinux.unstripped +vmlinux.unstripped: scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE +$(call if_changed_dep,link_vmlinux) ifdef CONFIG_DEBUG_INFO_BTF -$(vmlinux-final): $(RESOLVE_BTFIDS) +vmlinux.unstripped: $(RESOLVE_BTFIDS) endif ifdef CONFIG_BUILDTIME_TABLE_SORT -$(vmlinux-final): scripts/sorttable +vmlinux.unstripped: scripts/sorttable endif +# vmlinux +# --------------------------------------------------------------------------- + +remove-section-y := +remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel*' + +quiet_cmd_strip_relocs = OBJCOPY $@ + cmd_strip_relocs = $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $< $@ + +targets += vmlinux +vmlinux: vmlinux.unstripped FORCE + $(call if_changed,strip_relocs) + # modules.builtin.ranges # --------------------------------------------------------------------------- ifdef CONFIG_BUILTIN_MODULE_RANGES @@ -110,7 +109,7 @@ modules.builtin.ranges: $(srctree)/scripts/generate_builtin_ranges.awk \ modules.builtin vmlinux.map vmlinux.o.map FORCE $(call if_changed,modules_builtin_ranges) -vmlinux.map: $(vmlinux-final) +vmlinux.map: vmlinux.unstripped @: endif -- cgit v1.2.3 From 3e86e4d74c0490e5fc5a7f8de8f29e7579c9ffe5 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 18 Sep 2025 10:05:47 +0200 Subject: kbuild: keep .modinfo section in vmlinux.unstripped Keep the .modinfo section during linking, but strip it from the final vmlinux. Adjust scripts/mksysmap to exclude modinfo symbols from kallsyms. This change will allow the next commit to extract the .modinfo section from the vmlinux.unstripped intermediate. Signed-off-by: Masahiro Yamada Signed-off-by: Alexey Gladkov Reviewed-by: Nicolas Schier Link: https://patch.msgid.link/aaf67c07447215463300fccaa758904bac42f992.1758182101.git.legion@kernel.org Signed-off-by: Nathan Chancellor --- include/asm-generic/vmlinux.lds.h | 2 +- scripts/Makefile.vmlinux | 7 +++++-- scripts/mksysmap | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ae2d2359b79e..cfa63860dfd4 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -831,6 +831,7 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG) /* Required sections not related to debugging. */ #define ELF_DETAILS \ + .modinfo : { *(.modinfo) } \ .comment 0 : { *(.comment) } \ .symtab 0 : { *(.symtab) } \ .strtab 0 : { *(.strtab) } \ @@ -1044,7 +1045,6 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG) *(.discard.*) \ *(.export_symbol) \ *(.no_trim_symbol) \ - *(.modinfo) \ /* ld.bfd warns about .gnu.version* even when not emitted */ \ *(.gnu.version*) \ diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux index 4f2d4c3fb737..70856dab0f54 100644 --- a/scripts/Makefile.vmlinux +++ b/scripts/Makefile.vmlinux @@ -86,11 +86,14 @@ endif # vmlinux # --------------------------------------------------------------------------- -remove-section-y := +remove-section-y := .modinfo remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel*' +# To avoid warnings: "empty loadable segment detected at ..." from GNU objcopy, +# it is necessary to remove the PT_LOAD flag from the segment. quiet_cmd_strip_relocs = OBJCOPY $@ - cmd_strip_relocs = $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $< $@ + cmd_strip_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \ + $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $@ targets += vmlinux vmlinux: vmlinux.unstripped FORCE diff --git a/scripts/mksysmap b/scripts/mksysmap index 3accbdb269ac..a607a0059d11 100755 --- a/scripts/mksysmap +++ b/scripts/mksysmap @@ -79,6 +79,9 @@ / _SDA_BASE_$/d / _SDA2_BASE_$/d +# MODULE_INFO() +/ __UNIQUE_ID_modinfo[0-9]*$/d + # --------------------------------------------------------------------------- # Ignored patterns # (symbols that contain the pattern are ignored) -- cgit v1.2.3 From 39cfd5b12160be4f57df1c3ba60139741c827616 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 18 Sep 2025 10:05:48 +0200 Subject: kbuild: extract modules.builtin.modinfo from vmlinux.unstripped Currently, we assume all the data for modules.builtin.modinfo are available in vmlinux.o. This makes it impossible for modpost, which is invoked after vmlinux.o, to add additional module info. This commit moves the modules.builtin.modinfo rule after modpost. Signed-off-by: Masahiro Yamada Signed-off-by: Alexey Gladkov Reviewed-by: Nicolas Schier Link: https://patch.msgid.link/cdb3e5b9a739666b755cd0097dc34ab69c350e51.1758182101.git.legion@kernel.org Signed-off-by: Nathan Chancellor --- scripts/Makefile.vmlinux | 26 ++++++++++++++++++++++++++ scripts/Makefile.vmlinux_o | 26 +------------------------- 2 files changed, 27 insertions(+), 25 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux index 70856dab0f54..ce7946171497 100644 --- a/scripts/Makefile.vmlinux +++ b/scripts/Makefile.vmlinux @@ -99,6 +99,32 @@ targets += vmlinux vmlinux: vmlinux.unstripped FORCE $(call if_changed,strip_relocs) +# modules.builtin.modinfo +# --------------------------------------------------------------------------- + +OBJCOPYFLAGS_modules.builtin.modinfo := -j .modinfo -O binary + +targets += modules.builtin.modinfo +modules.builtin.modinfo: vmlinux.unstripped FORCE + $(call if_changed,objcopy) + +# modules.builtin +# --------------------------------------------------------------------------- + +__default: modules.builtin + +# The second line aids cases where multiple modules share the same object. + +quiet_cmd_modules_builtin = GEN $@ + cmd_modules_builtin = \ + tr '\0' '\n' < $< | \ + sed -n 's/^[[:alnum:]:_]*\.file=//p' | \ + tr ' ' '\n' | uniq | sed -e 's:^:kernel/:' -e 's/$$/.ko/' > $@ + +targets += modules.builtin +modules.builtin: modules.builtin.modinfo FORCE + $(call if_changed,modules_builtin) + # modules.builtin.ranges # --------------------------------------------------------------------------- ifdef CONFIG_BUILTIN_MODULE_RANGES diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o index b024ffb3e201..23c8751285d7 100644 --- a/scripts/Makefile.vmlinux_o +++ b/scripts/Makefile.vmlinux_o @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only PHONY := __default -__default: vmlinux.o modules.builtin.modinfo modules.builtin +__default: vmlinux.o include include/config/auto.conf include $(srctree)/scripts/Kbuild.include @@ -73,30 +73,6 @@ vmlinux.o: $(initcalls-lds) vmlinux.a $(KBUILD_VMLINUX_LIBS) FORCE targets += vmlinux.o -# modules.builtin.modinfo -# --------------------------------------------------------------------------- - -OBJCOPYFLAGS_modules.builtin.modinfo := -j .modinfo -O binary - -targets += modules.builtin.modinfo -modules.builtin.modinfo: vmlinux.o FORCE - $(call if_changed,objcopy) - -# modules.builtin -# --------------------------------------------------------------------------- - -# The second line aids cases where multiple modules share the same object. - -quiet_cmd_modules_builtin = GEN $@ - cmd_modules_builtin = \ - tr '\0' '\n' < $< | \ - sed -n 's/^[[:alnum:]:_]*\.file=//p' | \ - tr ' ' '\n' | uniq | sed -e 's:^:kernel/:' -e 's/$$/.ko/' > $@ - -targets += modules.builtin -modules.builtin: modules.builtin.modinfo FORCE - $(call if_changed,modules_builtin) - # Add FORCE to the prerequisites of a target to force it to be always rebuilt. # --------------------------------------------------------------------------- -- cgit v1.2.3 From 83fb49389bbe07defb85b063f7ff0fd016f06b35 Mon Sep 17 00:00:00 2001 From: Alexey Gladkov Date: Thu, 18 Sep 2025 10:05:50 +0200 Subject: modpost: Add modname to mod_device_table alias At this point, if a symbol is compiled as part of the kernel, information about which module the symbol belongs to is lost. To save this it is possible to add the module name to the alias name. It's not very pretty, but it's possible for now. Cc: Miguel Ojeda Cc: Andreas Hindborg Cc: Danilo Krummrich Cc: Alex Gaynor Cc: rust-for-linux@vger.kernel.org Signed-off-by: Alexey Gladkov Acked-by: Danilo Krummrich Acked-by: Nicolas Schier Link: https://patch.msgid.link/1a0d0bd87a4981d465b9ed21e14f4e78eaa03ded.1758182101.git.legion@kernel.org Signed-off-by: Nathan Chancellor --- include/linux/module.h | 14 +++++++++++++- rust/kernel/device_id.rs | 8 ++++---- scripts/mod/file2alias.c | 15 ++++++++++++--- 3 files changed, 29 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/include/linux/module.h b/include/linux/module.h index 3319a5269d28..e31ee29fac6b 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -244,10 +244,22 @@ struct module_kobject *lookup_or_create_module_kobject(const char *name); /* What your module does. */ #define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description) +/* + * Format: __mod_device_table__kmod_____ + * Parts of the string `__kmod_` and `__` are used as delimiters when parsing + * a symbol in file2alias.c + */ +#define __mod_device_table(type, name) \ + __PASTE(__mod_device_table__, \ + __PASTE(__KBUILD_MODNAME, \ + __PASTE(__, \ + __PASTE(type, \ + __PASTE(__, name))))) + #ifdef MODULE /* Creates an alias so file2alias.c can find device table. */ #define MODULE_DEVICE_TABLE(type, name) \ -static typeof(name) __mod_device_table__##type##__##name \ +static typeof(name) __mod_device_table(type, name) \ __attribute__ ((used, alias(__stringify(name)))) #else /* !MODULE */ #define MODULE_DEVICE_TABLE(type, name) diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs index 70d57814ff79..62c42da12e9d 100644 --- a/rust/kernel/device_id.rs +++ b/rust/kernel/device_id.rs @@ -195,10 +195,10 @@ macro_rules! module_device_table { ($table_type: literal, $module_table_name:ident, $table_name:ident) => { #[rustfmt::skip] #[export_name = - concat!("__mod_device_table__", $table_type, - "__", module_path!(), - "_", line!(), - "_", stringify!($table_name)) + concat!("__mod_device_table__", line!(), + "__kmod_", module_path!(), + "__", $table_type, + "__", stringify!($table_name)) ] static $module_table_name: [::core::mem::MaybeUninit; $table_name.raw_ids().size()] = unsafe { ::core::mem::transmute_copy($table_name.raw_ids()) }; diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 00586119a25b..1260bc2287fb 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1476,7 +1476,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, { void *symval; char *zeros = NULL; - const char *type, *name; + const char *type, *name, *modname; size_t typelen; static const char *prefix = "__mod_device_table__"; @@ -1488,10 +1488,19 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT) return; - /* All our symbols are of form __mod_device_table____. */ + /* All our symbols are of form __mod_device_table__kmod_____. */ if (!strstarts(symname, prefix)) return; - type = symname + strlen(prefix); + + modname = strstr(symname, "__kmod_"); + if (!modname) + return; + modname += strlen("__kmod_"); + + type = strstr(modname, "__"); + if (!type) + return; + type += strlen("__"); name = strstr(type, "__"); if (!name) -- cgit v1.2.3 From 5ab23c7923a1d2ae1890026866a2d8506b010a4a Mon Sep 17 00:00:00 2001 From: Alexey Gladkov Date: Thu, 18 Sep 2025 10:05:51 +0200 Subject: modpost: Create modalias for builtin modules For some modules, modalias is generated using the modpost utility and the section is added to the module file. When a module is added inside vmlinux, modpost does not generate modalias for such modules and the information is lost. As a result kmod (which uses modules.builtin.modinfo in userspace) cannot determine that modalias is handled by a builtin kernel module. $ cat /sys/devices/pci0000:00/0000:00:14.0/modalias pci:v00008086d0000A36Dsv00001043sd00008694bc0Csc03i30 $ modinfo xhci_pci name: xhci_pci filename: (builtin) license: GPL file: drivers/usb/host/xhci-pci description: xHCI PCI Host Controller Driver Missing modalias "pci:v*d*sv*sd*bc0Csc03i30*" which will be generated by modpost if the module is built separately. To fix this it is necessary to generate the same modalias for vmlinux as for the individual modules. Fortunately '.vmlinux.export.o' is already generated from which '.modinfo' can be extracted in the same way as for vmlinux.o. Signed-off-by: Masahiro Yamada Signed-off-by: Alexey Gladkov Tested-by: Stephen Rothwell Reviewed-by: Nicolas Schier Link: https://patch.msgid.link/28d4da3b0e3fc8474142746bcf469e03752c3208.1758182101.git.legion@kernel.org Signed-off-by: Nathan Chancellor --- include/linux/module.h | 4 ---- scripts/Makefile.vmlinux | 4 +++- scripts/mksysmap | 3 +++ scripts/mod/file2alias.c | 19 ++++++++++++++++++- scripts/mod/modpost.c | 15 +++++++++++++++ scripts/mod/modpost.h | 2 ++ 6 files changed, 41 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/include/linux/module.h b/include/linux/module.h index e31ee29fac6b..e135cc79acee 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -256,14 +256,10 @@ struct module_kobject *lookup_or_create_module_kobject(const char *name); __PASTE(type, \ __PASTE(__, name))))) -#ifdef MODULE /* Creates an alias so file2alias.c can find device table. */ #define MODULE_DEVICE_TABLE(type, name) \ static typeof(name) __mod_device_table(type, name) \ __attribute__ ((used, alias(__stringify(name)))) -#else /* !MODULE */ -#define MODULE_DEVICE_TABLE(type, name) -#endif /* Version of form [:][-]. * Or for CVS/RCS ID version, everything but the number is stripped. diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux index ce7946171497..1e5e37aadcd0 100644 --- a/scripts/Makefile.vmlinux +++ b/scripts/Makefile.vmlinux @@ -89,11 +89,13 @@ endif remove-section-y := .modinfo remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel*' +remove-symbols := -w --strip-symbol='__mod_device_table__*' + # To avoid warnings: "empty loadable segment detected at ..." from GNU objcopy, # it is necessary to remove the PT_LOAD flag from the segment. quiet_cmd_strip_relocs = OBJCOPY $@ cmd_strip_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \ - $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $@ + $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $@ targets += vmlinux vmlinux: vmlinux.unstripped FORCE diff --git a/scripts/mksysmap b/scripts/mksysmap index a607a0059d11..c4531eacde20 100755 --- a/scripts/mksysmap +++ b/scripts/mksysmap @@ -59,6 +59,9 @@ # EXPORT_SYMBOL (namespace) / __kstrtabns_/d +# MODULE_DEVICE_TABLE (symbol name) +/ __mod_device_table__/d + # --------------------------------------------------------------------------- # Ignored suffixes # (do not forget '$' after each pattern) diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 1260bc2287fb..7da9735e7ab3 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1477,7 +1477,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, void *symval; char *zeros = NULL; const char *type, *name, *modname; - size_t typelen; + size_t typelen, modnamelen; static const char *prefix = "__mod_device_table__"; /* We're looking for a section relative symbol */ @@ -1500,6 +1500,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, type = strstr(modname, "__"); if (!type) return; + modnamelen = type - modname; type += strlen("__"); name = strstr(type, "__"); @@ -1526,5 +1527,21 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, } } + if (mod->is_vmlinux) { + struct module_alias *alias; + + /* + * If this is vmlinux, record the name of the builtin module. + * Traverse the linked list in the reverse order, and set the + * builtin_modname unless it has already been set in the + * previous call. + */ + list_for_each_entry_reverse(alias, &mod->aliases, node) { + if (alias->builtin_modname) + break; + alias->builtin_modname = xstrndup(modname, modnamelen); + } + } + free(zeros); } diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 5ca7c268294e..47c8aa2a6939 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2067,11 +2067,26 @@ static void write_if_changed(struct buffer *b, const char *fname) static void write_vmlinux_export_c_file(struct module *mod) { struct buffer buf = { }; + struct module_alias *alias, *next; buf_printf(&buf, "#include \n"); add_exported_symbols(&buf, mod); + + buf_printf(&buf, + "#include \n" + "#undef __MODULE_INFO_PREFIX\n" + "#define __MODULE_INFO_PREFIX\n"); + + list_for_each_entry_safe(alias, next, &mod->aliases, node) { + buf_printf(&buf, "MODULE_INFO(%s.alias, \"%s\");\n", + alias->builtin_modname, alias->str); + list_del(&alias->node); + free(alias->builtin_modname); + free(alias); + } + write_if_changed(&buf, ".vmlinux.export.c"); free(buf.p); } diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index 9133e4c3803f..2aecb8f25c87 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h @@ -99,10 +99,12 @@ buf_write(struct buffer *buf, const char *s, int len); * struct module_alias - auto-generated MODULE_ALIAS() * * @node: linked to module::aliases + * @modname: name of the builtin module (only for vmlinux) * @str: a string for MODULE_ALIAS() */ struct module_alias { struct list_head node; + char *builtin_modname; char str[]; }; -- cgit v1.2.3 From 3328d39a8dca2d6ed27197a0025df7540b99adf2 Mon Sep 17 00:00:00 2001 From: Alexey Gladkov Date: Thu, 18 Sep 2025 10:05:52 +0200 Subject: kbuild: vmlinux.unstripped should always depend on .vmlinux.export.o Since .vmlinux.export.c is used to add generated by modpost modaliases for builtin modules the .vmlinux.export.o is no longer optional and should always be created. The generation of this file is not dependent on CONFIG_MODULES. Signed-off-by: Alexey Gladkov Reviewed-by: Nicolas Schier Link: https://patch.msgid.link/0e63a9c7741fe8217e4fd7c60afcf057ffa2ef5a.1758182101.git.legion@kernel.org Signed-off-by: Nathan Chancellor --- scripts/Makefile.vmlinux | 9 ++------- scripts/link-vmlinux.sh | 5 +---- 2 files changed, 3 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux index 1e5e37aadcd0..7c6ae9886f8f 100644 --- a/scripts/Makefile.vmlinux +++ b/scripts/Makefile.vmlinux @@ -53,11 +53,6 @@ endif # vmlinux.unstripped # --------------------------------------------------------------------------- -ifdef CONFIG_MODULES -targets += .vmlinux.export.o -vmlinux.unstripped: .vmlinux.export.o -endif - ifdef CONFIG_ARCH_WANTS_PRE_LINK_VMLINUX vmlinux.unstripped: arch/$(SRCARCH)/tools/vmlinux.arch.o @@ -72,8 +67,8 @@ cmd_link_vmlinux = \ $< "$(LD)" "$(KBUILD_LDFLAGS)" "$(LDFLAGS_vmlinux)" "$@"; \ $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true) -targets += vmlinux.unstripped -vmlinux.unstripped: scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE +targets += vmlinux.unstripped .vmlinux.export.o +vmlinux.unstripped: scripts/link-vmlinux.sh vmlinux.o .vmlinux.export.o $(KBUILD_LDS) FORCE +$(call if_changed_dep,link_vmlinux) ifdef CONFIG_DEBUG_INFO_BTF vmlinux.unstripped: $(RESOLVE_BTFIDS) diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 51367c2bfc21..433849ff7529 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -73,10 +73,7 @@ vmlinux_link() objs="${objs} .builtin-dtbs.o" fi - if is_enabled CONFIG_MODULES; then - objs="${objs} .vmlinux.export.o" - fi - + objs="${objs} .vmlinux.export.o" objs="${objs} init/version-timestamp.o" if [ "${SRCARCH}" = "um" ]; then -- cgit v1.2.3 From 64f4ea200eca05a248533b8374d7b5f0756a3990 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 23 Sep 2025 14:34:17 -0700 Subject: kconfig: Fix BrokenPipeError warnings in selftests The kconfig test harness ("make testconfig") was generating BrokenPipeError warnings when running interactive tests like oldaskconfig and oldconfig: /usr/lib/python3/dist-packages/_pytest/unraisableexception.py:85: PytestUnraisableExceptionWarning: Exception ignored in: <_io.BufferedWriter name=12> Traceback (most recent call last): File "/srv/code/scripts/kconfig/tests/conftest.py", line 127, in oldaskconfig return self._run_conf('--oldaskconfig', dot_config=dot_config, ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ interactive=True, in_keys=in_keys) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ BrokenPipeError: [Errno 32] Broken pipe The issue occurred when the test framework attempted to write to stdin after the conf subprocess had already exited. Wrap stdin write operations in try/except to catch BrokenPipeError and stop sending more input. Add explicit flush() after writes so we can see delivery errors immediately. Ignore BrokenPipeError when closing stdin. Explicitly call wait() to validate subprocess termination. Reviewed-by: Nathan Chancellor Tested-by: Nathan Chancellor Link: https://lore.kernel.org/r/20250923213422.1105654-1-kees@kernel.org Signed-off-by: Kees Cook --- scripts/kconfig/tests/conftest.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/tests/conftest.py b/scripts/kconfig/tests/conftest.py index 2a2a7e2da060..d94b79e012c0 100644 --- a/scripts/kconfig/tests/conftest.py +++ b/scripts/kconfig/tests/conftest.py @@ -81,7 +81,22 @@ class Conf: # For interactive modes such as oldaskconfig, oldconfig, # send 'Enter' key until the program finishes. if interactive: - ps.stdin.write(b'\n') + try: + ps.stdin.write(b'\n') + ps.stdin.flush() + except (BrokenPipeError, OSError): + # Process has exited, stop sending input + break + + # Close stdin gracefully + try: + ps.stdin.close() + except (BrokenPipeError, OSError): + # Ignore broken pipe on close + pass + + # Wait for process to complete + ps.wait() self.retcode = ps.returncode self.stdout = ps.stdout.read().decode() -- cgit v1.2.3 From f9afce4f32e9a120fc902fa6c9e0b90ad799a6ec Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 23 Sep 2025 14:34:18 -0700 Subject: kconfig: Add transitional symbol attribute for migration support During kernel option migrations (e.g. CONFIG_CFI_CLANG to CONFIG_CFI), existing .config files need to maintain backward compatibility while preventing deprecated options from appearing in newly generated configurations. This is challenging with existing Kconfig mechanisms because: 1. Simply removing old options breaks existing .config files. 2. Manually listing an option as "deprecated" leaves it needlessly visible and still writes them to new .config files. 3. Using any method to remove visibility (.e.g no 'prompt', 'if n', etc) prevents the option from being processed at all. Add a "transitional" attribute that creates symbols which are: - Processed during configuration (can influence other symbols' defaults) - Hidden from user menus (no prompts appear) - Omitted from newly written .config files (gets migrated) - Restricted to only having help sections (no defaults, selects, etc) making it truly just a "prior value pass-through" option. The transitional syntax requires a type argument and prevents type redefinition: config NEW_OPTION bool "New option" default OLD_OPTION config OLD_OPTION bool transitional help Transitional config for OLD_OPTION migration. This allows seamless migration: olddefconfig processes existing CONFIG_OLD_OPTION=y settings to enable CONFIG_NEW_OPTION=y, while CONFIG_OLD_OPTION is omitted from newly generated .config files. Added positive and negative testing via "testconfig" make target. Co-developed-by: Vegard Nossum Signed-off-by: Vegard Nossum Reviewed-by: Nathan Chancellor Tested-by: Nathan Chancellor Link: https://lore.kernel.org/r/20250923213422.1105654-2-kees@kernel.org Signed-off-by: Kees Cook --- Documentation/kbuild/kconfig-language.rst | 32 +++++++ scripts/kconfig/expr.h | 1 + scripts/kconfig/lexer.l | 1 + scripts/kconfig/parser.y | 47 ++++++++++ scripts/kconfig/symbol.c | 7 +- scripts/kconfig/tests/err_transitional/Kconfig | 52 +++++++++++ scripts/kconfig/tests/err_transitional/__init__.py | 14 +++ .../kconfig/tests/err_transitional/expected_stderr | 7 ++ scripts/kconfig/tests/transitional/Kconfig | 100 +++++++++++++++++++++ scripts/kconfig/tests/transitional/__init__.py | 18 ++++ scripts/kconfig/tests/transitional/expected_config | 12 +++ scripts/kconfig/tests/transitional/initial_config | 16 ++++ 12 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 scripts/kconfig/tests/err_transitional/Kconfig create mode 100644 scripts/kconfig/tests/err_transitional/__init__.py create mode 100644 scripts/kconfig/tests/err_transitional/expected_stderr create mode 100644 scripts/kconfig/tests/transitional/Kconfig create mode 100644 scripts/kconfig/tests/transitional/__init__.py create mode 100644 scripts/kconfig/tests/transitional/expected_config create mode 100644 scripts/kconfig/tests/transitional/initial_config (limited to 'scripts') diff --git a/Documentation/kbuild/kconfig-language.rst b/Documentation/kbuild/kconfig-language.rst index a91abb8f6840..abce88f15d7c 100644 --- a/Documentation/kbuild/kconfig-language.rst +++ b/Documentation/kbuild/kconfig-language.rst @@ -232,6 +232,38 @@ applicable everywhere (see syntax). enables the third modular state for all config symbols. At most one symbol may have the "modules" option set. +- transitional attribute: "transitional" + This declares the symbol as transitional, meaning it should be processed + during configuration but omitted from newly written .config files. + Transitional symbols are useful for backward compatibility during config + option migrations - they allow olddefconfig to process existing .config + files while ensuring the old option doesn't appear in new configurations. + + A transitional symbol: + - Has no prompt (is not visible to users in menus) + - Is processed normally during configuration (values are read and used) + - Can be referenced in default expressions of other symbols + - Is not written to new .config files + - Cannot have any other properties (it is a pass-through option) + + Example migration from OLD_NAME to NEW_NAME:: + + config NEW_NAME + bool "New option name" + default OLD_NAME + help + This replaces the old CONFIG_OLD_NAME option. + + config OLD_NAME + bool + transitional + help + Transitional config for OLD_NAME to NEW_NAME migration. + + With this setup, existing .config files with "CONFIG_OLD_NAME=y" will + result in "CONFIG_NEW_NAME=y" being set, while CONFIG_OLD_NAME will be + omitted from newly written .config files. + Menu dependencies ----------------- diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index fe2231e0e6a4..5f900d18dae0 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -145,6 +145,7 @@ struct symbol { #define SYMBOL_CONST 0x0001 /* symbol is const */ #define SYMBOL_CHECK 0x0008 /* used during dependency checking */ #define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */ +#define SYMBOL_TRANS 0x0100 /* symbol is transitional only (not visible)*/ #define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */ #define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .config */ #define SYMBOL_CHECKED 0x2000 /* used during dependency checking */ diff --git a/scripts/kconfig/lexer.l b/scripts/kconfig/lexer.l index 9c2cdfc33c6f..6d2c92c6095d 100644 --- a/scripts/kconfig/lexer.l +++ b/scripts/kconfig/lexer.l @@ -126,6 +126,7 @@ n [A-Za-z0-9_-] "select" return T_SELECT; "source" return T_SOURCE; "string" return T_STRING; +"transitional" return T_TRANSITIONAL; "tristate" return T_TRISTATE; "visible" return T_VISIBLE; "||" return T_OR; diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y index e9c3c664e925..49b79dde1725 100644 --- a/scripts/kconfig/parser.y +++ b/scripts/kconfig/parser.y @@ -75,6 +75,7 @@ struct menu *current_menu, *current_entry, *current_choice; %token T_SELECT %token T_SOURCE %token T_STRING +%token T_TRANSITIONAL %token T_TRISTATE %token T_VISIBLE %token T_EOL @@ -205,6 +206,12 @@ config_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno); }; +config_option: T_TRANSITIONAL T_EOL +{ + current_entry->sym->flags |= SYMBOL_TRANS; + printd(DEBUG_PARSE, "%s:%d:transitional\n", cur_filename, cur_lineno); +}; + config_option: default expr if_expr T_EOL { menu_add_expr(P_DEFAULT, $2, $3); @@ -482,6 +489,43 @@ assign_val: %% +/** + * transitional_check_sanity - check transitional symbols have no other + * properties + * + * @menu: menu of the potentially transitional symbol + * + * Return: -1 if an error is found, 0 otherwise. + */ +static int transitional_check_sanity(const struct menu *menu) +{ + struct property *prop; + + if (!menu->sym || !(menu->sym->flags & SYMBOL_TRANS)) + return 0; + + /* Check for depends and visible conditions. */ + if ((menu->dep && !expr_is_yes(menu->dep)) || + (menu->visibility && !expr_is_yes(menu->visibility))) { + fprintf(stderr, "%s:%d: error: %s", + menu->filename, menu->lineno, + "transitional symbols can only have help sections\n"); + return -1; + } + + /* Check for any property other than "help". */ + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->type != P_COMMENT) { + fprintf(stderr, "%s:%d: error: %s", + prop->filename, prop->lineno, + "transitional symbols can only have help sections\n"); + return -1; + } + } + + return 0; +} + /** * choice_check_sanity - check sanity of a choice member * @@ -558,6 +602,9 @@ void conf_parse(const char *name) if (menu->sym && sym_check_deps(menu->sym)) yynerrs++; + if (transitional_check_sanity(menu)) + yynerrs++; + if (menu->sym && sym_is_choice(menu->sym)) { menu_for_each_sub_entry(child, menu) if (child->sym && choice_check_sanity(child)) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 26ab10c0fd76..760cac998381 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -214,6 +214,11 @@ static void sym_calc_visibility(struct symbol *sym) struct property *prop; tristate tri; + if (sym->flags & SYMBOL_TRANS) { + sym->visible = yes; + return; + } + /* any prompt visible? */ tri = no; for_all_prompts(sym, prop) { @@ -526,7 +531,7 @@ void sym_calc_value(struct symbol *sym) } } - if (sym_is_choice(sym)) + if (sym_is_choice(sym) || sym->flags & SYMBOL_TRANS) sym->flags &= ~SYMBOL_WRITE; } diff --git a/scripts/kconfig/tests/err_transitional/Kconfig b/scripts/kconfig/tests/err_transitional/Kconfig new file mode 100644 index 000000000000..a75ed3b2fe5e --- /dev/null +++ b/scripts/kconfig/tests/err_transitional/Kconfig @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: GPL-2.0 +# Test that transitional symbols cannot have properties other than help + +config BAD_DEFAULT + bool + transitional + default y + help + This transitional symbol illegally has a default property. + +config BAD_PROMPT + bool + transitional + prompt "Bad prompt" + help + This transitional symbol illegally has a prompt. + +config BAD_SELECT + bool + transitional + select OTHER_SYMBOL + help + This transitional symbol illegally has a select. + +config BAD_IMPLY + bool + transitional + imply OTHER_SYMBOL + help + This transitional symbol illegally has an imply. + +config BAD_DEPENDS + bool + transitional + depends on OTHER_SYMBOL + help + This transitional symbol illegally has a depends. + +config BAD_RANGE + int + transitional + range 1 10 + help + This transitional symbol illegally has a range. + +config BAD_NO_TYPE + transitional + help + This transitional symbol illegally has no type specified. + +config OTHER_SYMBOL + bool diff --git a/scripts/kconfig/tests/err_transitional/__init__.py b/scripts/kconfig/tests/err_transitional/__init__.py new file mode 100644 index 000000000000..7dffb5b0833f --- /dev/null +++ b/scripts/kconfig/tests/err_transitional/__init__.py @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +""" +Test that transitional symbols with invalid properties are rejected. + +Transitional symbols can only have help sections. Any other properties +(default, select, depends, etc.) should cause a parser error. +""" + +def test(conf): + # This should fail with exit code 1 due to invalid transitional symbol + assert conf.olddefconfig() == 1 + + # Check that the error message is about transitional symbols + assert conf.stderr_contains('expected_stderr') diff --git a/scripts/kconfig/tests/err_transitional/expected_stderr b/scripts/kconfig/tests/err_transitional/expected_stderr new file mode 100644 index 000000000000..b52db4f680f4 --- /dev/null +++ b/scripts/kconfig/tests/err_transitional/expected_stderr @@ -0,0 +1,7 @@ +Kconfig:46:warning: config symbol defined without type +Kconfig:7: error: transitional symbols can only have help sections +Kconfig:14: error: transitional symbols can only have help sections +Kconfig:21: error: transitional symbols can only have help sections +Kconfig:28: error: transitional symbols can only have help sections +Kconfig:32: error: transitional symbols can only have help sections +Kconfig:42: error: transitional symbols can only have help sections diff --git a/scripts/kconfig/tests/transitional/Kconfig b/scripts/kconfig/tests/transitional/Kconfig new file mode 100644 index 000000000000..62c3b24665b9 --- /dev/null +++ b/scripts/kconfig/tests/transitional/Kconfig @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: GPL-2.0 +# Test transitional symbols for config migration with all Kconfig types + +# Enable module support for tristate testing +config MODULES + bool "Enable loadable module support" + modules + default y + +# Basic migration tests for all types +config NEW_BOOL + bool "New bool option" + default OLD_BOOL + +config OLD_BOOL + bool + transitional + +config NEW_TRISTATE + tristate "New tristate option" + default OLD_TRISTATE + +config OLD_TRISTATE + tristate + transitional + +config NEW_STRING + string "New string option" + default OLD_STRING + +config OLD_STRING + string + transitional + +config NEW_HEX + hex "New hex option" + default OLD_HEX + +config OLD_HEX + hex + transitional + +config NEW_INT + int "New int option" + default OLD_INT + +config OLD_INT + int + transitional + +# Precedence tests for all types +config NEW_BOOL_PRECEDENCE + bool "New bool option with precedence" + default OLD_BOOL_PRECEDENCE + +config OLD_BOOL_PRECEDENCE + bool + transitional + +config NEW_STRING_PRECEDENCE + string "New string option with precedence" + default OLD_STRING_PRECEDENCE + +config OLD_STRING_PRECEDENCE + string + transitional + +config NEW_TRISTATE_PRECEDENCE + tristate "New tristate option with precedence" + default OLD_TRISTATE_PRECEDENCE + +config OLD_TRISTATE_PRECEDENCE + tristate + transitional + +config NEW_HEX_PRECEDENCE + hex "New hex option with precedence" + default OLD_HEX_PRECEDENCE + +config OLD_HEX_PRECEDENCE + hex + transitional + +config NEW_INT_PRECEDENCE + int "New int option with precedence" + default OLD_INT_PRECEDENCE + +config OLD_INT_PRECEDENCE + int + transitional + +# Test that help sections are allowed for transitional symbols +config OLD_WITH_HELP + bool + transitional + help + This transitional symbol has a help section to validate that help is allowed. + +config REGULAR_OPTION + bool "Regular option" diff --git a/scripts/kconfig/tests/transitional/__init__.py b/scripts/kconfig/tests/transitional/__init__.py new file mode 100644 index 000000000000..61937d10edf1 --- /dev/null +++ b/scripts/kconfig/tests/transitional/__init__.py @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 +""" +Test transitional symbol migration functionality for all Kconfig types. + +This tests that: +- OLD_* options in existing .config cause NEW_* options to be set +- OLD_* options are not written to the new .config file +- NEW_* options appear in the new .config file with correct values +- All Kconfig types work correctly: bool, tristate, string, hex, int +- User-set NEW values take precedence over conflicting OLD transitional values +""" + +def test(conf): + # Run olddefconfig to process the migration with the initial config + assert conf.olddefconfig(dot_config='initial_config') == 0 + + # Check that the configuration matches expected output + assert conf.config_contains('expected_config') diff --git a/scripts/kconfig/tests/transitional/expected_config b/scripts/kconfig/tests/transitional/expected_config new file mode 100644 index 000000000000..846e9ddcab91 --- /dev/null +++ b/scripts/kconfig/tests/transitional/expected_config @@ -0,0 +1,12 @@ +CONFIG_MODULES=y +CONFIG_NEW_BOOL=y +CONFIG_NEW_TRISTATE=m +CONFIG_NEW_STRING="test string" +CONFIG_NEW_HEX=0x1234 +CONFIG_NEW_INT=42 +# CONFIG_NEW_BOOL_PRECEDENCE is not set +CONFIG_NEW_STRING_PRECEDENCE="user value" +CONFIG_NEW_TRISTATE_PRECEDENCE=y +CONFIG_NEW_HEX_PRECEDENCE=0xABCD +CONFIG_NEW_INT_PRECEDENCE=100 +# CONFIG_REGULAR_OPTION is not set diff --git a/scripts/kconfig/tests/transitional/initial_config b/scripts/kconfig/tests/transitional/initial_config new file mode 100644 index 000000000000..e648a65e504c --- /dev/null +++ b/scripts/kconfig/tests/transitional/initial_config @@ -0,0 +1,16 @@ +CONFIG_MODULES=y +CONFIG_OLD_BOOL=y +CONFIG_OLD_TRISTATE=m +CONFIG_OLD_STRING="test string" +CONFIG_OLD_HEX=0x1234 +CONFIG_OLD_INT=42 +# CONFIG_NEW_BOOL_PRECEDENCE is not set +CONFIG_OLD_BOOL_PRECEDENCE=y +CONFIG_NEW_STRING_PRECEDENCE="user value" +CONFIG_OLD_STRING_PRECEDENCE="old value" +CONFIG_NEW_TRISTATE_PRECEDENCE=y +CONFIG_OLD_TRISTATE_PRECEDENCE=m +CONFIG_NEW_HEX_PRECEDENCE=0xABCD +CONFIG_OLD_HEX_PRECEDENCE=0x5678 +CONFIG_NEW_INT_PRECEDENCE=100 +CONFIG_OLD_INT_PRECEDENCE=200 -- cgit v1.2.3 From 57c49d2355729c12475554b4c51dbf830b02d08d Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Thu, 18 Sep 2025 13:43:46 +0300 Subject: scripts/coccinelle: Find PTR_ERR() to %pe candidates Add a new Coccinelle script to identify places where PTR_ERR() is used in print functions and suggest using the %pe format specifier instead. For printing error pointers (i.e., a pointer for which IS_ERR() is true) %pe will print a symbolic error name (e.g,. -EINVAL), opposed to the raw errno (e.g,. -22) produced by PTR_ERR(). It also makes the code cleaner by saving a redundant call to PTR_ERR(). The script supports context, report, and org modes. Example transformation: printk("Error: %ld\n", PTR_ERR(ptr)); // Before printk("Error: %pe\n", ptr); // After Signed-off-by: Gal Pressman Reviewed-by: Alexei Lazar Signed-off-by: Tariq Toukan Reviewed-by: Simon Horman Link: https://patch.msgid.link/1758192227-701925-2-git-send-email-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- scripts/coccinelle/misc/ptr_err_to_pe.cocci | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 scripts/coccinelle/misc/ptr_err_to_pe.cocci (limited to 'scripts') diff --git a/scripts/coccinelle/misc/ptr_err_to_pe.cocci b/scripts/coccinelle/misc/ptr_err_to_pe.cocci new file mode 100644 index 000000000000..0494c7709245 --- /dev/null +++ b/scripts/coccinelle/misc/ptr_err_to_pe.cocci @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0-only +/// Use %pe format specifier instead of PTR_ERR() for printing error pointers. +/// +/// For printing error pointers (i.e., a pointer for which IS_ERR() is true) +/// %pe will print a symbolic error name (e.g., -EINVAL), opposed to the raw +/// errno (e.g., -22) produced by PTR_ERR(). +/// It also makes the code cleaner by saving a redundant call to PTR_ERR(). +/// +// Confidence: High +// Copyright: (C) 2025 NVIDIA CORPORATION & AFFILIATES. +// URL: https://coccinelle.gitlabpages.inria.fr/website +// Options: --no-includes --include-headers + +virtual context +virtual org +virtual report + +@r@ +expression ptr; +constant fmt; +position p; +identifier print_func; +@@ +* print_func(..., fmt, ..., PTR_ERR@p(ptr), ...) + +@script:python depends on r && report@ +p << r.p; +@@ +coccilib.report.print_report(p[0], "WARNING: Consider using %pe to print PTR_ERR()") + +@script:python depends on r && org@ +p << r.p; +@@ +coccilib.org.print_todo(p[0], "WARNING: Consider using %pe to print PTR_ERR()") -- cgit v1.2.3 From 77e46093e83a2788e2cf80ba8c8731375e587ad2 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Fri, 26 Sep 2025 12:57:49 +0200 Subject: scripts: dt_to_config: fix grammar and a typo in --help text - grammar: singular/plural inconsistency - typo: "of" -> "or" Signed-off-by: Markus Heidelberg Signed-off-by: Rob Herring (Arm) --- scripts/dtc/dt_to_config | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/dtc/dt_to_config b/scripts/dtc/dt_to_config index 299d1c2b20d7..70d6d5f06bdc 100755 --- a/scripts/dtc/dt_to_config +++ b/scripts/dtc/dt_to_config @@ -51,10 +51,10 @@ $num_pr_flags = $pr_flag_pos_config_test_fail + 1; "compatible is white listed", "matching driver and/or kernel config is hard coded", "kernel config hard coded in Makefile", - "one or more kernel config file options is not set", - "one or more kernel config file options is set to 'm'", - "one or more kernel config file options is set to 'y'", - "one of more kernel config file options fails to have correct value" + "one or more kernel config file options are not set", + "one or more kernel config file options are set to 'm'", + "one or more kernel config file options are set to 'y'", + "one or more kernel config file options fail to have correct value" ); -- cgit v1.2.3 From 2ea77fca84f07849aa995271271340d262d0c2e9 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Sat, 27 Sep 2025 21:28:06 -0700 Subject: modpost: Initialize builtin_modname to stop SIGSEGVs Segmentation fault ./scripts/mod/modpost -o vmlinux.symvers vmlinux.o stops the kernel build. It comes when write_vmlinux_export_c_file() tries to buf_printf alias->builtin_modname. malloc'ed memory is not necessarily zeroed. NULL new->builtin_modname before adding to aliases. Fixes: 5ab23c7923a1 ("modpost: Create modalias for builtin modules") Signed-off-by: Hugh Dickins Reviewed-by: Nicolas Schier Link: https://patch.msgid.link/4590a243-0a7e-b7e6-e2d3-cd1b41a12237@google.com Signed-off-by: Nathan Chancellor --- scripts/mod/file2alias.c | 1 + 1 file changed, 1 insertion(+) (limited to 'scripts') diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 7da9735e7ab3..b3333560b95e 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -94,6 +94,7 @@ module_alias_printf(struct module *mod, bool append_wildcard, } } + new->builtin_modname = NULL; list_add_tail(&new->node, &mod->aliases); } -- cgit v1.2.3 From 99b70ece33d87500ef7bee8e32cb99772c45ce14 Mon Sep 17 00:00:00 2001 From: Suchit Karunakaran Date: Tue, 23 Sep 2025 22:47:21 +0530 Subject: checkpatch: suppress strscpy warnings for userspace tools The checkpatch.pl script currently warns against the use of strcpy, strlcpy, and strncpy, recommending strscpy as a safer alternative. However, these warnings are also triggered for code under tools/ and scripts/, which are userspace utilities where strscpy is not available. This patch suppresses these warnings for files in tools/ and scripts/. Link: https://lkml.kernel.org/r/20250923171722.7798-1-suchitkarunakaran@gmail.com Signed-off-by: Suchit Karunakaran Acked-by: Joe Perches Cc: Andy Whitcroft Cc: Dwaipayan Ray Cc: Lukas Bulwahn Cc: Shuah Khan Signed-off-by: Andrew Morton --- scripts/checkpatch.pl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 319cc5f85885..92669904eecc 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2636,6 +2636,11 @@ sub exclude_global_initialisers { $realfile =~ m@/bpf/.*\.bpf\.c$@; } +sub is_userspace { + my ($realfile) = @_; + return ($realfile =~ m@^tools/@ || $realfile =~ m@^scripts/@); +} + sub process { my $filename = shift; @@ -7018,21 +7023,20 @@ sub process { # } # } # } - # strcpy uses that should likely be strscpy - if ($line =~ /\bstrcpy\s*\(/) { + if ($line =~ /\bstrcpy\s*\(/ && !is_userspace($realfile)) { WARN("STRCPY", "Prefer strscpy over strcpy - see: https://github.com/KSPP/linux/issues/88\n" . $herecurr); } # strlcpy uses that should likely be strscpy - if ($line =~ /\bstrlcpy\s*\(/) { + if ($line =~ /\bstrlcpy\s*\(/ && !is_userspace($realfile)) { WARN("STRLCPY", "Prefer strscpy over strlcpy - see: https://github.com/KSPP/linux/issues/89\n" . $herecurr); } # strncpy uses that should likely be strscpy or strscpy_pad - if ($line =~ /\bstrncpy\s*\(/) { + if ($line =~ /\bstrncpy\s*\(/ && !is_userspace($realfile)) { WARN("STRNCPY", "Prefer strscpy, strscpy_pad, or __nonstring over strncpy - see: https://github.com/KSPP/linux/issues/90\n" . $herecurr); } -- cgit v1.2.3 From de7342228b7343774d6a9981c2ddbfb5e201044b Mon Sep 17 00:00:00 2001 From: Rong Tao Date: Sat, 4 Oct 2025 22:23:29 +0800 Subject: bpf: Finish constification of 1st parameter of bpf_d_path() The commit 1b8abbb12128 ("bpf...d_path(): constify path argument") constified the first parameter of the bpf_d_path(), but failed to update it in all places. Finish constification. Otherwise the selftest fail to build: .../selftests/bpf/bpf_experimental.h:222:12: error: conflicting types for 'bpf_path_d_path' 222 | extern int bpf_path_d_path(const struct path *path, char *buf, size_t buf__sz) __ksym; | ^ .../selftests/bpf/tools/include/vmlinux.h:153922:12: note: previous declaration is here 153922 | extern int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz) __weak __ksym; Fixes: 1b8abbb12128 ("bpf...d_path(): constify path argument") Signed-off-by: Rong Tao Signed-off-by: Alexei Starovoitov --- include/uapi/linux/bpf.h | 2 +- scripts/bpf_doc.py | 1 + tools/include/uapi/linux/bpf.h | 2 +- tools/testing/selftests/bpf/progs/verifier_vfs_accept.c | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index ae83d8649ef1..6829936d33f5 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -4891,7 +4891,7 @@ union bpf_attr { * * **-ENOENT** if the bpf_local_storage cannot be found. * - * long bpf_d_path(struct path *path, char *buf, u32 sz) + * long bpf_d_path(const struct path *path, char *buf, u32 sz) * Description * Return full path for given **struct path** object, which * needs to be the kernel BTF *path* object. The path is diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py index c77dc40f7689..15d113a1bc1d 100755 --- a/scripts/bpf_doc.py +++ b/scripts/bpf_doc.py @@ -788,6 +788,7 @@ class PrinterHelpersHeader(Printer): 'struct task_struct', 'struct cgroup', 'struct path', + 'const struct path', 'struct btf_ptr', 'struct inode', 'struct socket', diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index ae83d8649ef1..6829936d33f5 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -4891,7 +4891,7 @@ union bpf_attr { * * **-ENOENT** if the bpf_local_storage cannot be found. * - * long bpf_d_path(struct path *path, char *buf, u32 sz) + * long bpf_d_path(const struct path *path, char *buf, u32 sz) * Description * Return full path for given **struct path** object, which * needs to be the kernel BTF *path* object. The path is diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c b/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c index 3e2d76ee8050..55398c04290a 100644 --- a/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c +++ b/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c @@ -70,7 +70,7 @@ __success int BPF_PROG(path_d_path_from_file_argument, struct file *file) { int ret; - struct path *path; + const struct path *path; /* The f_path member is a path which is embedded directly within a * file. Therefore, a pointer to such embedded members are still -- cgit v1.2.3 From 0902b3cb23ce7f436bddbdf6ba7b1ed427b36bd9 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 30 Sep 2025 08:45:19 -0700 Subject: kconfig: Avoid prompting for transitional symbols The "transitional" symbol keyword, while working with the "olddefconfig" target, was prompting during "oldconfig". This occurred because these symbols were not being marked as user-defined when they received values from transitional symbols that had user values. The "olddefconfig" target explicitly doesn't prompt for anything, so this deficiency wasn't noticed. The issue manifested when a symbol's value came from a transitional symbol's user value but the receiving symbol wasn't marked with SYMBOL_DEF_USER. Thus the "oldconfig" logic would then prompt for these symbols unnecessarily. Check after value calculation whether a symbol without a user value gets its value from a single transitional symbol that does have a user value. In such cases, mark the receiving symbol as user-defined to prevent prompting. Update regression tests to verify that symbols with transitional defaults are not prompted in "oldconfig", except when conditional defaults evaluate to 'no' and should legitimately be prompted. Build tested with "make testconfig". Reported-by: Linus Torvalds Closes: https://lore.kernel.org/lkml/CAHk-=wgZjUk4Cy2XgNkTrQoO8XCmNUHrTe5D519Fij1POK+3qw@mail.gmail.com/ Fixes: f9afce4f32e9 ("kconfig: Add transitional symbol attribute for migration support") Cc: Vegard Nossum Link: https://lore.kernel.org/r/20250930154514.it.623-kees@kernel.org Signed-off-by: Kees Cook --- scripts/kconfig/symbol.c | 15 +++++++++- scripts/kconfig/tests/transitional/Kconfig | 32 ++++++++++++++++++++++ scripts/kconfig/tests/transitional/__init__.py | 7 +++++ scripts/kconfig/tests/transitional/expected_config | 3 ++ scripts/kconfig/tests/transitional/expected_stdout | 1 + scripts/kconfig/tests/transitional/initial_config | 4 +++ 6 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 scripts/kconfig/tests/transitional/expected_stdout (limited to 'scripts') diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 760cac998381..7e81b3676ee9 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -411,7 +411,7 @@ bool sym_dep_errors(void) void sym_calc_value(struct symbol *sym) { struct symbol_value newval, oldval; - struct property *prop; + struct property *prop = NULL; struct menu *choice_menu; if (!sym) @@ -520,6 +520,19 @@ void sym_calc_value(struct symbol *sym) ; } + /* + * If the symbol lacks a user value but its value comes from a + * single transitional symbol with an existing user value, mark + * this symbol as having a user value to avoid prompting. + */ + if (prop && !sym_has_value(sym)) { + struct symbol *ds = prop_get_symbol(prop); + if (ds && (ds->flags & SYMBOL_TRANS) && sym_has_value(ds)) { + sym->def[S_DEF_USER] = newval; + sym->flags |= SYMBOL_DEF_USER; + } + } + sym->curr = newval; sym_validate_range(sym); diff --git a/scripts/kconfig/tests/transitional/Kconfig b/scripts/kconfig/tests/transitional/Kconfig index 62c3b24665b9..faa4d396f828 100644 --- a/scripts/kconfig/tests/transitional/Kconfig +++ b/scripts/kconfig/tests/transitional/Kconfig @@ -96,5 +96,37 @@ config OLD_WITH_HELP help This transitional symbol has a help section to validate that help is allowed. +# Test that we can set something to =n via transitional symbol +config NEW_DISABLED + tristate "Check for setting to disabled" + default OLD_DISABLED + +config OLD_DISABLED + tristate + transitional + +# Test that a potential new value disappears if it lacks a prompt +config NEW_DISABLED_UNSAVED + tristate + default OLD_DISABLED + +config OLD_DISABLED_UNSAVED + tristate + transitional + +# Test conditional default: transitional value should not prevent prompting +# when default visibility makes the expression evaluate to 'no' +config DEPENDENCY_TEST + bool "Dependency for testing" + default n + +config NEW_CONDITIONAL_DEFAULT + bool "New option with conditional default" + default OLD_CONDITIONAL_DEFAULT if DEPENDENCY_TEST + +config OLD_CONDITIONAL_DEFAULT + bool + transitional + config REGULAR_OPTION bool "Regular option" diff --git a/scripts/kconfig/tests/transitional/__init__.py b/scripts/kconfig/tests/transitional/__init__.py index 61937d10edf1..b50ba2397548 100644 --- a/scripts/kconfig/tests/transitional/__init__.py +++ b/scripts/kconfig/tests/transitional/__init__.py @@ -6,6 +6,7 @@ This tests that: - OLD_* options in existing .config cause NEW_* options to be set - OLD_* options are not written to the new .config file - NEW_* options appear in the new .config file with correct values +- NEW_* options with defaults from transitional symbols are not prompted - All Kconfig types work correctly: bool, tristate, string, hex, int - User-set NEW values take precedence over conflicting OLD transitional values """ @@ -16,3 +17,9 @@ def test(conf): # Check that the configuration matches expected output assert conf.config_contains('expected_config') + + # Test oldconfig to ensure symbols with transitional defaults are not prompted + assert conf.oldconfig(dot_config='initial_config', in_keys='n\n') == 0 + + # Except for when conditional default evaluates to 'no' + assert conf.stdout_contains('expected_stdout') diff --git a/scripts/kconfig/tests/transitional/expected_config b/scripts/kconfig/tests/transitional/expected_config index 846e9ddcab91..e01f5f070a26 100644 --- a/scripts/kconfig/tests/transitional/expected_config +++ b/scripts/kconfig/tests/transitional/expected_config @@ -9,4 +9,7 @@ CONFIG_NEW_STRING_PRECEDENCE="user value" CONFIG_NEW_TRISTATE_PRECEDENCE=y CONFIG_NEW_HEX_PRECEDENCE=0xABCD CONFIG_NEW_INT_PRECEDENCE=100 +# CONFIG_NEW_DISABLED is not set +# CONFIG_DEPENDENCY_TEST is not set +# CONFIG_NEW_CONDITIONAL_DEFAULT is not set # CONFIG_REGULAR_OPTION is not set diff --git a/scripts/kconfig/tests/transitional/expected_stdout b/scripts/kconfig/tests/transitional/expected_stdout new file mode 100644 index 000000000000..6f0b285d6469 --- /dev/null +++ b/scripts/kconfig/tests/transitional/expected_stdout @@ -0,0 +1 @@ +New option with conditional default (NEW_CONDITIONAL_DEFAULT) [N/y/?] (NEW) n diff --git a/scripts/kconfig/tests/transitional/initial_config b/scripts/kconfig/tests/transitional/initial_config index e648a65e504c..68b7da672426 100644 --- a/scripts/kconfig/tests/transitional/initial_config +++ b/scripts/kconfig/tests/transitional/initial_config @@ -14,3 +14,7 @@ CONFIG_NEW_HEX_PRECEDENCE=0xABCD CONFIG_OLD_HEX_PRECEDENCE=0x5678 CONFIG_NEW_INT_PRECEDENCE=100 CONFIG_OLD_INT_PRECEDENCE=200 +# CONFIG_OLD_DISABLED is not set +# CONFIG_OLD_DISABLED_UNSAVED is not set +# CONFIG_DEPENDENCY_TEST is not set +CONFIG_OLD_CONDITIONAL_DEFAULT=y -- cgit v1.2.3 From 7ded7d37e5f5b36b4acd74380156cf07b6640c5b Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 6 Oct 2025 14:39:56 -0700 Subject: scripts/Makefile.extrawarn: Respect CONFIG_WERROR / W=e for hostprogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 27758d8c2583 ("kbuild: enable -Werror for hostprogs") unconditionally enabled -Werror for the compiler, assembler, and linker when building the host programs, as the build footprint of the host programs is small (thus risk of build failures from warnings are low) and that stage of the build may not have Kconfig values (thus CONFIG_WERROR could not be used as a precondition). While turning warnings into errors unconditionally happens in a few places within the kernel, it can be disruptive to people who may be building with newer compilers, such as while doing a bisect. While it is possible to avoid this behavior by passing HOSTCFLAGS=-w or HOSTCFLAGS=-Wno-error, it may not be the most intuitive for regular users not intimately familiar with Kbuild. Avoid being disruptive to the entire build by depending on the explicit opt-in of CONFIG_WERROR or W=e to enable -Werror and the like while building the host programs. While this means there is a small portion of the build that does not have -Werror enabled (namely scripts/kconfig/* and scripts/basic/fixdep), it is better than not having it altogether. Fixes: 27758d8c2583 ("kbuild: enable -Werror for hostprogs") Acked-by: Miguel Ojeda Reported-by: Askar Safin Closes: https://lore.kernel.org/20251005011100.1035272-1-safinaskar@gmail.com/ Reviewed-by: Thomas Weißschuh Tested-by: Miguel Ojeda # Rust Link: https://patch.msgid.link/20251006-kbuild-hostprogs-werror-fix-v1-1-23cf1ffced5c@kernel.org Signed-off-by: Nathan Chancellor --- scripts/Makefile.extrawarn | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn index 1434cb6208cb..6af392f9cd02 100644 --- a/scripts/Makefile.extrawarn +++ b/scripts/Makefile.extrawarn @@ -223,9 +223,11 @@ KBUILD_USERCFLAGS += -Werror KBUILD_USERLDFLAGS += -Wl,--fatal-warnings KBUILD_RUSTFLAGS += -Dwarnings -endif - -# Hostprog flags are used during build bootstrapping and can not rely on CONFIG_ symbols. +# While hostprog flags are used during build bootstrapping (thus should not +# depend on CONFIG_ symbols), -Werror is disruptive and should be opted into. +# Only apply -Werror to hostprogs built after the initial Kconfig stage. KBUILD_HOSTCFLAGS += -Werror KBUILD_HOSTLDFLAGS += -Wl,--fatal-warnings KBUILD_HOSTRUSTFLAGS += -Dwarnings + +endif -- cgit v1.2.3 From 4b47a3aefb29c523ca66f0d28de8db15a10f9352 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 8 Oct 2025 15:46:44 -0700 Subject: kbuild: Restore pattern to avoid stripping .rela.dyn from vmlinux Commit 0ce5139fd96e ("kbuild: always create intermediate vmlinux.unstripped") removed the pattern to avoid stripping .rela.dyn sections added by commit e9d86b8e17e7 ("scripts: Do not strip .rela.dyn section"). Restore it so that .rela.dyn sections remain in the final vmlinux. Fixes: 0ce5139fd96e ("kbuild: always create intermediate vmlinux.unstripped") Acked-by: Ard Biesheuvel Acked-by: Alexey Gladkov Acked-by: Nicolas Schier Link: https://patch.msgid.link/20251008-kbuild-fix-modinfo-regressions-v1-1-9fc776c5887c@kernel.org Signed-off-by: Nathan Chancellor --- scripts/Makefile.vmlinux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux index 7c6ae9886f8f..a62639982be5 100644 --- a/scripts/Makefile.vmlinux +++ b/scripts/Makefile.vmlinux @@ -82,7 +82,7 @@ endif # --------------------------------------------------------------------------- remove-section-y := .modinfo -remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel*' +remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel*' '!.rel*.dyn' remove-symbols := -w --strip-symbol='__mod_device_table__*' -- cgit v1.2.3 From 8ec3af916fe3954381cf3555ea03dc5adf4d0e8e Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 8 Oct 2025 15:46:45 -0700 Subject: kbuild: Add '.rel.*' strip pattern for vmlinux Prior to binutils commit c12d9fa2afe ("Support objcopy --remove-section=.relaFOO") [1] in 2.32, stripping relocation sections required the trailing period (i.e., '.rel.*') to work properly. After commit 3e86e4d74c04 ("kbuild: keep .modinfo section in vmlinux.unstripped"), there is an error with binutils 2.31.1 or earlier because these sections are not properly removed: s390-linux-objcopy: st6tO8Ev: symbol `.modinfo' required but not present s390-linux-objcopy:st6tO8Ev: no symbols Add the old pattern to resolve this issue (along with a comment to allow cleaning this when binutils 2.32 or newer is the minimum supported version). While the aforementioned kbuild change exposes this, the pattern was originally changed by commit 71d815bf5dfd ("kbuild: Strip runtime const RELA sections correctly"), where it would still be incorrect with binutils older than 2.32. Fixes: 71d815bf5dfd ("kbuild: Strip runtime const RELA sections correctly") Link: https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=c12d9fa2afe7abcbe407a00e15719e1a1350c2a7 [1] Reported-by: Linux Kernel Functional Testing Closes: https://lore.kernel.org/CA+G9fYvVktRhFtZXdNgVOL8j+ArsJDpvMLgCitaQvQmCx=hwOQ@mail.gmail.com/ Acked-by: Ard Biesheuvel Acked-by: Alexey Gladkov Acked-by: Nicolas Schier Link: https://patch.msgid.link/20251008-kbuild-fix-modinfo-regressions-v1-2-9fc776c5887c@kernel.org Signed-off-by: Nathan Chancellor --- scripts/Makefile.vmlinux | 3 +++ 1 file changed, 3 insertions(+) (limited to 'scripts') diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux index a62639982be5..c02f85c2e241 100644 --- a/scripts/Makefile.vmlinux +++ b/scripts/Makefile.vmlinux @@ -83,6 +83,9 @@ endif remove-section-y := .modinfo remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel*' '!.rel*.dyn' +# for compatibility with binutils < 2.32 +# https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=c12d9fa2afe7abcbe407a00e15719e1a1350c2a7 +remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel.*' remove-symbols := -w --strip-symbol='__mod_device_table__*' -- cgit v1.2.3 From b0f2942a16017f88395d768afedd7373860968ce Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Fri, 10 Oct 2025 14:49:27 -0700 Subject: kbuild: Use '--strip-unneeded-symbol' for removing module device table symbols After commit 5ab23c7923a1 ("modpost: Create modalias for builtin modules"), relocatable RISC-V kernels with CONFIG_KASAN=y start failing when attempting to strip the module device table symbols: riscv64-linux-objcopy: not stripping symbol `__mod_device_table__kmod_irq_starfive_jh8100_intc__of__starfive_intc_irqchip_match_table' because it is named in a relocation make[4]: *** [scripts/Makefile.vmlinux:97: vmlinux] Error 1 The relocation appears to come from .LASANLOC5 in .data.rel.local: $ llvm-objdump --disassemble-symbols=.LASANLOC5 --disassemble-all -r drivers/irqchip/irq-starfive-jh8100-intc.o drivers/irqchip/irq-starfive-jh8100-intc.o: file format elf64-littleriscv Disassembly of section .data.rel.local: 0000000000000180 <.LASANLOC5>: ... 1d0: 0000 unimp 00000000000001d0: R_RISCV_64 __mod_device_table__kmod_irq_starfive_jh8100_intc__of__starfive_intc_irqchip_match_table ... This section appears to come from GCC for including additional information about global variables that may be protected by KASAN. There appears to be no way to opt out of the generation of these symbols through either a flag or attribute. Attempting to remove '.LASANLOC*' with '--strip-symbol' results in the same error as above because these symbols may refer to (thus have relocation between) each other. Avoid this build breakage by switching to '--strip-unneeded-symbol' for removing __mod_device_table__ symbols, as it will only remove the symbol when there is no relocation pointing to it. While this may result in a little more bloat in the symbol table in certain configurations, it is not as bad as outright build failures. Fixes: 5ab23c7923a1 ("modpost: Create modalias for builtin modules") Reported-by: Charles Mirabile Closes: https://lore.kernel.org/20251007011637.2512413-1-cmirabil@redhat.com/ Suggested-by: Alexey Gladkov Tested-by: Nicolas Schier Signed-off-by: Nathan Chancellor --- scripts/Makefile.vmlinux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux index c02f85c2e241..ced4379550d7 100644 --- a/scripts/Makefile.vmlinux +++ b/scripts/Makefile.vmlinux @@ -87,7 +87,7 @@ remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel*' '!.rel*.dyn' # https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=c12d9fa2afe7abcbe407a00e15719e1a1350c2a7 remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel.*' -remove-symbols := -w --strip-symbol='__mod_device_table__*' +remove-symbols := -w --strip-unneeded-symbol='__mod_device_table__*' # To avoid warnings: "empty loadable segment detected at ..." from GNU objcopy, # it is necessary to remove the PT_LOAD flag from the segment. -- cgit v1.2.3 From 567f9c428f99560fe14e647def9f42f5344ebde9 Mon Sep 17 00:00:00 2001 From: John Wang Date: Fri, 28 Mar 2025 15:38:02 +0800 Subject: scripts/faddr2line: Set LANG=C to enforce ASCII output Force tools like readelf to use the POSIX/C locale by exporting LANG=C This ensures ASCII-only output and avoids locale-specific characters(e.g., UTF-8 symbols or translated strings), which could break text processing utilities like sed in the script Signed-off-by: John Wang Signed-off-by: Josh Poimboeuf --- scripts/faddr2line | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'scripts') diff --git a/scripts/faddr2line b/scripts/faddr2line index 1fa6beef9f97..1f364fbb0cd8 100755 --- a/scripts/faddr2line +++ b/scripts/faddr2line @@ -76,6 +76,10 @@ ADDR2LINE="${UTIL_PREFIX}addr2line${UTIL_SUFFIX}" AWK="awk" GREP="grep" +# Enforce ASCII-only output from tools like readelf +# ensuring sed processes strings correctly. +export LANG=C + command -v ${AWK} >/dev/null 2>&1 || die "${AWK} isn't installed" command -v ${READELF} >/dev/null 2>&1 || die "${READELF} isn't installed" command -v ${ADDR2LINE} >/dev/null 2>&1 || die "${ADDR2LINE} isn't installed" -- cgit v1.2.3 From 6b4679fcbfdf6f27f8455f9c7050ab6c46c6c5e0 Mon Sep 17 00:00:00 2001 From: Pankaj Raghav Date: Sun, 21 Sep 2025 12:03:57 +0200 Subject: scripts/faddr2line: Use /usr/bin/env bash for portability The shebang `#!/bin/bash` assumes a fixed path for the bash interpreter. This path does not exist on some systems, such as NixOS, causing the script to fail. Replace `/bin/bash` with the more portable `#!/usr/bin/env bash`. Signed-off-by: Pankaj Raghav Signed-off-by: Josh Poimboeuf --- scripts/faddr2line | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/faddr2line b/scripts/faddr2line index 1f364fbb0cd8..7746d4ad0bfa 100755 --- a/scripts/faddr2line +++ b/scripts/faddr2line @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # SPDX-License-Identifier: GPL-2.0 # # Translate stack dump function offsets. -- cgit v1.2.3 From ff5c0466486ba8d07ab2700380e8fd6d5344b4e9 Mon Sep 17 00:00:00 2001 From: Pankaj Raghav Date: Sun, 21 Sep 2025 12:03:58 +0200 Subject: scripts/faddr2line: Fix "Argument list too long" error The run_readelf() function reads the entire output of readelf into a single shell variable. For large object files with extensive debug information, the size of this variable can exceed the system's command-line argument length limit. When this variable is subsequently passed to sed via `echo "${out}"`, it triggers an "Argument list too long" error, causing the script to fail. Fix this by redirecting the output of readelf to a temporary file instead of a variable. The sed commands are then modified to read from this file, avoiding the argument length limitation entirely. Signed-off-by: Pankaj Raghav Signed-off-by: Josh Poimboeuf --- scripts/faddr2line | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/faddr2line b/scripts/faddr2line index 7746d4ad0bfa..622875396bcf 100755 --- a/scripts/faddr2line +++ b/scripts/faddr2line @@ -111,14 +111,19 @@ find_dir_prefix() { run_readelf() { local objfile=$1 - local out=$(${READELF} --file-header --section-headers --symbols --wide $objfile) + local tmpfile + tmpfile=$(mktemp) + + ${READELF} --file-header --section-headers --symbols --wide "$objfile" > "$tmpfile" # This assumes that readelf first prints the file header, then the section headers, then the symbols. # Note: It seems that GNU readelf does not prefix section headers with the "There are X section headers" # line when multiple options are given, so let's also match with the "Section Headers:" line. - ELF_FILEHEADER=$(echo "${out}" | sed -n '/There are [0-9]* section headers, starting at offset\|Section Headers:/q;p') - ELF_SECHEADERS=$(echo "${out}" | sed -n '/There are [0-9]* section headers, starting at offset\|Section Headers:/,$p' | sed -n '/Symbol table .* contains [0-9]* entries:/q;p') - ELF_SYMS=$(echo "${out}" | sed -n '/Symbol table .* contains [0-9]* entries:/,$p') + ELF_FILEHEADER=$(sed -n '/There are [0-9]* section headers, starting at offset\|Section Headers:/q;p' "$tmpfile") + ELF_SECHEADERS=$(sed -n '/There are [0-9]* section headers, starting at offset\|Section Headers:/,$p' "$tmpfile" | sed -n '/Symbol table .* contains [0-9]* entries:/q;p') + ELF_SYMS=$(sed -n '/Symbol table .* contains [0-9]* entries:/,$p' "$tmpfile") + + rm -f -- "$tmpfile" } check_vmlinux() { -- cgit v1.2.3 From 1ba9f8979426590367406c70c1c821f5b943f993 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 17 Sep 2025 09:03:10 -0700 Subject: vmlinux.lds: Unify TEXT_MAIN, DATA_MAIN, and related macros TEXT_MAIN, DATA_MAIN and friends are defined differently depending on whether certain config options enable -ffunction-sections and/or -fdata-sections. There's no technical reason for that beyond voodoo coding. Keeping the separate implementations adds unnecessary complexity, fragments the logic, and increases the risk of subtle bugs. Unify the macros by using the same input section patterns across all configs. This is a prerequisite for the upcoming livepatch klp-build tooling which will manually enable -ffunction-sections and -fdata-sections via KCFLAGS. Cc: Heiko Carstens Cc: Vasily Gorbik Cc: Alexander Gordeev Acked-by: Petr Mladek Tested-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- include/asm-generic/vmlinux.lds.h | 40 ++++++++++++--------------------------- scripts/module.lds.S | 12 +++++------- 2 files changed, 17 insertions(+), 35 deletions(-) (limited to 'scripts') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 8a9a2e732a65..5facbc994634 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -87,39 +87,24 @@ #define ALIGN_FUNCTION() . = ALIGN(CONFIG_FUNCTION_ALIGNMENT) /* - * LD_DEAD_CODE_DATA_ELIMINATION option enables -fdata-sections, which - * generates .data.identifier sections, which need to be pulled in with - * .data. We don't want to pull in .data..other sections, which Linux - * has defined. Same for text and bss. + * Support -ffunction-sections by matching .text and .text.*, + * but exclude '.text..*'. * - * With LTO_CLANG, the linker also splits sections by default, so we need - * these macros to combine the sections during the final link. - * - * With AUTOFDO_CLANG and PROPELLER_CLANG, by default, the linker splits - * text sections and regroups functions into subsections. - * - * RODATA_MAIN is not used because existing code already defines .rodata.x - * sections to be brought in with rodata. + * Special .text.* sections that are typically grouped separately, such as + * .text.unlikely or .text.hot, must be matched explicitly before using + * TEXT_MAIN. */ -#if defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || defined(CONFIG_LTO_CLANG) || \ -defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG) #define TEXT_MAIN .text .text.[0-9a-zA-Z_]* -#else -#define TEXT_MAIN .text -#endif -#if defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION) || defined(CONFIG_LTO_CLANG) + +/* + * Support -fdata-sections by matching .data, .data.*, and others, + * but exclude '.data..*'. + */ #define DATA_MAIN .data .data.[0-9a-zA-Z_]* .data.rel.* .data..L* .data..compoundliteral* .data.$__unnamed_* .data.$L* #define SDATA_MAIN .sdata .sdata.[0-9a-zA-Z_]* #define RODATA_MAIN .rodata .rodata.[0-9a-zA-Z_]* .rodata..L* #define BSS_MAIN .bss .bss.[0-9a-zA-Z_]* .bss..L* .bss..compoundliteral* #define SBSS_MAIN .sbss .sbss.[0-9a-zA-Z_]* -#else -#define DATA_MAIN .data .data.rel .data.rel.local -#define SDATA_MAIN .sdata -#define RODATA_MAIN .rodata -#define BSS_MAIN .bss -#define SBSS_MAIN .sbss -#endif /* * GCC 4.5 and later have a 32 bytes section alignment for structures. @@ -581,9 +566,8 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG) * during second ld run in second ld pass when generating System.map * * TEXT_MAIN here will match symbols with a fixed pattern (for example, - * .text.hot or .text.unlikely) if dead code elimination or - * function-section is enabled. Match these symbols first before - * TEXT_MAIN to ensure they are grouped together. + * .text.hot or .text.unlikely). Match those before TEXT_MAIN to ensure + * they get grouped together. * * Also placing .text.hot section at the beginning of a page, this * would help the TLB performance. diff --git a/scripts/module.lds.S b/scripts/module.lds.S index ee79c41059f3..2632c6cb8ebe 100644 --- a/scripts/module.lds.S +++ b/scripts/module.lds.S @@ -38,12 +38,10 @@ SECTIONS { __kcfi_traps : { KEEP(*(.kcfi_traps)) } #endif -#ifdef CONFIG_LTO_CLANG - /* - * With CONFIG_LTO_CLANG, LLD always enables -fdata-sections and - * -ffunction-sections, which increases the size of the final module. - * Merge the split sections in the final binary. - */ + .text : { + *(.text .text.[0-9a-zA-Z_]*) + } + .bss : { *(.bss .bss.[0-9a-zA-Z_]*) *(.bss..L*) @@ -58,7 +56,7 @@ SECTIONS { *(.rodata .rodata.[0-9a-zA-Z_]*) *(.rodata..L*) } -#endif + MOD_SEPARATE_CODETAG_SECTIONS() } -- cgit v1.2.3 From 6717e8f91db71641cb52855ed14c7900972ed0bc Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 17 Sep 2025 09:03:16 -0700 Subject: kbuild: Remove 'kmod_' prefix from __KBUILD_MODNAME In preparation for the objtool klp diff subcommand, remove the arbitrary 'kmod_' prefix from __KBUILD_MODNAME and instead add it explicitly in the __initcall_id() macro. This change supports the standardization of "unique" symbol naming by ensuring the non-unique portion of the name comes before the unique part. That will enable objtool to properly correlate symbols across builds. Cc: Masahiro Yamada Acked-by: Petr Mladek Tested-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- include/linux/init.h | 3 ++- scripts/Makefile.lib | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/include/linux/init.h b/include/linux/init.h index 17c1bc712e23..40331923b9f4 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -200,12 +200,13 @@ extern struct module __this_module; /* Format: ____ */ #define __initcall_id(fn) \ + __PASTE(kmod_, \ __PASTE(__KBUILD_MODNAME, \ __PASTE(__, \ __PASTE(__COUNTER__, \ __PASTE(_, \ __PASTE(__LINE__, \ - __PASTE(_, fn)))))) + __PASTE(_, fn))))))) /* Format: ____ */ #define __initcall_name(prefix, __iid, id) \ diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 1d581ba5df66..b95560266124 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -20,7 +20,7 @@ name-fix-token = $(subst $(comma),_,$(subst -,_,$1)) name-fix = $(call stringify,$(call name-fix-token,$1)) basename_flags = -DKBUILD_BASENAME=$(call name-fix,$(basetarget)) modname_flags = -DKBUILD_MODNAME=$(call name-fix,$(modname)) \ - -D__KBUILD_MODNAME=kmod_$(call name-fix-token,$(modname)) + -D__KBUILD_MODNAME=$(call name-fix-token,$(modname)) modfile_flags = -DKBUILD_MODFILE=$(call stringify,$(modfile)) _c_flags = $(filter-out $(CFLAGS_REMOVE_$(target-stem).o), \ -- cgit v1.2.3 From 4109043bff31f95d3da9ace33eb3c1925fd62cbd Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 17 Sep 2025 09:03:17 -0700 Subject: modpost: Ignore unresolved section bounds symbols In preparation for klp-build livepatch module creation tooling, suppress warnings for unresolved references to linker-generated __start_* and __stop_* section bounds symbols. These symbols are expected to be undefined when modpost runs, as they're created later by the linker. Cc: Masahiro Yamada Acked-by: Petr Mladek Tested-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- scripts/mod/modpost.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'scripts') diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 47c8aa2a6939..755b842f1f9b 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -606,6 +606,11 @@ static int ignore_undef_symbol(struct elf_info *info, const char *symname) strstarts(symname, "_savevr_") || strcmp(symname, ".TOC.") == 0) return 1; + + /* ignore linker-created section bounds variables */ + if (strstarts(symname, "__start_") || strstarts(symname, "__stop_")) + return 1; + /* Do not ignore this symbol */ return 0; } -- cgit v1.2.3 From 56754f0f46f6a36ba66e8c1b2878f7a4f1edfe3b Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 17 Sep 2025 09:03:42 -0700 Subject: objtool: Rename --Werror to --werror The objtool --Werror option name is stylistically inconsistent: halfway between GCC's single-dash capitalized -Werror and objtool's double-dash --lowercase convention, making it unnecessarily hard to remember. Make the 'W' lower case (--werror) for consistency with objtool's other options. Acked-by: Petr Mladek Tested-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- scripts/Makefile.lib | 2 +- scripts/Makefile.vmlinux_o | 2 +- tools/objtool/builtin-check.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index b95560266124..15fee73e9289 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -191,7 +191,7 @@ objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES) -objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror +objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror objtool-args = $(objtool-args-y) \ $(if $(delay-objtool), --link) \ diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o index 23c8751285d7..20533cc0b1ee 100644 --- a/scripts/Makefile.vmlinux_o +++ b/scripts/Makefile.vmlinux_o @@ -41,7 +41,7 @@ objtool-enabled := $(or $(delay-objtool),$(CONFIG_NOINSTR_VALIDATION)) ifeq ($(delay-objtool),y) vmlinux-objtool-args-y += $(objtool-args-y) else -vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror +vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror endif vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \ diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index fcd4a6517896..2aa28af8fb09 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -101,7 +101,7 @@ static const struct option check_options[] = { OPT_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"), OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"), OPT_BOOLEAN('v', "verbose", &opts.verbose, "verbose warnings"), - OPT_BOOLEAN(0, "Werror", &opts.werror, "return error on warnings"), + OPT_BOOLEAN(0, "werror", &opts.werror, "return error on warnings"), OPT_END(), }; -- cgit v1.2.3 From dd590d4d57ebeeb826823c288741f2ed20f452af Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 17 Sep 2025 09:03:59 -0700 Subject: objtool/klp: Introduce klp diff subcommand for diffing object files Add a new klp diff subcommand which performs a binary diff between two object files and extracts changed functions into a new object which can then be linked into a livepatch module. This builds on concepts from the longstanding out-of-tree kpatch [1] project which began in 2012 and has been used for many years to generate livepatch modules for production kernels. However, this is a complete rewrite which incorporates hard-earned lessons from 12+ years of maintaining kpatch. Key improvements compared to kpatch-build: - Integrated with objtool: Leverages objtool's existing control-flow graph analysis to help detect changed functions. - Works on vmlinux.o: Supports late-linked objects, making it compatible with LTO, IBT, and similar. - Simplified code base: ~3k fewer lines of code. - Upstream: No more out-of-tree #ifdef hacks, far less cruft. - Cleaner internals: Vastly simplified logic for symbol/section/reloc inclusion and special section extraction. - Robust __LINE__ macro handling: Avoids false positive binary diffs caused by the __LINE__ macro by introducing a fix-patch-lines script (coming in a later patch) which injects #line directives into the source .patch to preserve the original line numbers at compile time. Note the end result of this subcommand is not yet functionally complete. Livepatch needs some ELF magic which linkers don't like: - Two relocation sections (.rela*, .klp.rela*) for the same text section. - Use of SHN_LIVEPATCH to mark livepatch symbols. Unfortunately linkers tend to mangle such things. To work around that, klp diff generates a linker-compliant intermediate binary which encodes the relevant KLP section/reloc/symbol metadata. After module linking, a klp post-link step (coming soon) will clean up the mess and convert the linked .ko into a fully compliant livepatch module. Note this subcommand requires the diffed binaries to have been compiled with -ffunction-sections and -fdata-sections, and processed with 'objtool --checksum'. Those constraints will be handled by a klp-build script introduced in a later patch. Without '-ffunction-sections -fdata-sections', reliable object diffing would be infeasible due to toolchain limitations: - For intra-file+intra-section references, the compiler might occasionally generated hard-coded instruction offsets instead of relocations. - Section-symbol-based references can be ambiguous: - Overlapping or zero-length symbols create ambiguity as to which symbol is being referenced. - A reference to the end of a symbol (e.g., checking array bounds) can be misinterpreted as a reference to the next symbol, or vice versa. A potential future alternative to '-ffunction-sections -fdata-sections' would be to introduce a toolchain option that forces symbol-based (non-section) relocations. Acked-by: Petr Mladek Tested-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- MAINTAINERS | 2 +- include/linux/livepatch.h | 25 +- include/linux/livepatch_external.h | 76 ++ kernel/livepatch/core.c | 4 +- scripts/module.lds.S | 10 +- tools/include/linux/livepatch_external.h | 76 ++ tools/include/linux/string.h | 14 + tools/objtool/Build | 4 +- tools/objtool/Makefile | 3 +- tools/objtool/arch/x86/decode.c | 40 + tools/objtool/builtin-klp.c | 52 + tools/objtool/check.c | 14 - tools/objtool/elf.c | 21 +- tools/objtool/include/objtool/arch.h | 1 + tools/objtool/include/objtool/builtin.h | 2 + tools/objtool/include/objtool/elf.h | 56 +- tools/objtool/include/objtool/klp.h | 31 + tools/objtool/include/objtool/objtool.h | 2 + tools/objtool/include/objtool/util.h | 19 + tools/objtool/klp-diff.c | 1646 ++++++++++++++++++++++++++++++ tools/objtool/objtool.c | 41 +- tools/objtool/sync-check.sh | 1 + tools/objtool/weak.c | 7 + 23 files changed, 2088 insertions(+), 59 deletions(-) create mode 100644 include/linux/livepatch_external.h create mode 100644 tools/include/linux/livepatch_external.h create mode 100644 tools/objtool/builtin-klp.c create mode 100644 tools/objtool/include/objtool/klp.h create mode 100644 tools/objtool/include/objtool/util.h create mode 100644 tools/objtool/klp-diff.c (limited to 'scripts') diff --git a/MAINTAINERS b/MAINTAINERS index 46126ce2f968..755e2528f839 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14439,7 +14439,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching.g F: Documentation/ABI/testing/sysfs-kernel-livepatch F: Documentation/livepatch/ F: arch/powerpc/include/asm/livepatch.h -F: include/linux/livepatch.h +F: include/linux/livepatch*.h F: kernel/livepatch/ F: kernel/module/livepatch.c F: samples/livepatch/ diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index 51a258c24ff5..772919e8096a 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #if IS_ENABLED(CONFIG_LIVEPATCH) @@ -77,30 +78,6 @@ struct klp_func { bool transition; }; -struct klp_object; - -/** - * struct klp_callbacks - pre/post live-(un)patch callback structure - * @pre_patch: executed before code patching - * @post_patch: executed after code patching - * @pre_unpatch: executed before code unpatching - * @post_unpatch: executed after code unpatching - * @post_unpatch_enabled: flag indicating if post-unpatch callback - * should run - * - * All callbacks are optional. Only the pre-patch callback, if provided, - * will be unconditionally executed. If the parent klp_object fails to - * patch for any reason, including a non-zero error status returned from - * the pre-patch callback, no further callbacks will be executed. - */ -struct klp_callbacks { - int (*pre_patch)(struct klp_object *obj); - void (*post_patch)(struct klp_object *obj); - void (*pre_unpatch)(struct klp_object *obj); - void (*post_unpatch)(struct klp_object *obj); - bool post_unpatch_enabled; -}; - /** * struct klp_object - kernel object structure for live patching * @name: module name (or NULL for vmlinux) diff --git a/include/linux/livepatch_external.h b/include/linux/livepatch_external.h new file mode 100644 index 000000000000..138af19b0f5c --- /dev/null +++ b/include/linux/livepatch_external.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * External livepatch interfaces for patch creation tooling + */ + +#ifndef _LINUX_LIVEPATCH_EXTERNAL_H_ +#define _LINUX_LIVEPATCH_EXTERNAL_H_ + +#include + +#define KLP_RELOC_SEC_PREFIX ".klp.rela." +#define KLP_SYM_PREFIX ".klp.sym." + +#define __KLP_PRE_PATCH_PREFIX __klp_pre_patch_callback_ +#define __KLP_POST_PATCH_PREFIX __klp_post_patch_callback_ +#define __KLP_PRE_UNPATCH_PREFIX __klp_pre_unpatch_callback_ +#define __KLP_POST_UNPATCH_PREFIX __klp_post_unpatch_callback_ + +#define KLP_PRE_PATCH_PREFIX __stringify(__KLP_PRE_PATCH_PREFIX) +#define KLP_POST_PATCH_PREFIX __stringify(__KLP_POST_PATCH_PREFIX) +#define KLP_PRE_UNPATCH_PREFIX __stringify(__KLP_PRE_UNPATCH_PREFIX) +#define KLP_POST_UNPATCH_PREFIX __stringify(__KLP_POST_UNPATCH_PREFIX) + +struct klp_object; + +typedef int (*klp_pre_patch_t)(struct klp_object *obj); +typedef void (*klp_post_patch_t)(struct klp_object *obj); +typedef void (*klp_pre_unpatch_t)(struct klp_object *obj); +typedef void (*klp_post_unpatch_t)(struct klp_object *obj); + +/** + * struct klp_callbacks - pre/post live-(un)patch callback structure + * @pre_patch: executed before code patching + * @post_patch: executed after code patching + * @pre_unpatch: executed before code unpatching + * @post_unpatch: executed after code unpatching + * @post_unpatch_enabled: flag indicating if post-unpatch callback + * should run + * + * All callbacks are optional. Only the pre-patch callback, if provided, + * will be unconditionally executed. If the parent klp_object fails to + * patch for any reason, including a non-zero error status returned from + * the pre-patch callback, no further callbacks will be executed. + */ +struct klp_callbacks { + klp_pre_patch_t pre_patch; + klp_post_patch_t post_patch; + klp_pre_unpatch_t pre_unpatch; + klp_post_unpatch_t post_unpatch; + bool post_unpatch_enabled; +}; + +/* + * 'struct klp_{func,object}_ext' are compact "external" representations of + * 'struct klp_{func,object}'. They are used by objtool for livepatch + * generation. The structs are then read by the livepatch module and converted + * to the real structs before calling klp_enable_patch(). + * + * TODO make these the official API for klp_enable_patch(). That should + * simplify livepatch's interface as well as its data structure lifetime + * management. + */ +struct klp_func_ext { + const char *old_name; + void *new_func; + unsigned long sympos; +}; + +struct klp_object_ext { + const char *name; + struct klp_func_ext *funcs; + struct klp_callbacks callbacks; + unsigned int nr_funcs; +}; + +#endif /* _LINUX_LIVEPATCH_EXTERNAL_H_ */ diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 7e443c2cf7d4..0044a8125013 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -224,7 +224,7 @@ static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab, /* Format: .klp.sym.sym_objname.sym_name,sympos */ cnt = sscanf(strtab + sym->st_name, - ".klp.sym.%55[^.].%511[^,],%lu", + KLP_SYM_PREFIX "%55[^.].%511[^,],%lu", sym_objname, sym_name, &sympos); if (cnt != 3) { pr_err("symbol %s has an incorrectly formatted name\n", @@ -303,7 +303,7 @@ static int klp_write_section_relocs(struct module *pmod, Elf_Shdr *sechdrs, * See comment in klp_resolve_symbols() for an explanation * of the selected field width value. */ - cnt = sscanf(shstrtab + sec->sh_name, ".klp.rela.%55[^.]", + cnt = sscanf(shstrtab + sec->sh_name, KLP_RELOC_SEC_PREFIX "%55[^.]", sec_objname); if (cnt != 1) { pr_err("section %s has an incorrectly formatted name\n", diff --git a/scripts/module.lds.S b/scripts/module.lds.S index 2632c6cb8ebe..3037d5e5527c 100644 --- a/scripts/module.lds.S +++ b/scripts/module.lds.S @@ -34,8 +34,16 @@ SECTIONS { __patchable_function_entries : { *(__patchable_function_entries) } + __klp_funcs 0: ALIGN(8) { KEEP(*(__klp_funcs)) } + + __klp_objects 0: ALIGN(8) { + __start_klp_objects = .; + KEEP(*(__klp_objects)) + __stop_klp_objects = .; + } + #ifdef CONFIG_ARCH_USES_CFI_TRAPS - __kcfi_traps : { KEEP(*(.kcfi_traps)) } + __kcfi_traps : { KEEP(*(.kcfi_traps)) } #endif .text : { diff --git a/tools/include/linux/livepatch_external.h b/tools/include/linux/livepatch_external.h new file mode 100644 index 000000000000..138af19b0f5c --- /dev/null +++ b/tools/include/linux/livepatch_external.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * External livepatch interfaces for patch creation tooling + */ + +#ifndef _LINUX_LIVEPATCH_EXTERNAL_H_ +#define _LINUX_LIVEPATCH_EXTERNAL_H_ + +#include + +#define KLP_RELOC_SEC_PREFIX ".klp.rela." +#define KLP_SYM_PREFIX ".klp.sym." + +#define __KLP_PRE_PATCH_PREFIX __klp_pre_patch_callback_ +#define __KLP_POST_PATCH_PREFIX __klp_post_patch_callback_ +#define __KLP_PRE_UNPATCH_PREFIX __klp_pre_unpatch_callback_ +#define __KLP_POST_UNPATCH_PREFIX __klp_post_unpatch_callback_ + +#define KLP_PRE_PATCH_PREFIX __stringify(__KLP_PRE_PATCH_PREFIX) +#define KLP_POST_PATCH_PREFIX __stringify(__KLP_POST_PATCH_PREFIX) +#define KLP_PRE_UNPATCH_PREFIX __stringify(__KLP_PRE_UNPATCH_PREFIX) +#define KLP_POST_UNPATCH_PREFIX __stringify(__KLP_POST_UNPATCH_PREFIX) + +struct klp_object; + +typedef int (*klp_pre_patch_t)(struct klp_object *obj); +typedef void (*klp_post_patch_t)(struct klp_object *obj); +typedef void (*klp_pre_unpatch_t)(struct klp_object *obj); +typedef void (*klp_post_unpatch_t)(struct klp_object *obj); + +/** + * struct klp_callbacks - pre/post live-(un)patch callback structure + * @pre_patch: executed before code patching + * @post_patch: executed after code patching + * @pre_unpatch: executed before code unpatching + * @post_unpatch: executed after code unpatching + * @post_unpatch_enabled: flag indicating if post-unpatch callback + * should run + * + * All callbacks are optional. Only the pre-patch callback, if provided, + * will be unconditionally executed. If the parent klp_object fails to + * patch for any reason, including a non-zero error status returned from + * the pre-patch callback, no further callbacks will be executed. + */ +struct klp_callbacks { + klp_pre_patch_t pre_patch; + klp_post_patch_t post_patch; + klp_pre_unpatch_t pre_unpatch; + klp_post_unpatch_t post_unpatch; + bool post_unpatch_enabled; +}; + +/* + * 'struct klp_{func,object}_ext' are compact "external" representations of + * 'struct klp_{func,object}'. They are used by objtool for livepatch + * generation. The structs are then read by the livepatch module and converted + * to the real structs before calling klp_enable_patch(). + * + * TODO make these the official API for klp_enable_patch(). That should + * simplify livepatch's interface as well as its data structure lifetime + * management. + */ +struct klp_func_ext { + const char *old_name; + void *new_func; + unsigned long sympos; +}; + +struct klp_object_ext { + const char *name; + struct klp_func_ext *funcs; + struct klp_callbacks callbacks; + unsigned int nr_funcs; +}; + +#endif /* _LINUX_LIVEPATCH_EXTERNAL_H_ */ diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h index 8499f509f03e..51ad3cf4fa82 100644 --- a/tools/include/linux/string.h +++ b/tools/include/linux/string.h @@ -44,6 +44,20 @@ static inline bool strstarts(const char *str, const char *prefix) return strncmp(str, prefix, strlen(prefix)) == 0; } +/* + * Checks if a string ends with another. + */ +static inline bool str_ends_with(const char *str, const char *substr) +{ + size_t len = strlen(str); + size_t sublen = strlen(substr); + + if (sublen > len) + return false; + + return !strcmp(str + len - sublen, substr); +} + extern char * __must_check skip_spaces(const char *); extern char *strim(char *); diff --git a/tools/objtool/Build b/tools/objtool/Build index a3cdf8af6635..0b01657671d7 100644 --- a/tools/objtool/Build +++ b/tools/objtool/Build @@ -8,8 +8,8 @@ objtool-y += builtin-check.o objtool-y += elf.o objtool-y += objtool.o -objtool-$(BUILD_ORC) += orc_gen.o -objtool-$(BUILD_ORC) += orc_dump.o +objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o +objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o objtool-y += libstring.o objtool-y += libctype.o diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index 958761c05b7c..48928c9bebef 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -15,13 +15,14 @@ ifeq ($(ARCH_HAS_KLP),y) HAVE_XXHASH = $(shell echo "int main() {}" | \ $(HOSTCC) -xc - -o /dev/null -lxxhash 2> /dev/null && echo y || echo n) ifeq ($(HAVE_XXHASH),y) + BUILD_KLP := y LIBXXHASH_CFLAGS := $(shell $(HOSTPKG_CONFIG) libxxhash --cflags 2>/dev/null) \ -DBUILD_KLP LIBXXHASH_LIBS := $(shell $(HOSTPKG_CONFIG) libxxhash --libs 2>/dev/null || echo -lxxhash) endif endif -export BUILD_ORC +export BUILD_ORC BUILD_KLP ifeq ($(srctree),) srctree := $(patsubst %/,%,$(dir $(CURDIR))) diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index b2c320f701f9..5c72beeaa3a7 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -88,6 +88,46 @@ s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc) return phys_to_virt(addend); } +static void scan_for_insn(struct section *sec, unsigned long offset, + unsigned long *insn_off, unsigned int *insn_len) +{ + unsigned long o = 0; + struct insn insn; + + while (1) { + + insn_decode(&insn, sec->data->d_buf + o, sec_size(sec) - o, + INSN_MODE_64); + + if (o + insn.length > offset) { + *insn_off = o; + *insn_len = insn.length; + return; + } + + o += insn.length; + } +} + +u64 arch_adjusted_addend(struct reloc *reloc) +{ + unsigned int type = reloc_type(reloc); + s64 addend = reloc_addend(reloc); + unsigned long insn_off; + unsigned int insn_len; + + if (type == R_X86_64_PLT32) + return addend + 4; + + if (type != R_X86_64_PC32 || !is_text_sec(reloc->sec->base)) + return addend; + + scan_for_insn(reloc->sec->base, reloc_offset(reloc), + &insn_off, &insn_len); + + return addend + insn_off + insn_len - reloc_offset(reloc); +} + unsigned long arch_jump_destination(struct instruction *insn) { return insn->offset + insn->len + insn->immediate; diff --git a/tools/objtool/builtin-klp.c b/tools/objtool/builtin-klp.c new file mode 100644 index 000000000000..9b13dd1182af --- /dev/null +++ b/tools/objtool/builtin-klp.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include +#include + +struct subcmd { + const char *name; + const char *description; + int (*fn)(int, const char **); +}; + +static struct subcmd subcmds[] = { + { "diff", "Generate binary diff of two object files", cmd_klp_diff, }, +}; + +static void cmd_klp_usage(void) +{ + fprintf(stderr, "usage: objtool klp []\n\n"); + fprintf(stderr, "Subcommands:\n"); + + for (int i = 0; i < ARRAY_SIZE(subcmds); i++) { + struct subcmd *cmd = &subcmds[i]; + + fprintf(stderr, " %s\t%s\n", cmd->name, cmd->description); + } + + exit(1); +} + +int cmd_klp(int argc, const char **argv) +{ + argc--; + argv++; + + if (!argc) + cmd_klp_usage(); + + if (argc) { + for (int i = 0; i < ARRAY_SIZE(subcmds); i++) { + struct subcmd *cmd = &subcmds[i]; + + if (!strcmp(cmd->name, argv[0])) + return cmd->fn(argc, argv); + } + } + + cmd_klp_usage(); + return 0; +} diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 0f5278127f37..8d17d930d0c8 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -185,20 +185,6 @@ static bool is_sibling_call(struct instruction *insn) return (is_static_jump(insn) && insn_call_dest(insn)); } -/* - * Checks if a string ends with another. - */ -static bool str_ends_with(const char *s, const char *sub) -{ - const int slen = strlen(s); - const int sublen = strlen(sub); - - if (sublen > slen) - return 0; - - return !memcmp(s + slen - sublen, sub, sublen); -} - /* * Checks if a function is a Rust "noreturn" one. */ diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 0119b3b4c554..e1daae0630be 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -288,6 +288,18 @@ struct symbol *find_symbol_by_name(const struct elf *elf, const char *name) return NULL; } +struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name) +{ + struct symbol *sym; + + elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) { + if (!strcmp(sym->name, name) && !is_local_sym(sym)) + return sym; + } + + return NULL; +} + struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, unsigned long offset, unsigned int len) { @@ -475,6 +487,8 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym) else entry = &sym->sec->symbol_list; list_add(&sym->list, entry); + + list_add_tail(&sym->global_list, &elf->symbols); elf_hash_add(symbol, &sym->hash, sym->idx); elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name)); @@ -531,6 +545,9 @@ static int read_symbols(struct elf *elf) ERROR_GLIBC("calloc"); return -1; } + + INIT_LIST_HEAD(&elf->symbols); + for (i = 0; i < symbols_nr; i++) { sym = &elf->symbol_data[i]; @@ -639,7 +656,7 @@ static int mark_group_syms(struct elf *elf) return -1; } - list_for_each_entry(sec, &elf->sections, list) { + for_each_sec(elf, sec) { if (sec->sh.sh_type == SHT_GROUP && sec->sh.sh_link == symtab->idx) { sym = find_symbol_by_index(elf, sec->sh.sh_info); @@ -1224,6 +1241,8 @@ struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name) return NULL; } + INIT_LIST_HEAD(&elf->symbols); + if (!elf_alloc_hash(section, 1000) || !elf_alloc_hash(section_name, 1000) || !elf_alloc_hash(symbol, 10000) || diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h index a4502947307a..d89f8b5ec14e 100644 --- a/tools/objtool/include/objtool/arch.h +++ b/tools/objtool/include/objtool/arch.h @@ -84,6 +84,7 @@ bool arch_callee_saved_reg(unsigned char reg); unsigned long arch_jump_destination(struct instruction *insn); s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc); +u64 arch_adjusted_addend(struct reloc *reloc); const char *arch_nop_insn(int len); const char *arch_ret_insn(int len); diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h index cee9fc031877..bb0b25eb08ba 100644 --- a/tools/objtool/include/objtool/builtin.h +++ b/tools/objtool/include/objtool/builtin.h @@ -53,4 +53,6 @@ int objtool_run(int argc, const char **argv); int make_backup(void); +int cmd_klp(int argc, const char **argv); + #endif /* _BUILTIN_H */ diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index a1f1762f89c4..e2cd817fca52 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -18,6 +18,7 @@ #include #include +#define SEC_NAME_LEN 1024 #define SYM_NAME_LEN 512 #define bswap_if_needed(elf, val) __bswap_if_needed(&elf->ehdr, val) @@ -53,10 +54,12 @@ struct section { bool _changed, text, rodata, noinstr, init, truncate; struct reloc *relocs; unsigned long nr_alloc_relocs; + struct section *twin; }; struct symbol { struct list_head list; + struct list_head global_list; struct rb_node node; struct elf_hash_node hash; struct elf_hash_node name_hash; @@ -83,10 +86,13 @@ struct symbol { u8 cold : 1; u8 prefix : 1; u8 debug_checksum : 1; + u8 changed : 1; + u8 included : 1; struct list_head pv_target; struct reloc *relocs; struct section *group_sec; struct checksum csum; + struct symbol *twin, *clone; }; struct reloc { @@ -104,6 +110,7 @@ struct elf { const char *name, *tmp_name; unsigned int num_files; struct list_head sections; + struct list_head symbols; unsigned long num_relocs; int symbol_bits; @@ -179,6 +186,7 @@ struct section *find_section_by_name(const struct elf *elf, const char *name); struct symbol *find_func_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_name(const struct elf *elf, const char *name); +struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name); struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset); int find_symbol_hole_containing(const struct section *sec, unsigned long offset); struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset); @@ -448,22 +456,48 @@ static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next) #define sec_for_each_sym(sec, sym) \ list_for_each_entry(sym, &sec->symbol_list, list) +#define sec_prev_sym(sym) \ + sym->sec && sym->list.prev != &sym->sec->symbol_list ? \ + list_prev_entry(sym, list) : NULL + #define for_each_sym(elf, sym) \ - for (struct section *__sec, *__fake = (struct section *)1; \ - __fake; __fake = NULL) \ - for_each_sec(elf, __sec) \ - sec_for_each_sym(__sec, sym) + list_for_each_entry(sym, &elf->symbols, global_list) + +#define for_each_sym_continue(elf, sym) \ + list_for_each_entry_continue(sym, &elf->symbols, global_list) + +#define rsec_next_reloc(rsec, reloc) \ + reloc_idx(reloc) < sec_num_entries(rsec) - 1 ? reloc + 1 : NULL #define for_each_reloc(rsec, reloc) \ - for (int __i = 0, __fake = 1; __fake; __fake = 0) \ - for (reloc = rsec->relocs; \ - __i < sec_num_entries(rsec); \ - __i++, reloc++) + for (reloc = rsec->relocs; reloc; reloc = rsec_next_reloc(rsec, reloc)) #define for_each_reloc_from(rsec, reloc) \ - for (int __i = reloc_idx(reloc); \ - __i < sec_num_entries(rsec); \ - __i++, reloc++) + for (; reloc; reloc = rsec_next_reloc(rsec, reloc)) + +#define for_each_reloc_continue(rsec, reloc) \ + for (reloc = rsec_next_reloc(rsec, reloc); reloc; \ + reloc = rsec_next_reloc(rsec, reloc)) + +#define sym_for_each_reloc(elf, sym, reloc) \ + for (reloc = find_reloc_by_dest_range(elf, sym->sec, \ + sym->offset, sym->len); \ + reloc && reloc_offset(reloc) < sym->offset + sym->len; \ + reloc = rsec_next_reloc(sym->sec->rsec, reloc)) + +static inline struct symbol *get_func_prefix(struct symbol *func) +{ + struct symbol *prev; + + if (!is_func_sym(func)) + return NULL; + + prev = sec_prev_sym(func); + if (prev && is_prefix_func(prev)) + return prev; + + return NULL; +} #define OFFSET_STRIDE_BITS 4 #define OFFSET_STRIDE (1UL << OFFSET_STRIDE_BITS) diff --git a/tools/objtool/include/objtool/klp.h b/tools/objtool/include/objtool/klp.h new file mode 100644 index 000000000000..07928fac059b --- /dev/null +++ b/tools/objtool/include/objtool/klp.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _OBJTOOL_KLP_H +#define _OBJTOOL_KLP_H + +/* + * __klp_objects and __klp_funcs are created by klp diff and used by the patch + * module init code to build the klp_patch, klp_object and klp_func structs + * needed by the livepatch API. + */ +#define KLP_OBJECTS_SEC "__klp_objects" +#define KLP_FUNCS_SEC "__klp_funcs" + +/* + * __klp_relocs is an intermediate section which are created by klp diff and + * converted into KLP symbols/relas by "objtool klp post-link". This is needed + * to work around the linker, which doesn't preserve SHN_LIVEPATCH or + * SHF_RELA_LIVEPATCH, nor does it support having two RELA sections for a + * single PROGBITS section. + */ +#define KLP_RELOCS_SEC "__klp_relocs" +#define KLP_STRINGS_SEC ".rodata.klp.str1.1" + +struct klp_reloc { + void *offset; + void *sym; + u32 type; +}; + +int cmd_klp_diff(int argc, const char **argv); + +#endif /* _OBJTOOL_KLP_H */ diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/include/objtool/objtool.h index c0dc86a78ff6..7f70b41d1b8d 100644 --- a/tools/objtool/include/objtool/objtool.h +++ b/tools/objtool/include/objtool/objtool.h @@ -39,6 +39,8 @@ struct objtool_file { struct pv_state *pv_ops; }; +char *top_level_dir(const char *file); + struct objtool_file *objtool_open_read(const char *_objname); int objtool_pv_add(struct objtool_file *file, int idx, struct symbol *func); diff --git a/tools/objtool/include/objtool/util.h b/tools/objtool/include/objtool/util.h new file mode 100644 index 000000000000..a0180b312f73 --- /dev/null +++ b/tools/objtool/include/objtool/util.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _UTIL_H +#define _UTIL_H + +#include + +#define snprintf_check(str, size, format, args...) \ +({ \ + int __ret = snprintf(str, size, format, args); \ + if (__ret < 0) \ + ERROR_GLIBC("snprintf"); \ + else if (__ret >= size) \ + ERROR("snprintf() failed for '" format "'", args); \ + else \ + __ret = 0; \ + __ret; \ +}) + +#endif /* _UTIL_H */ diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c new file mode 100644 index 000000000000..0d69b621a26c --- /dev/null +++ b/tools/objtool/klp-diff.c @@ -0,0 +1,1646 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#define _GNU_SOURCE /* memmem() */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) + +struct elfs { + struct elf *orig, *patched, *out; + const char *modname; +}; + +struct export { + struct hlist_node hash; + char *mod, *sym; +}; + +static const char * const klp_diff_usage[] = { + "objtool klp diff [] ", + NULL, +}; + +static const struct option klp_diff_options[] = { + OPT_END(), +}; + +static DEFINE_HASHTABLE(exports, 15); + +static inline u32 str_hash(const char *str) +{ + return jhash(str, strlen(str), 0); +} + +static int read_exports(void) +{ + const char *symvers = "Module.symvers"; + char line[1024], *path = NULL; + unsigned int line_num = 1; + FILE *file; + + file = fopen(symvers, "r"); + if (!file) { + path = top_level_dir(symvers); + if (!path) { + ERROR("can't open '%s', \"objtool diff\" should be run from the kernel tree", symvers); + return -1; + } + + file = fopen(path, "r"); + if (!file) { + ERROR_GLIBC("fopen"); + return -1; + } + } + + while (fgets(line, 1024, file)) { + char *sym, *mod, *type; + struct export *export; + + sym = strchr(line, '\t'); + if (!sym) { + ERROR("malformed Module.symvers (sym) at line %d", line_num); + return -1; + } + + *sym++ = '\0'; + + mod = strchr(sym, '\t'); + if (!mod) { + ERROR("malformed Module.symvers (mod) at line %d", line_num); + return -1; + } + + *mod++ = '\0'; + + type = strchr(mod, '\t'); + if (!type) { + ERROR("malformed Module.symvers (type) at line %d", line_num); + return -1; + } + + *type++ = '\0'; + + if (*sym == '\0' || *mod == '\0') { + ERROR("malformed Module.symvers at line %d", line_num); + return -1; + } + + export = calloc(1, sizeof(*export)); + if (!export) { + ERROR_GLIBC("calloc"); + return -1; + } + + export->mod = strdup(mod); + if (!export->mod) { + ERROR_GLIBC("strdup"); + return -1; + } + + export->sym = strdup(sym); + if (!export->sym) { + ERROR_GLIBC("strdup"); + return -1; + } + + hash_add(exports, &export->hash, str_hash(sym)); + } + + free(path); + fclose(file); + + return 0; +} + +static int read_sym_checksums(struct elf *elf) +{ + struct section *sec; + + sec = find_section_by_name(elf, ".discard.sym_checksum"); + if (!sec) { + ERROR("'%s' missing .discard.sym_checksum section, file not processed by 'objtool --checksum'?", + elf->name); + return -1; + } + + if (!sec->rsec) { + ERROR("missing reloc section for .discard.sym_checksum"); + return -1; + } + + if (sec_size(sec) % sizeof(struct sym_checksum)) { + ERROR("struct sym_checksum size mismatch"); + return -1; + } + + for (int i = 0; i < sec_size(sec) / sizeof(struct sym_checksum); i++) { + struct sym_checksum *sym_checksum; + struct reloc *reloc; + struct symbol *sym; + + sym_checksum = (struct sym_checksum *)sec->data->d_buf + i; + + reloc = find_reloc_by_dest(elf, sec, i * sizeof(*sym_checksum)); + if (!reloc) { + ERROR("can't find reloc for sym_checksum[%d]", i); + return -1; + } + + sym = reloc->sym; + + if (is_sec_sym(sym)) { + ERROR("not sure how to handle section %s", sym->name); + return -1; + } + + if (is_func_sym(sym)) + sym->csum.checksum = sym_checksum->checksum; + } + + return 0; +} + +static struct symbol *first_file_symbol(struct elf *elf) +{ + struct symbol *sym; + + for_each_sym(elf, sym) { + if (is_file_sym(sym)) + return sym; + } + + return NULL; +} + +static struct symbol *next_file_symbol(struct elf *elf, struct symbol *sym) +{ + for_each_sym_continue(elf, sym) { + if (is_file_sym(sym)) + return sym; + } + + return NULL; +} + +/* + * Certain static local variables should never be correlated. They will be + * used in place rather than referencing the originals. + */ +static bool is_uncorrelated_static_local(struct symbol *sym) +{ + static const char * const vars[] = { + "__key.", + "__warned.", + "__already_done.", + "__func__.", + "_rs.", + "descriptor.", + "CSWTCH.", + }; + + if (!is_object_sym(sym) || !is_local_sym(sym)) + return false; + + if (!strcmp(sym->sec->name, ".data.once")) + return true; + + for (int i = 0; i < ARRAY_SIZE(vars); i++) { + if (strstarts(sym->name, vars[i])) + return true; + } + + return false; +} + +/* + * Clang emits several useless .Ltmp_* code labels. + */ +static bool is_clang_tmp_label(struct symbol *sym) +{ + return sym->type == STT_NOTYPE && + is_text_sec(sym->sec) && + strstarts(sym->name, ".Ltmp") && + isdigit(sym->name[5]); +} + +static bool is_special_section(struct section *sec) +{ + static const char * const specials[] = { + ".altinstructions", + ".smp_locks", + "__bug_table", + "__ex_table", + "__jump_table", + "__mcount_loc", + + /* + * Extract .static_call_sites here to inherit non-module + * preferential treatment. The later static call processing + * during klp module build will be skipped when it sees this + * section already exists. + */ + ".static_call_sites", + }; + + static const char * const non_special_discards[] = { + ".discard.addressable", + ".discard.sym_checksum", + }; + + if (is_text_sec(sec)) + return false; + + for (int i = 0; i < ARRAY_SIZE(specials); i++) { + if (!strcmp(sec->name, specials[i])) + return true; + } + + /* Most .discard data sections are special */ + for (int i = 0; i < ARRAY_SIZE(non_special_discards); i++) { + if (!strcmp(sec->name, non_special_discards[i])) + return false; + } + + return strstarts(sec->name, ".discard."); +} + +/* + * These sections are referenced by special sections but aren't considered + * special sections themselves. + */ +static bool is_special_section_aux(struct section *sec) +{ + static const char * const specials_aux[] = { + ".altinstr_replacement", + ".altinstr_aux", + }; + + for (int i = 0; i < ARRAY_SIZE(specials_aux); i++) { + if (!strcmp(sec->name, specials_aux[i])) + return true; + } + + return false; +} + +/* + * These symbols should never be correlated, so their local patched versions + * are used instead of linking to the originals. + */ +static bool dont_correlate(struct symbol *sym) +{ + return is_file_sym(sym) || + is_null_sym(sym) || + is_sec_sym(sym) || + is_prefix_func(sym) || + is_uncorrelated_static_local(sym) || + is_clang_tmp_label(sym) || + is_string_sec(sym->sec) || + is_special_section(sym->sec) || + is_special_section_aux(sym->sec) || + strstarts(sym->name, "__initcall__"); +} + +/* + * For each symbol in the original kernel, find its corresponding "twin" in the + * patched kernel. + */ +static int correlate_symbols(struct elfs *e) +{ + struct symbol *file1_sym, *file2_sym; + struct symbol *sym1, *sym2; + + /* Correlate locals */ + for (file1_sym = first_file_symbol(e->orig), + file2_sym = first_file_symbol(e->patched); ; + file1_sym = next_file_symbol(e->orig, file1_sym), + file2_sym = next_file_symbol(e->patched, file2_sym)) { + + if (!file1_sym && file2_sym) { + ERROR("FILE symbol mismatch: NULL != %s", file2_sym->name); + return -1; + } + + if (file1_sym && !file2_sym) { + ERROR("FILE symbol mismatch: %s != NULL", file1_sym->name); + return -1; + } + + if (!file1_sym) + break; + + if (strcmp(file1_sym->name, file2_sym->name)) { + ERROR("FILE symbol mismatch: %s != %s", file1_sym->name, file2_sym->name); + return -1; + } + + file1_sym->twin = file2_sym; + file2_sym->twin = file1_sym; + + sym1 = file1_sym; + + for_each_sym_continue(e->orig, sym1) { + if (is_file_sym(sym1) || !is_local_sym(sym1)) + break; + + if (dont_correlate(sym1)) + continue; + + sym2 = file2_sym; + for_each_sym_continue(e->patched, sym2) { + if (is_file_sym(sym2) || !is_local_sym(sym2)) + break; + + if (sym2->twin || dont_correlate(sym2)) + continue; + + if (strcmp(sym1->demangled_name, sym2->demangled_name)) + continue; + + sym1->twin = sym2; + sym2->twin = sym1; + break; + } + } + } + + /* Correlate globals */ + for_each_sym(e->orig, sym1) { + if (sym1->bind == STB_LOCAL) + continue; + + sym2 = find_global_symbol_by_name(e->patched, sym1->name); + + if (sym2 && !sym2->twin && !strcmp(sym1->name, sym2->name)) { + sym1->twin = sym2; + sym2->twin = sym1; + } + } + + for_each_sym(e->orig, sym1) { + if (sym1->twin || dont_correlate(sym1)) + continue; + WARN("no correlation: %s", sym1->name); + } + + return 0; +} + +/* "sympos" is used by livepatch to disambiguate duplicate symbol names */ +static unsigned long find_sympos(struct elf *elf, struct symbol *sym) +{ + bool vmlinux = str_ends_with(objname, "vmlinux.o"); + unsigned long sympos = 0, nr_matches = 0; + bool has_dup = false; + struct symbol *s; + + if (sym->bind != STB_LOCAL) + return 0; + + if (vmlinux && sym->type == STT_FUNC) { + /* + * HACK: Unfortunately, symbol ordering can differ between + * vmlinux.o and vmlinux due to the linker script emitting + * .text.unlikely* before .text*. Count .text.unlikely* first. + * + * TODO: Disambiguate symbols more reliably (checksums?) + */ + for_each_sym(elf, s) { + if (strstarts(s->sec->name, ".text.unlikely") && + !strcmp(s->name, sym->name)) { + nr_matches++; + if (s == sym) + sympos = nr_matches; + else + has_dup = true; + } + } + for_each_sym(elf, s) { + if (!strstarts(s->sec->name, ".text.unlikely") && + !strcmp(s->name, sym->name)) { + nr_matches++; + if (s == sym) + sympos = nr_matches; + else + has_dup = true; + } + } + } else { + for_each_sym(elf, s) { + if (!strcmp(s->name, sym->name)) { + nr_matches++; + if (s == sym) + sympos = nr_matches; + else + has_dup = true; + } + } + } + + if (!sympos) { + ERROR("can't find sympos for %s", sym->name); + return ULONG_MAX; + } + + return has_dup ? sympos : 0; +} + +static int clone_sym_relocs(struct elfs *e, struct symbol *patched_sym); + +static struct symbol *__clone_symbol(struct elf *elf, struct symbol *patched_sym, + bool data_too) +{ + struct section *out_sec = NULL; + unsigned long offset = 0; + struct symbol *out_sym; + + if (data_too && !is_undef_sym(patched_sym)) { + struct section *patched_sec = patched_sym->sec; + + out_sec = find_section_by_name(elf, patched_sec->name); + if (!out_sec) { + out_sec = elf_create_section(elf, patched_sec->name, 0, + patched_sec->sh.sh_entsize, + patched_sec->sh.sh_type, + patched_sec->sh.sh_addralign, + patched_sec->sh.sh_flags); + if (!out_sec) + return NULL; + } + + if (is_string_sec(patched_sym->sec)) { + out_sym = elf_create_section_symbol(elf, out_sec); + if (!out_sym) + return NULL; + + goto sym_created; + } + + if (!is_sec_sym(patched_sym)) + offset = sec_size(out_sec); + + if (patched_sym->len || is_sec_sym(patched_sym)) { + void *data = NULL; + size_t size; + + /* bss doesn't have data */ + if (patched_sym->sec->data->d_buf) + data = patched_sym->sec->data->d_buf + patched_sym->offset; + + if (is_sec_sym(patched_sym)) + size = sec_size(patched_sym->sec); + else + size = patched_sym->len; + + if (!elf_add_data(elf, out_sec, data, size)) + return NULL; + } + } + + out_sym = elf_create_symbol(elf, patched_sym->name, out_sec, + patched_sym->bind, patched_sym->type, + offset, patched_sym->len); + if (!out_sym) + return NULL; + +sym_created: + patched_sym->clone = out_sym; + out_sym->clone = patched_sym; + + return out_sym; +} + +/* + * Copy a symbol to the output object, optionally including its data and + * relocations. + */ +static struct symbol *clone_symbol(struct elfs *e, struct symbol *patched_sym, + bool data_too) +{ + struct symbol *pfx; + + if (patched_sym->clone) + return patched_sym->clone; + + /* Make sure the prefix gets cloned first */ + if (is_func_sym(patched_sym) && data_too) { + pfx = get_func_prefix(patched_sym); + if (pfx) + clone_symbol(e, pfx, true); + } + + if (!__clone_symbol(e->out, patched_sym, data_too)) + return NULL; + + if (data_too && clone_sym_relocs(e, patched_sym)) + return NULL; + + return patched_sym->clone; +} + +static void mark_included_function(struct symbol *func) +{ + struct symbol *pfx; + + func->included = 1; + + /* Include prefix function */ + pfx = get_func_prefix(func); + if (pfx) + pfx->included = 1; + + /* Make sure .cold parent+child always stay together */ + if (func->cfunc && func->cfunc != func) + func->cfunc->included = 1; + if (func->pfunc && func->pfunc != func) + func->pfunc->included = 1; +} + +/* + * Copy all changed functions (and their dependencies) from the patched object + * to the output object. + */ +static int mark_changed_functions(struct elfs *e) +{ + struct symbol *sym_orig, *patched_sym; + bool changed = false; + + /* Find changed functions */ + for_each_sym(e->orig, sym_orig) { + if (!is_func_sym(sym_orig) || is_prefix_func(sym_orig)) + continue; + + patched_sym = sym_orig->twin; + if (!patched_sym) + continue; + + if (sym_orig->csum.checksum != patched_sym->csum.checksum) { + patched_sym->changed = 1; + mark_included_function(patched_sym); + changed = true; + } + } + + /* Find added functions and print them */ + for_each_sym(e->patched, patched_sym) { + if (!is_func_sym(patched_sym) || is_prefix_func(patched_sym)) + continue; + + if (!patched_sym->twin) { + printf("%s: new function: %s\n", objname, patched_sym->name); + mark_included_function(patched_sym); + changed = true; + } + } + + /* Print changed functions */ + for_each_sym(e->patched, patched_sym) { + if (patched_sym->changed) + printf("%s: changed function: %s\n", objname, patched_sym->name); + } + + return !changed ? -1 : 0; +} + +static int clone_included_functions(struct elfs *e) +{ + struct symbol *patched_sym; + + for_each_sym(e->patched, patched_sym) { + if (patched_sym->included) { + if (!clone_symbol(e, patched_sym, true)) + return -1; + } + } + + return 0; +} + +/* + * Determine whether a relocation should reference the section rather than the + * underlying symbol. + */ +static bool section_reference_needed(struct section *sec) +{ + /* + * String symbols are zero-length and uncorrelated. It's easier to + * deal with them as section symbols. + */ + if (is_string_sec(sec)) + return true; + + /* + * .rodata has mostly anonymous data so there's no way to determine the + * length of a needed reference. just copy the whole section if needed. + */ + if (strstarts(sec->name, ".rodata")) + return true; + + /* UBSAN anonymous data */ + if (strstarts(sec->name, ".data..Lubsan") || /* GCC */ + strstarts(sec->name, ".data..L__unnamed_")) /* Clang */ + return true; + + return false; +} + +static bool is_reloc_allowed(struct reloc *reloc) +{ + return section_reference_needed(reloc->sym->sec) == is_sec_sym(reloc->sym); +} + +static struct export *find_export(struct symbol *sym) +{ + struct export *export; + + hash_for_each_possible(exports, export, hash, str_hash(sym->name)) { + if (!strcmp(export->sym, sym->name)) + return export; + } + + return NULL; +} + +static const char *__find_modname(struct elfs *e) +{ + struct section *sec; + char *name; + + sec = find_section_by_name(e->orig, ".modinfo"); + if (!sec) { + ERROR("missing .modinfo section"); + return NULL; + } + + name = memmem(sec->data->d_buf, sec_size(sec), "\0name=", 6); + if (name) + return name + 6; + + name = strdup(e->orig->name); + if (!name) { + ERROR_GLIBC("strdup"); + return NULL; + } + + for (char *c = name; *c; c++) { + if (*c == '/') + name = c + 1; + else if (*c == '-') + *c = '_'; + else if (*c == '.') { + *c = '\0'; + break; + } + } + + return name; +} + +/* Get the object's module name as defined by the kernel (and klp_object) */ +static const char *find_modname(struct elfs *e) +{ + const char *modname; + + if (e->modname) + return e->modname; + + modname = __find_modname(e); + e->modname = modname; + return modname; +} + +/* + * Copying a function from its native compiled environment to a kernel module + * removes its natural access to local functions/variables and unexported + * globals. References to such symbols need to be converted to KLP relocs so + * the kernel arch relocation code knows to apply them and where to find the + * symbols. Particularly, duplicate static symbols need to be disambiguated. + */ +static bool klp_reloc_needed(struct reloc *patched_reloc) +{ + struct symbol *patched_sym = patched_reloc->sym; + struct export *export; + + /* no external symbol to reference */ + if (dont_correlate(patched_sym)) + return false; + + /* For included functions, a regular reloc will do. */ + if (patched_sym->included) + return false; + + /* + * If exported by a module, it has to be a klp reloc. Thanks to the + * clusterfunk that is late module patching, the patch module is + * allowed to be loaded before any modules it depends on. + * + * If exported by vmlinux, a normal reloc will do. + */ + export = find_export(patched_sym); + if (export) + return strcmp(export->mod, "vmlinux"); + + if (!patched_sym->twin) { + /* + * Presumably the symbol and its reference were added by the + * patch. The symbol could be defined in this .o or in another + * .o in the patch module. + * + * This check needs to be *after* the export check due to the + * possibility of the patch adding a new UNDEF reference to an + * exported symbol. + */ + return false; + } + + /* Unexported symbol which lives in the original vmlinux or module. */ + return true; +} + +static int convert_reloc_sym_to_secsym(struct elf *elf, struct reloc *reloc) +{ + struct symbol *sym = reloc->sym; + struct section *sec = sym->sec; + + if (!sec->sym && !elf_create_section_symbol(elf, sec)) + return -1; + + reloc->sym = sec->sym; + set_reloc_sym(elf, reloc, sym->idx); + set_reloc_addend(elf, reloc, sym->offset + reloc_addend(reloc)); + return 0; +} + +static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc) +{ + struct symbol *sym = reloc->sym; + struct section *sec = sym->sec; + + /* If the symbol has a dedicated section, it's easy to find */ + sym = find_symbol_by_offset(sec, 0); + if (sym && sym->len == sec_size(sec)) + goto found_sym; + + /* No dedicated section; find the symbol manually */ + sym = find_symbol_containing(sec, arch_adjusted_addend(reloc)); + if (!sym) { + /* + * This can happen for special section references to weak code + * whose symbol has been stripped by the linker. + */ + return -1; + } + +found_sym: + reloc->sym = sym; + set_reloc_sym(elf, reloc, sym->idx); + set_reloc_addend(elf, reloc, reloc_addend(reloc) - sym->offset); + return 0; +} + +/* + * Convert a relocation symbol reference to the needed format: either a section + * symbol or the underlying symbol itself. + */ +static int convert_reloc_sym(struct elf *elf, struct reloc *reloc) +{ + if (is_reloc_allowed(reloc)) + return 0; + + if (section_reference_needed(reloc->sym->sec)) + return convert_reloc_sym_to_secsym(elf, reloc); + else + return convert_reloc_secsym_to_sym(elf, reloc); +} + +/* + * Convert a regular relocation to a klp relocation (sort of). + */ +static int clone_reloc_klp(struct elfs *e, struct reloc *patched_reloc, + struct section *sec, unsigned long offset, + struct export *export) +{ + struct symbol *patched_sym = patched_reloc->sym; + s64 addend = reloc_addend(patched_reloc); + const char *sym_modname, *sym_orig_name; + static struct section *klp_relocs; + struct symbol *sym, *klp_sym; + unsigned long klp_reloc_off; + char sym_name[SYM_NAME_LEN]; + struct klp_reloc klp_reloc; + unsigned long sympos; + + if (!patched_sym->twin) { + ERROR("unexpected klp reloc for new symbol %s", patched_sym->name); + return -1; + } + + /* + * Keep the original reloc intact for now to avoid breaking objtool run + * which relies on proper relocations for many of its features. This + * will be disabled later by "objtool klp post-link". + * + * Convert it to UNDEF (and WEAK to avoid modpost warnings). + */ + + sym = patched_sym->clone; + if (!sym) { + /* STB_WEAK: avoid modpost undefined symbol warnings */ + sym = elf_create_symbol(e->out, patched_sym->name, NULL, + STB_WEAK, patched_sym->type, 0, 0); + if (!sym) + return -1; + + patched_sym->clone = sym; + sym->clone = patched_sym; + } + + if (!elf_create_reloc(e->out, sec, offset, sym, addend, reloc_type(patched_reloc))) + return -1; + + /* + * Create the KLP symbol. + */ + + if (export) { + sym_modname = export->mod; + sym_orig_name = export->sym; + sympos = 0; + } else { + sym_modname = find_modname(e); + if (!sym_modname) + return -1; + + sym_orig_name = patched_sym->twin->name; + sympos = find_sympos(e->orig, patched_sym->twin); + if (sympos == ULONG_MAX) + return -1; + } + + /* symbol format: .klp.sym.modname.sym_name,sympos */ + if (snprintf_check(sym_name, SYM_NAME_LEN, KLP_SYM_PREFIX "%s.%s,%ld", + sym_modname, sym_orig_name, sympos)) + return -1; + + klp_sym = find_symbol_by_name(e->out, sym_name); + if (!klp_sym) { + /* STB_WEAK: avoid modpost undefined symbol warnings */ + klp_sym = elf_create_symbol(e->out, sym_name, NULL, + STB_WEAK, patched_sym->type, 0, 0); + if (!klp_sym) + return -1; + } + + /* + * Create the __klp_relocs entry. This will be converted to an actual + * KLP rela by "objtool klp post-link". + * + * This intermediate step is necessary to prevent corruption by the + * linker, which doesn't know how to properly handle two rela sections + * applying to the same base section. + */ + + if (!klp_relocs) { + klp_relocs = elf_create_section(e->out, KLP_RELOCS_SEC, 0, + 0, SHT_PROGBITS, 8, SHF_ALLOC); + if (!klp_relocs) + return -1; + } + + klp_reloc_off = sec_size(klp_relocs); + memset(&klp_reloc, 0, sizeof(klp_reloc)); + + klp_reloc.type = reloc_type(patched_reloc); + if (!elf_add_data(e->out, klp_relocs, &klp_reloc, sizeof(klp_reloc))) + return -1; + + /* klp_reloc.offset */ + if (!sec->sym && !elf_create_section_symbol(e->out, sec)) + return -1; + + if (!elf_create_reloc(e->out, klp_relocs, + klp_reloc_off + offsetof(struct klp_reloc, offset), + sec->sym, offset, R_ABS64)) + return -1; + + /* klp_reloc.sym */ + if (!elf_create_reloc(e->out, klp_relocs, + klp_reloc_off + offsetof(struct klp_reloc, sym), + klp_sym, addend, R_ABS64)) + return -1; + + return 0; +} + +/* Copy a reloc and its symbol to the output object */ +static int clone_reloc(struct elfs *e, struct reloc *patched_reloc, + struct section *sec, unsigned long offset) +{ + struct symbol *patched_sym = patched_reloc->sym; + struct export *export = find_export(patched_sym); + long addend = reloc_addend(patched_reloc); + struct symbol *out_sym; + bool klp; + + if (!is_reloc_allowed(patched_reloc)) { + ERROR_FUNC(patched_reloc->sec->base, reloc_offset(patched_reloc), + "missing symbol for reference to %s+%ld", + patched_sym->name, addend); + return -1; + } + + klp = klp_reloc_needed(patched_reloc); + + if (klp) { + if (clone_reloc_klp(e, patched_reloc, sec, offset, export)) + return -1; + + return 0; + } + + /* + * Why !export sets 'data_too': + * + * Unexported non-klp symbols need to live in the patch module, + * otherwise there will be unresolved symbols. Notably, this includes: + * + * - New functions/data + * - String sections + * - Special section entries + * - Uncorrelated static local variables + * - UBSAN sections + */ + out_sym = clone_symbol(e, patched_sym, patched_sym->included || !export); + if (!out_sym) + return -1; + + /* + * For strings, all references use section symbols, thanks to + * section_reference_needed(). clone_symbol() has cloned an empty + * version of the string section. Now copy the string itself. + */ + if (is_string_sec(patched_sym->sec)) { + const char *str = patched_sym->sec->data->d_buf + addend; + + addend = elf_add_string(e->out, out_sym->sec, str); + if (addend == -1) + return -1; + } + + if (!elf_create_reloc(e->out, sec, offset, out_sym, addend, + reloc_type(patched_reloc))) + return -1; + + return 0; +} + +/* Copy all relocs needed for a symbol's contents */ +static int clone_sym_relocs(struct elfs *e, struct symbol *patched_sym) +{ + struct section *patched_rsec = patched_sym->sec->rsec; + struct reloc *patched_reloc; + unsigned long start, end; + struct symbol *out_sym; + + out_sym = patched_sym->clone; + if (!out_sym) { + ERROR("no clone for %s", patched_sym->name); + return -1; + } + + if (!patched_rsec) + return 0; + + if (!is_sec_sym(patched_sym) && !patched_sym->len) + return 0; + + if (is_string_sec(patched_sym->sec)) + return 0; + + if (is_sec_sym(patched_sym)) { + start = 0; + end = sec_size(patched_sym->sec); + } else { + start = patched_sym->offset; + end = start + patched_sym->len; + } + + for_each_reloc(patched_rsec, patched_reloc) { + unsigned long offset; + + if (reloc_offset(patched_reloc) < start || + reloc_offset(patched_reloc) >= end) + continue; + + /* + * Skip any reloc referencing .altinstr_aux. Its code is + * always patched by alternatives. See ALTERNATIVE_TERNARY(). + */ + if (patched_reloc->sym->sec && + !strcmp(patched_reloc->sym->sec->name, ".altinstr_aux")) + continue; + + if (convert_reloc_sym(e->patched, patched_reloc)) { + ERROR_FUNC(patched_rsec->base, reloc_offset(patched_reloc), + "failed to convert reloc sym '%s' to its proper format", + patched_reloc->sym->name); + return -1; + } + + offset = out_sym->offset + (reloc_offset(patched_reloc) - patched_sym->offset); + + if (clone_reloc(e, patched_reloc, out_sym->sec, offset)) + return -1; + } + return 0; + +} + +static int create_fake_symbol(struct elf *elf, struct section *sec, + unsigned long offset, size_t size) +{ + char name[SYM_NAME_LEN]; + unsigned int type; + static int ctr; + char *c; + + if (snprintf_check(name, SYM_NAME_LEN, "%s_%d", sec->name, ctr++)) + return -1; + + for (c = name; *c; c++) + if (*c == '.') + *c = '_'; + + /* + * STT_NOTYPE: Prevent objtool from validating .altinstr_replacement + * while still allowing objdump to disassemble it. + */ + type = is_text_sec(sec) ? STT_NOTYPE : STT_OBJECT; + return elf_create_symbol(elf, name, sec, STB_LOCAL, type, offset, size) ? 0 : -1; +} + +/* + * Special sections (alternatives, etc) are basically arrays of structs. + * For all the special sections, create a symbol for each struct entry. This + * is a bit cumbersome, but it makes the extracting of the individual entries + * much more straightforward. + * + * There are three ways to identify the entry sizes for a special section: + * + * 1) ELF section header sh_entsize: Ideally this would be used almost + * everywhere. But unfortunately the toolchains make it difficult. The + * assembler .[push]section directive syntax only takes entsize when + * combined with SHF_MERGE. But Clang disallows combining SHF_MERGE with + * SHF_WRITE. And some special sections do need to be writable. + * + * Another place this wouldn't work is .altinstr_replacement, whose entries + * don't have a fixed size. + * + * 2) ANNOTATE_DATA_SPECIAL: This is a lightweight objtool annotation which + * points to the beginning of each entry. The size of the entry is then + * inferred by the location of the subsequent annotation (or end of + * section). + * + * 3) Simple array of pointers: If the special section is just a basic array of + * pointers, the entry size can be inferred by the number of relocations. + * No annotations needed. + * + * Note I also tried to create per-entry symbols at the time of creation, in + * the original [inline] asm. Unfortunately, creating uniquely named symbols + * is trickier than one might think, especially with Clang inline asm. I + * eventually just gave up trying to make that work, in favor of using + * ANNOTATE_DATA_SPECIAL and creating the symbols here after the fact. + */ +static int create_fake_symbols(struct elf *elf) +{ + struct section *sec; + struct reloc *reloc; + + /* + * 1) Make symbols for all the ANNOTATE_DATA_SPECIAL entries: + */ + + sec = find_section_by_name(elf, ".discard.annotate_data"); + if (!sec || !sec->rsec) + return 0; + + for_each_reloc(sec->rsec, reloc) { + unsigned long offset, size; + struct reloc *next_reloc; + + if (annotype(elf, sec, reloc) != ANNOTYPE_DATA_SPECIAL) + continue; + + offset = reloc_addend(reloc); + + size = 0; + next_reloc = reloc; + for_each_reloc_continue(sec->rsec, next_reloc) { + if (annotype(elf, sec, next_reloc) != ANNOTYPE_DATA_SPECIAL || + next_reloc->sym->sec != reloc->sym->sec) + continue; + + size = reloc_addend(next_reloc) - offset; + break; + } + + if (!size) + size = sec_size(reloc->sym->sec) - offset; + + if (create_fake_symbol(elf, reloc->sym->sec, offset, size)) + return -1; + } + + /* + * 2) Make symbols for sh_entsize, and simple arrays of pointers: + */ + + for_each_sec(elf, sec) { + unsigned int entry_size; + unsigned long offset; + + if (!is_special_section(sec) || find_symbol_by_offset(sec, 0)) + continue; + + if (!sec->rsec) { + ERROR("%s: missing special section relocations", sec->name); + return -1; + } + + entry_size = sec->sh.sh_entsize; + if (!entry_size) { + entry_size = arch_reloc_size(sec->rsec->relocs); + if (sec_size(sec) != entry_size * sec_num_entries(sec->rsec)) { + ERROR("%s: missing special section entsize or annotations", sec->name); + return -1; + } + } + + for (offset = 0; offset < sec_size(sec); offset += entry_size) { + if (create_fake_symbol(elf, sec, offset, entry_size)) + return -1; + } + } + + return 0; +} + +/* Keep a special section entry if it references an included function */ +static bool should_keep_special_sym(struct elf *elf, struct symbol *sym) +{ + struct reloc *reloc; + + if (is_sec_sym(sym) || !sym->sec->rsec) + return false; + + sym_for_each_reloc(elf, sym, reloc) { + if (convert_reloc_sym(elf, reloc)) + continue; + + if (is_func_sym(reloc->sym) && reloc->sym->included) + return true; + } + + return false; +} + +/* + * Klp relocations aren't allowed for __jump_table and .static_call_sites if + * the referenced symbol lives in a kernel module, because such klp relocs may + * be applied after static branch/call init, resulting in code corruption. + * + * Validate a special section entry to avoid that. Note that an inert + * tracepoint is harmless enough, in that case just skip the entry and print a + * warning. Otherwise, return an error. + * + * This is only a temporary limitation which will be fixed when livepatch adds + * support for submodules: fully self-contained modules which are embedded in + * the top-level livepatch module's data and which can be loaded on demand when + * their corresponding to-be-patched module gets loaded. Then klp relocs can + * be retired. + * + * Return: + * -1: error: validation failed + * 1: warning: tracepoint skipped + * 0: success + */ +static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym) +{ + bool static_branch = !strcmp(sym->sec->name, "__jump_table"); + bool static_call = !strcmp(sym->sec->name, ".static_call_sites"); + struct symbol *code_sym = NULL; + unsigned long code_offset = 0; + struct reloc *reloc; + int ret = 0; + + if (!static_branch && !static_call) + return 0; + + sym_for_each_reloc(e->patched, sym, reloc) { + const char *sym_modname; + struct export *export; + + /* Static branch/call keys are always STT_OBJECT */ + if (reloc->sym->type != STT_OBJECT) { + + /* Save code location which can be printed below */ + if (reloc->sym->type == STT_FUNC && !code_sym) { + code_sym = reloc->sym; + code_offset = reloc_addend(reloc); + } + + continue; + } + + if (!klp_reloc_needed(reloc)) + continue; + + export = find_export(reloc->sym); + if (export) { + sym_modname = export->mod; + } else { + sym_modname = find_modname(e); + if (!sym_modname) + return -1; + } + + /* vmlinux keys are ok */ + if (!strcmp(sym_modname, "vmlinux")) + continue; + + if (static_branch) { + if (strstarts(reloc->sym->name, "__tracepoint_")) { + WARN("%s: disabling unsupported tracepoint %s", + code_sym->name, reloc->sym->name + 13); + ret = 1; + continue; + } + + ERROR("%s+0x%lx: unsupported static branch key %s. Use static_key_enabled() instead", + code_sym->name, code_offset, reloc->sym->name); + return -1; + } + + /* static call */ + if (strstarts(reloc->sym->name, "__SCK__tp_func_")) { + ret = 1; + continue; + } + + ERROR("%s()+0x%lx: unsupported static call key %s. Use KLP_STATIC_CALL() instead", + code_sym->name, code_offset, reloc->sym->name); + return -1; + } + + return ret; +} + +static int clone_special_section(struct elfs *e, struct section *patched_sec) +{ + struct symbol *patched_sym; + + /* + * Extract all special section symbols (and their dependencies) which + * reference included functions. + */ + sec_for_each_sym(patched_sec, patched_sym) { + int ret; + + if (!is_object_sym(patched_sym)) + continue; + + if (!should_keep_special_sym(e->patched, patched_sym)) + continue; + + ret = validate_special_section_klp_reloc(e, patched_sym); + if (ret < 0) + return -1; + if (ret > 0) + continue; + + if (!clone_symbol(e, patched_sym, true)) + return -1; + } + + return 0; +} + +/* Extract only the needed bits from special sections */ +static int clone_special_sections(struct elfs *e) +{ + struct section *patched_sec; + + if (create_fake_symbols(e->patched)) + return -1; + + for_each_sec(e->patched, patched_sec) { + if (is_special_section(patched_sec)) { + if (clone_special_section(e, patched_sec)) + return -1; + } + } + + return 0; +} + +/* + * Create __klp_objects and __klp_funcs sections which are intermediate + * sections provided as input to the patch module's init code for building the + * klp_patch, klp_object and klp_func structs for the livepatch API. + */ +static int create_klp_sections(struct elfs *e) +{ + size_t obj_size = sizeof(struct klp_object_ext); + size_t func_size = sizeof(struct klp_func_ext); + struct section *obj_sec, *funcs_sec, *str_sec; + struct symbol *funcs_sym, *str_sym, *sym; + char sym_name[SYM_NAME_LEN]; + unsigned int nr_funcs = 0; + const char *modname; + void *obj_data; + s64 addend; + + obj_sec = elf_create_section_pair(e->out, KLP_OBJECTS_SEC, obj_size, 0, 0); + if (!obj_sec) + return -1; + + funcs_sec = elf_create_section_pair(e->out, KLP_FUNCS_SEC, func_size, 0, 0); + if (!funcs_sec) + return -1; + + funcs_sym = elf_create_section_symbol(e->out, funcs_sec); + if (!funcs_sym) + return -1; + + str_sec = elf_create_section(e->out, KLP_STRINGS_SEC, 0, 0, + SHT_PROGBITS, 1, + SHF_ALLOC | SHF_STRINGS | SHF_MERGE); + if (!str_sec) + return -1; + + if (elf_add_string(e->out, str_sec, "") == -1) + return -1; + + str_sym = elf_create_section_symbol(e->out, str_sec); + if (!str_sym) + return -1; + + /* allocate klp_object_ext */ + obj_data = elf_add_data(e->out, obj_sec, NULL, obj_size); + if (!obj_data) + return -1; + + modname = find_modname(e); + if (!modname) + return -1; + + /* klp_object_ext.name */ + if (strcmp(modname, "vmlinux")) { + addend = elf_add_string(e->out, str_sec, modname); + if (addend == -1) + return -1; + + if (!elf_create_reloc(e->out, obj_sec, + offsetof(struct klp_object_ext, name), + str_sym, addend, R_ABS64)) + return -1; + } + + /* klp_object_ext.funcs */ + if (!elf_create_reloc(e->out, obj_sec, offsetof(struct klp_object_ext, funcs), + funcs_sym, 0, R_ABS64)) + return -1; + + for_each_sym(e->out, sym) { + unsigned long offset = nr_funcs * func_size; + unsigned long sympos; + void *func_data; + + if (!is_func_sym(sym) || sym->cold || !sym->clone || !sym->clone->changed) + continue; + + /* allocate klp_func_ext */ + func_data = elf_add_data(e->out, funcs_sec, NULL, func_size); + if (!func_data) + return -1; + + /* klp_func_ext.old_name */ + addend = elf_add_string(e->out, str_sec, sym->clone->twin->name); + if (addend == -1) + return -1; + + if (!elf_create_reloc(e->out, funcs_sec, + offset + offsetof(struct klp_func_ext, old_name), + str_sym, addend, R_ABS64)) + return -1; + + /* klp_func_ext.new_func */ + if (!elf_create_reloc(e->out, funcs_sec, + offset + offsetof(struct klp_func_ext, new_func), + sym, 0, R_ABS64)) + return -1; + + /* klp_func_ext.sympos */ + BUILD_BUG_ON(sizeof(sympos) != sizeof_field(struct klp_func_ext, sympos)); + sympos = find_sympos(e->orig, sym->clone->twin); + if (sympos == ULONG_MAX) + return -1; + memcpy(func_data + offsetof(struct klp_func_ext, sympos), &sympos, + sizeof_field(struct klp_func_ext, sympos)); + + nr_funcs++; + } + + /* klp_object_ext.nr_funcs */ + BUILD_BUG_ON(sizeof(nr_funcs) != sizeof_field(struct klp_object_ext, nr_funcs)); + memcpy(obj_data + offsetof(struct klp_object_ext, nr_funcs), &nr_funcs, + sizeof_field(struct klp_object_ext, nr_funcs)); + + /* + * Find callback pointers created by KLP_PRE_PATCH_CALLBACK() and + * friends, and add them to the klp object. + */ + + if (snprintf_check(sym_name, SYM_NAME_LEN, KLP_PRE_PATCH_PREFIX "%s", modname)) + return -1; + + sym = find_symbol_by_name(e->out, sym_name); + if (sym) { + struct reloc *reloc; + + reloc = find_reloc_by_dest(e->out, sym->sec, sym->offset); + + if (!elf_create_reloc(e->out, obj_sec, + offsetof(struct klp_object_ext, callbacks) + + offsetof(struct klp_callbacks, pre_patch), + reloc->sym, reloc_addend(reloc), R_ABS64)) + return -1; + } + + if (snprintf_check(sym_name, SYM_NAME_LEN, KLP_POST_PATCH_PREFIX "%s", modname)) + return -1; + + sym = find_symbol_by_name(e->out, sym_name); + if (sym) { + struct reloc *reloc; + + reloc = find_reloc_by_dest(e->out, sym->sec, sym->offset); + + if (!elf_create_reloc(e->out, obj_sec, + offsetof(struct klp_object_ext, callbacks) + + offsetof(struct klp_callbacks, post_patch), + reloc->sym, reloc_addend(reloc), R_ABS64)) + return -1; + } + + if (snprintf_check(sym_name, SYM_NAME_LEN, KLP_PRE_UNPATCH_PREFIX "%s", modname)) + return -1; + + sym = find_symbol_by_name(e->out, sym_name); + if (sym) { + struct reloc *reloc; + + reloc = find_reloc_by_dest(e->out, sym->sec, sym->offset); + + if (!elf_create_reloc(e->out, obj_sec, + offsetof(struct klp_object_ext, callbacks) + + offsetof(struct klp_callbacks, pre_unpatch), + reloc->sym, reloc_addend(reloc), R_ABS64)) + return -1; + } + + if (snprintf_check(sym_name, SYM_NAME_LEN, KLP_POST_UNPATCH_PREFIX "%s", modname)) + return -1; + + sym = find_symbol_by_name(e->out, sym_name); + if (sym) { + struct reloc *reloc; + + reloc = find_reloc_by_dest(e->out, sym->sec, sym->offset); + + if (!elf_create_reloc(e->out, obj_sec, + offsetof(struct klp_object_ext, callbacks) + + offsetof(struct klp_callbacks, post_unpatch), + reloc->sym, reloc_addend(reloc), R_ABS64)) + return -1; + } + + return 0; +} + +/* + * Copy all .modinfo import_ns= tags to ensure all namespaced exported symbols + * can be accessed via normal relocs. + */ +static int copy_import_ns(struct elfs *e) +{ + struct section *patched_sec, *out_sec = NULL; + char *import_ns, *data_end; + + patched_sec = find_section_by_name(e->patched, ".modinfo"); + if (!patched_sec) + return 0; + + import_ns = patched_sec->data->d_buf; + if (!import_ns) + return 0; + + for (data_end = import_ns + sec_size(patched_sec); + import_ns < data_end; + import_ns += strlen(import_ns) + 1) { + + import_ns = memmem(import_ns, data_end - import_ns, "import_ns=", 10); + if (!import_ns) + return 0; + + if (!out_sec) { + out_sec = find_section_by_name(e->out, ".modinfo"); + if (!out_sec) { + out_sec = elf_create_section(e->out, ".modinfo", 0, + patched_sec->sh.sh_entsize, + patched_sec->sh.sh_type, + patched_sec->sh.sh_addralign, + patched_sec->sh.sh_flags); + if (!out_sec) + return -1; + } + } + + if (!elf_add_data(e->out, out_sec, import_ns, strlen(import_ns) + 1)) + return -1; + } + + return 0; +} + +int cmd_klp_diff(int argc, const char **argv) +{ + struct elfs e = {0}; + + argc = parse_options(argc, argv, klp_diff_options, klp_diff_usage, 0); + if (argc != 3) + usage_with_options(klp_diff_usage, klp_diff_options); + + objname = argv[0]; + + e.orig = elf_open_read(argv[0], O_RDONLY); + e.patched = elf_open_read(argv[1], O_RDONLY); + e.out = NULL; + + if (!e.orig || !e.patched) + return -1; + + if (read_exports()) + return -1; + + if (read_sym_checksums(e.orig)) + return -1; + + if (read_sym_checksums(e.patched)) + return -1; + + if (correlate_symbols(&e)) + return -1; + + if (mark_changed_functions(&e)) + return 0; + + e.out = elf_create_file(&e.orig->ehdr, argv[2]); + if (!e.out) + return -1; + + if (clone_included_functions(&e)) + return -1; + + if (clone_special_sections(&e)) + return -1; + + if (create_klp_sections(&e)) + return -1; + + if (copy_import_ns(&e)) + return -1; + + if (elf_write(e.out)) + return -1; + + return elf_close(e.out); +} diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c index 5c8b974ad0f9..c8f611c1320d 100644 --- a/tools/objtool/objtool.c +++ b/tools/objtool/objtool.c @@ -16,8 +16,6 @@ #include #include -bool help; - static struct objtool_file file; struct objtool_file *objtool_open_read(const char *filename) @@ -71,6 +69,39 @@ int objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func) return 0; } +char *top_level_dir(const char *file) +{ + ssize_t len, self_len, file_len; + char self[PATH_MAX], *str; + int i; + + len = readlink("/proc/self/exe", self, sizeof(self) - 1); + if (len <= 0) + return NULL; + self[len] = '\0'; + + for (i = 0; i < 3; i++) { + char *s = strrchr(self, '/'); + if (!s) + return NULL; + *s = '\0'; + } + + self_len = strlen(self); + file_len = strlen(file); + + str = malloc(self_len + file_len + 2); + if (!str) + return NULL; + + memcpy(str, self, self_len); + str[self_len] = '/'; + strcpy(str + self_len + 1, file); + + return str; +} + + int main(int argc, const char **argv) { static const char *UNUSED = "OBJTOOL_NOT_IMPLEMENTED"; @@ -79,5 +110,11 @@ int main(int argc, const char **argv) exec_cmd_init("objtool", UNUSED, UNUSED, UNUSED); pager_init(UNUSED); + if (argc > 1 && !strcmp(argv[1], "klp")) { + argc--; + argv++; + return cmd_klp(argc, argv); + } + return objtool_run(argc, argv); } diff --git a/tools/objtool/sync-check.sh b/tools/objtool/sync-check.sh index 86d64e3ac6f7..e38167ca56a9 100755 --- a/tools/objtool/sync-check.sh +++ b/tools/objtool/sync-check.sh @@ -17,6 +17,7 @@ arch/x86/include/asm/emulate_prefix.h arch/x86/lib/x86-opcode-map.txt arch/x86/tools/gen-insn-attr-x86.awk include/linux/interval_tree_generic.h +include/linux/livepatch_external.h include/linux/static_call_types.h " diff --git a/tools/objtool/weak.c b/tools/objtool/weak.c index d83f607733b0..d6562f292259 100644 --- a/tools/objtool/weak.c +++ b/tools/objtool/weak.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #define UNSUPPORTED(name) \ ({ \ @@ -24,3 +26,8 @@ int __weak orc_create(struct objtool_file *file) { UNSUPPORTED("ORC"); } + +int __weak cmd_klp(int argc, const char **argv) +{ + UNSUPPORTED("klp"); +} -- cgit v1.2.3 From f2c356d1d0f048e88c281a4178c8b2db138d3ac1 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 17 Sep 2025 09:04:05 -0700 Subject: kbuild,objtool: Defer objtool validation step for CONFIG_KLP_BUILD In preparation for klp-build, defer objtool validation for CONFIG_KLP_BUILD kernels until the final pre-link archive (e.g., vmlinux.o, module-foo.o) is built. This will simplify the process of generating livepatch modules. Delayed objtool is generally preferred anyway, and is already standard for IBT and LTO. Eventually the per-translation-unit mode will be phased out. Acked-by: Petr Mladek Tested-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- scripts/Makefile.lib | 2 +- scripts/link-vmlinux.sh | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 15fee73e9289..28a1c08e3b22 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -197,7 +197,7 @@ objtool-args = $(objtool-args-y) \ $(if $(delay-objtool), --link) \ $(if $(part-of-module), --module) -delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT)) +delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT),$(CONFIG_KLP_BUILD)) cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@) cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd) diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 433849ff7529..2df714ba51a9 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -60,7 +60,8 @@ vmlinux_link() # skip output file argument shift - if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT; then + if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT || + is_enabled CONFIG_KLP_BUILD; then # Use vmlinux.o instead of performing the slow LTO link again. objs=vmlinux.o libs= -- cgit v1.2.3 From abaf1f42ddd070662fb419aed29c985ea209bd88 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 17 Sep 2025 09:04:06 -0700 Subject: livepatch/klp-build: Introduce fix-patch-lines script to avoid __LINE__ diff noise The __LINE__ macro creates challenges for binary diffing. When a .patch file adds or removes lines, it shifts the line numbers for all code below it. This can cause the code generation of functions using __LINE__ to change due to the line number constant being embedded in a MOV instruction, despite there being no semantic difference. Avoid such false positives by adding a fix-patch-lines script which can be used to insert a #line directive in each patch hunk affecting the line numbering. This script will be used by klp-build, which will be introduced in a subsequent patch. Acked-by: Petr Mladek Tested-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- MAINTAINERS | 1 + scripts/livepatch/fix-patch-lines | 79 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100755 scripts/livepatch/fix-patch-lines (limited to 'scripts') diff --git a/MAINTAINERS b/MAINTAINERS index 755e2528f839..fc573e9a523c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14443,6 +14443,7 @@ F: include/linux/livepatch*.h F: kernel/livepatch/ F: kernel/module/livepatch.c F: samples/livepatch/ +F: scripts/livepatch/ F: tools/testing/selftests/livepatch/ LLC (802.2) diff --git a/scripts/livepatch/fix-patch-lines b/scripts/livepatch/fix-patch-lines new file mode 100755 index 000000000000..73c5e3dea46e --- /dev/null +++ b/scripts/livepatch/fix-patch-lines @@ -0,0 +1,79 @@ +#!/usr/bin/awk -f +# SPDX-License-Identifier: GPL-2.0 +# +# Use #line directives to preserve original __LINE__ numbers across patches to +# avoid unwanted compilation changes. + +BEGIN { + in_hunk = 0 + skip = 0 +} + +/^--- / { + skip = $2 !~ /\.(c|h)$/ + print + next +} + +/^@@/ { + if (skip) { + print + next + } + + in_hunk = 1 + + # for @@ -1,3 +1,4 @@: + # 1: line number in old file + # 3: how many lines the hunk covers in old file + # 1: line number in new file + # 4: how many lines the hunk covers in new file + + match($0, /^@@ -([0-9]+)(,([0-9]+))? \+([0-9]+)(,([0-9]+))? @@/, m) + + # Set 'cur' to the old file's line number at the start of the hunk. It + # gets incremented for every context line and every line removal, so + # that it always represents the old file's current line number. + cur = m[1] + + # last = last line number of current hunk + last = cur + (m[3] ? m[3] : 1) - 1 + + need_line_directive = 0 + + print + next +} + +{ + if (skip || !in_hunk || $0 ~ /^\\ No newline at end of file/) { + print + next + } + + # change line + if ($0 ~ /^[+-]/) { + # inject #line after this group of changes + need_line_directive = 1 + + if ($0 ~ /^-/) + cur++ + + print + next + } + + # If this is the first context line after a group of changes, inject + # the #line directive to force the compiler to correct the line + # numbering to match the original file. + if (need_line_directive) { + print "+#line " cur + need_line_directive = 0 + } + + if (cur == last) + in_hunk = 0 + + cur++ + print +} -- cgit v1.2.3 From 59adee07b568fb78e2bf07df1f22f3fe45b7240a Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 17 Sep 2025 09:04:07 -0700 Subject: livepatch/klp-build: Add stub init code for livepatch modules Add a module initialization stub which can be linked with binary diff objects to produce a livepatch module. Acked-by: Petr Mladek Tested-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- scripts/livepatch/init.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 scripts/livepatch/init.c (limited to 'scripts') diff --git a/scripts/livepatch/init.c b/scripts/livepatch/init.c new file mode 100644 index 000000000000..2274d8f5a482 --- /dev/null +++ b/scripts/livepatch/init.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Init code for a livepatch kernel module + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +extern struct klp_object_ext __start_klp_objects[]; +extern struct klp_object_ext __stop_klp_objects[]; + +static struct klp_patch *patch; + +static int __init livepatch_mod_init(void) +{ + struct klp_object *objs; + unsigned int nr_objs; + int ret; + + nr_objs = __stop_klp_objects - __start_klp_objects; + + if (!nr_objs) { + pr_err("nothing to patch!\n"); + ret = -EINVAL; + goto err; + } + + patch = kzalloc(sizeof(*patch), GFP_KERNEL); + if (!patch) { + ret = -ENOMEM; + goto err; + } + + objs = kzalloc(sizeof(struct klp_object) * (nr_objs + 1), GFP_KERNEL); + if (!objs) { + ret = -ENOMEM; + goto err_free_patch; + } + + for (int i = 0; i < nr_objs; i++) { + struct klp_object_ext *obj_ext = __start_klp_objects + i; + struct klp_func_ext *funcs_ext = obj_ext->funcs; + unsigned int nr_funcs = obj_ext->nr_funcs; + struct klp_func *funcs = objs[i].funcs; + struct klp_object *obj = objs + i; + + funcs = kzalloc(sizeof(struct klp_func) * (nr_funcs + 1), GFP_KERNEL); + if (!funcs) { + ret = -ENOMEM; + for (int j = 0; j < i; j++) + kfree(objs[i].funcs); + goto err_free_objs; + } + + for (int j = 0; j < nr_funcs; j++) { + funcs[j].old_name = funcs_ext[j].old_name; + funcs[j].new_func = funcs_ext[j].new_func; + funcs[j].old_sympos = funcs_ext[j].sympos; + } + + obj->name = obj_ext->name; + obj->funcs = funcs; + + memcpy(&obj->callbacks, &obj_ext->callbacks, sizeof(struct klp_callbacks)); + } + + patch->mod = THIS_MODULE; + patch->objs = objs; + + /* TODO patch->states */ + +#ifdef KLP_NO_REPLACE + patch->replace = false; +#else + patch->replace = true; +#endif + + return klp_enable_patch(patch); + +err_free_objs: + kfree(objs); +err_free_patch: + kfree(patch); +err: + return ret; +} + +static void __exit livepatch_mod_exit(void) +{ + unsigned int nr_objs; + + nr_objs = __stop_klp_objects - __start_klp_objects; + + for (int i = 0; i < nr_objs; i++) + kfree(patch->objs[i].funcs); + + kfree(patch->objs); + kfree(patch); +} + +module_init(livepatch_mod_init); +module_exit(livepatch_mod_exit); +MODULE_LICENSE("GPL"); +MODULE_INFO(livepatch, "Y"); +MODULE_DESCRIPTION("Livepatch module"); -- cgit v1.2.3 From 24ebfcd65a871df4555b98c49c9ed9a92f146113 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 17 Sep 2025 09:04:08 -0700 Subject: livepatch/klp-build: Introduce klp-build script for generating livepatch modules Add a klp-build script which automates the generation of a livepatch module from a source .patch file by performing the following steps: - Builds an original kernel with -function-sections and -fdata-sections, plus objtool function checksumming. - Applies the .patch file and rebuilds the kernel using the same options. - Runs 'objtool klp diff' to detect changed functions and generate intermediate binary diff objects. - Builds a kernel module which links the diff objects with some livepatch module init code (scripts/livepatch/init.c). - Finalizes the livepatch module (aka work around linker wreckage) using 'objtool klp post-link'. Acked-by: Petr Mladek Tested-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- scripts/Makefile.lib | 1 + scripts/livepatch/fix-patch-lines | 2 +- scripts/livepatch/klp-build | 743 ++++++++++++++++++++++++++++++++++++++ tools/objtool/klp-diff.c | 6 +- 4 files changed, 749 insertions(+), 3 deletions(-) create mode 100755 scripts/livepatch/klp-build (limited to 'scripts') diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 28a1c08e3b22..f4b33919ec37 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -173,6 +173,7 @@ ifdef CONFIG_OBJTOOL objtool := $(objtree)/tools/objtool/objtool +objtool-args-$(CONFIG_KLP_BUILD) += --checksum objtool-args-$(CONFIG_HAVE_JUMP_LABEL_HACK) += --hacks=jump_label objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) += --hacks=noinstr objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) += --hacks=skylake diff --git a/scripts/livepatch/fix-patch-lines b/scripts/livepatch/fix-patch-lines index 73c5e3dea46e..fa7d4f6592e6 100755 --- a/scripts/livepatch/fix-patch-lines +++ b/scripts/livepatch/fix-patch-lines @@ -23,7 +23,7 @@ BEGIN { in_hunk = 1 - # for @@ -1,3 +1,4 @@: + # @@ -1,3 +1,4 @@: # 1: line number in old file # 3: how many lines the hunk covers in old file # 1: line number in new file diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build new file mode 100755 index 000000000000..01ed0b66bfaf --- /dev/null +++ b/scripts/livepatch/klp-build @@ -0,0 +1,743 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Build a livepatch module + +# shellcheck disable=SC1090,SC2155 + +if (( BASH_VERSINFO[0] < 4 || \ + (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4) )); then + echo "error: this script requires bash 4.4+" >&2 + exit 1 +fi + +set -o errexit +set -o errtrace +set -o pipefail +set -o nounset + +# Allow doing 'cmd | mapfile -t array' instead of 'mapfile -t array < <(cmd)'. +# This helps keep execution in pipes so pipefail+errexit can catch errors. +shopt -s lastpipe + +unset SKIP_CLEANUP XTRACE + +REPLACE=1 +SHORT_CIRCUIT=0 +JOBS="$(getconf _NPROCESSORS_ONLN)" +VERBOSE="-s" +shopt -o xtrace | grep -q 'on' && XTRACE=1 + +# Avoid removing the previous $TMP_DIR until args have been fully processed. +KEEP_TMP=1 + +SCRIPT="$(basename "$0")" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +FIX_PATCH_LINES="$SCRIPT_DIR/fix-patch-lines" + +SRC="$(pwd)" +OBJ="$(pwd)" + +CONFIG="$OBJ/.config" +TMP_DIR="$OBJ/klp-tmp" + +ORIG_DIR="$TMP_DIR/orig" +PATCHED_DIR="$TMP_DIR/patched" +DIFF_DIR="$TMP_DIR/diff" +KMOD_DIR="$TMP_DIR/kmod" + +STASH_DIR="$TMP_DIR/stash" +TIMESTAMP="$TMP_DIR/timestamp" +PATCH_TMP_DIR="$TMP_DIR/tmp" + +KLP_DIFF_LOG="$DIFF_DIR/diff.log" + +grep0() { + command grep "$@" || true +} + +status() { + echo "$*" +} + +warn() { + echo "error: $SCRIPT: $*" >&2 +} + +die() { + warn "$@" + exit 1 +} + +declare -a STASHED_FILES + +stash_file() { + local file="$1" + local rel_file="${file#"$SRC"/}" + + [[ ! -e "$file" ]] && die "no file to stash: $file" + + mkdir -p "$STASH_DIR/$(dirname "$rel_file")" + cp -f "$file" "$STASH_DIR/$rel_file" + + STASHED_FILES+=("$rel_file") +} + +restore_files() { + local file + + for file in "${STASHED_FILES[@]}"; do + mv -f "$STASH_DIR/$file" "$SRC/$file" || warn "can't restore file: $file" + done + + STASHED_FILES=() +} + +cleanup() { + set +o nounset + revert_patches "--recount" + restore_files + [[ "$KEEP_TMP" -eq 0 ]] && rm -rf "$TMP_DIR" + return 0 +} + +trap_err() { + warn "line ${BASH_LINENO[0]}: '$BASH_COMMAND'" +} + +trap cleanup EXIT INT TERM HUP +trap trap_err ERR + +__usage() { + cat < Build jobs to run simultaneously [default: $JOBS] + -o, --output= Output file [default: livepatch-.ko] + --no-replace Disable livepatch atomic replace + -v, --verbose Pass V=1 to kernel/module builds + +Advanced Options: + -S, --short-circuit=STEP Start at build step (requires prior --keep-tmp) + 1|orig Build original kernel (default) + 2|patched Build patched kernel + 3|diff Diff objects + 4|kmod Build patch module + -T, --keep-tmp Preserve tmp dir on exit + +EOF +} + +usage() { + __usage >&2 +} + +process_args() { + local keep_tmp=0 + local short + local long + local args + + short="hj:o:vS:T" + long="help,jobs:,output:,no-replace,verbose,short-circuit:,keep-tmp" + + args=$(getopt --options "$short" --longoptions "$long" -- "$@") || { + echo; usage; exit + } + eval set -- "$args" + + while true; do + case "$1" in + -h | --help) + usage + exit 0 + ;; + -j | --jobs) + JOBS="$2" + shift 2 + ;; + -o | --output) + [[ "$2" != *.ko ]] && die "output filename should end with .ko" + OUTFILE="$2" + NAME="$(basename "$OUTFILE")" + NAME="${NAME%.ko}" + NAME="$(module_name_string "$NAME")" + shift 2 + ;; + --no-replace) + REPLACE=0 + shift + ;; + -v | --verbose) + VERBOSE="V=1" + shift + ;; + -S | --short-circuit) + [[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir" + keep_tmp=1 + case "$2" in + 1 | orig) SHORT_CIRCUIT=1; ;; + 2 | patched) SHORT_CIRCUIT=2; ;; + 3 | diff) SHORT_CIRCUIT=3; ;; + 4 | mod) SHORT_CIRCUIT=4; ;; + *) die "invalid short-circuit step '$2'" ;; + esac + shift 2 + ;; + -T | --keep-tmp) + keep_tmp=1 + shift + ;; + --) + shift + break + ;; + *) + usage + exit 1 + ;; + esac + done + + if [[ $# -eq 0 ]]; then + usage + exit 1 + fi + + KEEP_TMP="$keep_tmp" + PATCHES=("$@") +} + +# temporarily disable xtrace for especially verbose code +xtrace_save() { + [[ -v XTRACE ]] && set +x + return 0 +} + +xtrace_restore() { + [[ -v XTRACE ]] && set -x + return 0 +} + +validate_config() { + xtrace_save "reading .config" + source "$CONFIG" || die "no .config file in $(dirname "$CONFIG")" + xtrace_restore + + [[ -v CONFIG_LIVEPATCH ]] || \ + die "CONFIG_LIVEPATCH not enabled" + + [[ -v CONFIG_KLP_BUILD ]] || \ + die "CONFIG_KLP_BUILD not enabled" + + [[ -v CONFIG_GCC_PLUGIN_LATENT_ENTROPY ]] && \ + die "kernel option 'CONFIG_GCC_PLUGIN_LATENT_ENTROPY' not supported" + + [[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]] && \ + die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not supported" + + return 0 +} + +# Only allow alphanumerics and '_' and '-' in the module name. Everything else +# is replaced with '-'. Also truncate to 55 chars so the full name + NUL +# terminator fits in the kernel's 56-byte module name array. +module_name_string() { + echo "${1//[^a-zA-Z0-9_-]/-}" | cut -c 1-55 +} + +# If the module name wasn't specified on the cmdline with --output, give it a +# name based on the patch name. +set_module_name() { + [[ -v NAME ]] && return 0 + + if [[ "${#PATCHES[@]}" -eq 1 ]]; then + NAME="$(basename "${PATCHES[0]}")" + NAME="${NAME%.*}" + else + NAME="patch" + fi + + NAME="livepatch-$NAME" + NAME="$(module_name_string "$NAME")" + + OUTFILE="$NAME.ko" +} + +# Hardcode the value printed by the localversion script to prevent patch +# application from appending it with '+' due to a dirty git working tree. +set_kernelversion() { + local file="$SRC/scripts/setlocalversion" + local localversion + + stash_file "$file" + + localversion="$(cd "$SRC" && make --no-print-directory kernelversion)" + localversion="$(cd "$SRC" && KERNELVERSION="$localversion" ./scripts/setlocalversion)" + [[ -z "$localversion" ]] && die "setlocalversion failed" + + sed -i "2i echo $localversion; exit 0" scripts/setlocalversion +} + +get_patch_files() { + local patch="$1" + + grep0 -E '^(--- |\+\+\+ )' "$patch" \ + | gawk '{print $2}' \ + | sed 's|^[^/]*/||' \ + | sort -u +} + +# Make sure git re-stats the changed files +git_refresh() { + local patch="$1" + local files=() + + [[ ! -e "$SRC/.git" ]] && return + + get_patch_files "$patch" | mapfile -t files + + ( + cd "$SRC" + git update-index -q --refresh -- "${files[@]}" + ) +} + +check_unsupported_patches() { + local patch + + for patch in "${PATCHES[@]}"; do + local files=() + + get_patch_files "$patch" | mapfile -t files + + for file in "${files[@]}"; do + case "$file" in + lib/*|*.S) + die "unsupported patch to $file" + ;; + esac + done + done +} + +apply_patch() { + local patch="$1" + shift + local extra_args=("$@") + + [[ ! -f "$patch" ]] && die "$patch doesn't exist" + + ( + cd "$SRC" + + # The sed strips the version signature from 'git format-patch', + # otherwise 'git apply --recount' warns. + sed -n '/^-- /q;p' "$patch" | + git apply "${extra_args[@]}" + ) + + APPLIED_PATCHES+=("$patch") +} + +revert_patch() { + local patch="$1" + shift + local extra_args=("$@") + local tmp=() + + ( + cd "$SRC" + + sed -n '/^-- /q;p' "$patch" | + git apply --reverse "${extra_args[@]}" + ) + git_refresh "$patch" + + for p in "${APPLIED_PATCHES[@]}"; do + [[ "$p" == "$patch" ]] && continue + tmp+=("$p") + done + + APPLIED_PATCHES=("${tmp[@]}") +} + +apply_patches() { + local patch + + for patch in "${PATCHES[@]}"; do + apply_patch "$patch" + done +} + +revert_patches() { + local extra_args=("$@") + local patches=("${APPLIED_PATCHES[@]}") + + for (( i=${#patches[@]}-1 ; i>=0 ; i-- )) ; do + revert_patch "${patches[$i]}" "${extra_args[@]}" + done + + APPLIED_PATCHES=() +} + +validate_patches() { + check_unsupported_patches + apply_patches + revert_patches +} + +do_init() { + # We're not yet smart enough to handle anything other than in-tree + # builds in pwd. + [[ ! "$SRC" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory" + [[ ! "$OBJ" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory" + + (( SHORT_CIRCUIT <= 1 )) && rm -rf "$TMP_DIR" + mkdir -p "$TMP_DIR" + + APPLIED_PATCHES=() + + [[ -x "$FIX_PATCH_LINES" ]] || die "can't find fix-patch-lines" + + validate_config + set_module_name + set_kernelversion +} + +# Refresh the patch hunk headers, specifically the line numbers and counts. +refresh_patch() { + local patch="$1" + local tmpdir="$PATCH_TMP_DIR" + local files=() + + rm -rf "$tmpdir" + mkdir -p "$tmpdir/a" + mkdir -p "$tmpdir/b" + + # Get all source files affected by the patch + get_patch_files "$patch" | mapfile -t files + + # Copy orig source files to 'a' + ( cd "$SRC" && echo "${files[@]}" | xargs cp --parents --target-directory="$tmpdir/a" ) + + # Copy patched source files to 'b' + apply_patch "$patch" --recount + ( cd "$SRC" && echo "${files[@]}" | xargs cp --parents --target-directory="$tmpdir/b" ) + revert_patch "$patch" --recount + + # Diff 'a' and 'b' to make a clean patch + ( cd "$tmpdir" && git diff --no-index --no-prefix a b > "$patch" ) || true +} + +# Copy the patches to a temporary directory, fix their lines so as not to +# affect the __LINE__ macro for otherwise unchanged functions further down the +# file, and update $PATCHES to point to the fixed patches. +fix_patches() { + local idx + local i + + rm -f "$TMP_DIR"/*.patch + + idx=0001 + for i in "${!PATCHES[@]}"; do + local old_patch="${PATCHES[$i]}" + local tmp_patch="$TMP_DIR/tmp.patch" + local patch="${PATCHES[$i]}" + local new_patch + + new_patch="$TMP_DIR/$idx-fixed-$(basename "$patch")" + + cp -f "$old_patch" "$tmp_patch" + refresh_patch "$tmp_patch" + "$FIX_PATCH_LINES" "$tmp_patch" > "$new_patch" + refresh_patch "$new_patch" + + PATCHES[i]="$new_patch" + + rm -f "$tmp_patch" + idx=$(printf "%04d" $(( 10#$idx + 1 ))) + done +} + +clean_kernel() { + local cmd=() + + cmd=("make") + cmd+=("--silent") + cmd+=("-j$JOBS") + cmd+=("clean") + + ( + cd "$SRC" + "${cmd[@]}" + ) +} + +build_kernel() { + local log="$TMP_DIR/build.log" + local cmd=() + + cmd=("make") + + # When a patch to a kernel module references a newly created unexported + # symbol which lives in vmlinux or another kernel module, the patched + # kernel build fails with the following error: + # + # ERROR: modpost: "klp_string" [fs/xfs/xfs.ko] undefined! + # + # The undefined symbols are working as designed in that case. They get + # resolved later when the livepatch module build link pulls all the + # disparate objects together into the same kernel module. + # + # It would be good to have a way to tell modpost to skip checking for + # undefined symbols altogether. For now, just convert the error to a + # warning with KBUILD_MODPOST_WARN, and grep out the warning to avoid + # confusing the user. + # + cmd+=("KBUILD_MODPOST_WARN=1") + + cmd+=("$VERBOSE") + cmd+=("-j$JOBS") + cmd+=("KCFLAGS=-ffunction-sections -fdata-sections") + cmd+=("vmlinux") + cmd+=("modules") + + ( + cd "$SRC" + "${cmd[@]}" \ + 1> >(tee -a "$log") \ + 2> >(tee -a "$log" | grep0 -v "modpost.*undefined!" >&2) + ) +} + +find_objects() { + local opts=("$@") + + # Find root-level vmlinux.o and non-root-level .ko files, + # excluding klp-tmp/ and .git/ + find "$OBJ" \( -path "$TMP_DIR" -o -path "$OBJ/.git" -o -regex "$OBJ/[^/][^/]*\.ko" \) -prune -o \ + -type f "${opts[@]}" \ + \( -name "*.ko" -o -path "$OBJ/vmlinux.o" \) \ + -printf '%P\n' +} + +# Copy all .o archives to $ORIG_DIR +copy_orig_objects() { + local files=() + + rm -rf "$ORIG_DIR" + mkdir -p "$ORIG_DIR" + + find_objects | mapfile -t files + + xtrace_save "copying orig objects" + for _file in "${files[@]}"; do + local rel_file="${_file/.ko/.o}" + local file="$OBJ/$rel_file" + local file_dir="$(dirname "$file")" + local orig_file="$ORIG_DIR/$rel_file" + local orig_dir="$(dirname "$orig_file")" + local cmd_file="$file_dir/.$(basename "$file").cmd" + + [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file" + + mkdir -p "$orig_dir" + cp -f "$file" "$orig_dir" + [[ -e "$cmd_file" ]] && cp -f "$cmd_file" "$orig_dir" + done + xtrace_restore + + mv -f "$TMP_DIR/build.log" "$ORIG_DIR" + touch "$TIMESTAMP" +} + +# Copy all changed objects to $PATCHED_DIR +copy_patched_objects() { + local files=() + local opts=() + local found=0 + + rm -rf "$PATCHED_DIR" + mkdir -p "$PATCHED_DIR" + + # Note this doesn't work with some configs, thus the 'cmp' below. + opts=("-newer") + opts+=("$TIMESTAMP") + + find_objects "${opts[@]}" | mapfile -t files + + xtrace_save "copying changed objects" + for _file in "${files[@]}"; do + local rel_file="${_file/.ko/.o}" + local file="$OBJ/$rel_file" + local orig_file="$ORIG_DIR/$rel_file" + local patched_file="$PATCHED_DIR/$rel_file" + local patched_dir="$(dirname "$patched_file")" + + [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file" + + cmp -s "$orig_file" "$file" && continue + + mkdir -p "$patched_dir" + cp -f "$file" "$patched_dir" + found=1 + done + xtrace_restore + + (( found == 0 )) && die "no changes detected" + + mv -f "$TMP_DIR/build.log" "$PATCHED_DIR" +} + +# Diff changed objects, writing output object to $DIFF_DIR +diff_objects() { + local log="$KLP_DIFF_LOG" + local files=() + + rm -rf "$DIFF_DIR" + mkdir -p "$DIFF_DIR" + + find "$PATCHED_DIR" -type f -name "*.o" | mapfile -t files + [[ ${#files[@]} -eq 0 ]] && die "no changes detected" + + # Diff all changed objects + for file in "${files[@]}"; do + local rel_file="${file#"$PATCHED_DIR"/}" + local orig_file="$rel_file" + local patched_file="$PATCHED_DIR/$rel_file" + local out_file="$DIFF_DIR/$rel_file" + local cmd=() + + mkdir -p "$(dirname "$out_file")" + + cmd=("$SRC/tools/objtool/objtool") + cmd+=("klp") + cmd+=("diff") + cmd+=("$orig_file") + cmd+=("$patched_file") + cmd+=("$out_file") + + ( + cd "$ORIG_DIR" + "${cmd[@]}" \ + 1> >(tee -a "$log") \ + 2> >(tee -a "$log" >&2) || \ + die "objtool klp diff failed" + ) + done +} + +# Build and post-process livepatch module in $KMOD_DIR +build_patch_module() { + local makefile="$KMOD_DIR/Kbuild" + local log="$KMOD_DIR/build.log" + local kmod_file + local cflags=() + local files=() + local cmd=() + + rm -rf "$KMOD_DIR" + mkdir -p "$KMOD_DIR" + + cp -f "$SRC/scripts/livepatch/init.c" "$KMOD_DIR" + + echo "obj-m := $NAME.o" > "$makefile" + echo -n "$NAME-y := init.o" >> "$makefile" + + find "$DIFF_DIR" -type f -name "*.o" | mapfile -t files + [[ ${#files[@]} -eq 0 ]] && die "no changes detected" + + for file in "${files[@]}"; do + local rel_file="${file#"$DIFF_DIR"/}" + local orig_file="$ORIG_DIR/$rel_file" + local orig_dir="$(dirname "$orig_file")" + local kmod_file="$KMOD_DIR/$rel_file" + local kmod_dir="$(dirname "$kmod_file")" + local cmd_file="$orig_dir/.$(basename "$file").cmd" + + mkdir -p "$kmod_dir" + cp -f "$file" "$kmod_dir" + [[ -e "$cmd_file" ]] && cp -f "$cmd_file" "$kmod_dir" + + # Tell kbuild this is a prebuilt object + cp -f "$file" "${kmod_file}_shipped" + + echo -n " $rel_file" >> "$makefile" + done + + echo >> "$makefile" + + cflags=("-ffunction-sections") + cflags+=("-fdata-sections") + [[ $REPLACE -eq 0 ]] && cflags+=("-DKLP_NO_REPLACE") + + cmd=("make") + cmd+=("$VERBOSE") + cmd+=("-j$JOBS") + cmd+=("--directory=.") + cmd+=("M=$KMOD_DIR") + cmd+=("KCFLAGS=${cflags[*]}") + + # Build a "normal" kernel module with init.c and the diffed objects + ( + cd "$SRC" + "${cmd[@]}" \ + 1> >(tee -a "$log") \ + 2> >(tee -a "$log" >&2) + ) + + kmod_file="$KMOD_DIR/$NAME.ko" + + # Save off the intermediate binary for debugging + cp -f "$kmod_file" "$kmod_file.orig" + + # Work around issue where slight .config change makes corrupt BTF + objcopy --remove-section=.BTF "$kmod_file" + + # Fix (and work around) linker wreckage for klp syms / relocs + "$SRC/tools/objtool/objtool" klp post-link "$kmod_file" || die "objtool klp post-link failed" + + cp -f "$kmod_file" "$OUTFILE" +} + + +################################################################################ + +process_args "$@" +do_init + +if (( SHORT_CIRCUIT <= 1 )); then + status "Validating patch(es)" + validate_patches + status "Building original kernel" + clean_kernel + build_kernel + status "Copying original object files" + copy_orig_objects +fi + +if (( SHORT_CIRCUIT <= 2 )); then + status "Fixing patch(es)" + fix_patches + apply_patches + status "Building patched kernel" + build_kernel + revert_patches + status "Copying patched object files" + copy_patched_objects +fi + +if (( SHORT_CIRCUIT <= 3 )); then + status "Diffing objects" + diff_objects +fi + +if (( SHORT_CIRCUIT <= 4 )); then + status "Building patch module: $OUTFILE" + build_patch_module +fi + +status "SUCCESS" diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 817d44394a78..4d1f9e9977eb 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -241,10 +241,12 @@ static struct symbol *next_file_symbol(struct elf *elf, struct symbol *sym) static bool is_uncorrelated_static_local(struct symbol *sym) { static const char * const vars[] = { - "__key.", - "__warned.", "__already_done.", "__func__.", + "__key.", + "__warned.", + "_entry.", + "_entry_ptr.", "_rs.", "descriptor.", "CSWTCH.", -- cgit v1.2.3 From 2c2f0b8626917c48e4b12827d296a3c654612b90 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 17 Sep 2025 09:04:09 -0700 Subject: livepatch/klp-build: Add --debug option to show cloning decisions Add a --debug option which gets passed to "objtool klp diff" to enable debug output related to cloning decisions. Acked-by: Petr Mladek Tested-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- scripts/livepatch/klp-build | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index 01ed0b66bfaf..28ee259ce5f6 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -20,7 +20,7 @@ set -o nounset # This helps keep execution in pipes so pipefail+errexit can catch errors. shopt -s lastpipe -unset SKIP_CLEANUP XTRACE +unset DEBUG_CLONE SKIP_CLEANUP XTRACE REPLACE=1 SHORT_CIRCUIT=0 @@ -120,6 +120,7 @@ Options: -v, --verbose Pass V=1 to kernel/module builds Advanced Options: + -d, --debug Show symbol/reloc cloning decisions -S, --short-circuit=STEP Start at build step (requires prior --keep-tmp) 1|orig Build original kernel (default) 2|patched Build patched kernel @@ -140,8 +141,8 @@ process_args() { local long local args - short="hj:o:vS:T" - long="help,jobs:,output:,no-replace,verbose,short-circuit:,keep-tmp" + short="hj:o:vdS:T" + long="help,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp" args=$(getopt --options "$short" --longoptions "$long" -- "$@") || { echo; usage; exit @@ -174,6 +175,11 @@ process_args() { VERBOSE="V=1" shift ;; + -d | --debug) + DEBUG_CLONE=1 + keep_tmp=1 + shift + ;; -S | --short-circuit) [[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir" keep_tmp=1 @@ -596,6 +602,7 @@ copy_patched_objects() { diff_objects() { local log="$KLP_DIFF_LOG" local files=() + local opts=() rm -rf "$DIFF_DIR" mkdir -p "$DIFF_DIR" @@ -603,6 +610,8 @@ diff_objects() { find "$PATCHED_DIR" -type f -name "*.o" | mapfile -t files [[ ${#files[@]} -eq 0 ]] && die "no changes detected" + [[ -v DEBUG_CLONE ]] && opts=("--debug") + # Diff all changed objects for file in "${files[@]}"; do local rel_file="${file#"$PATCHED_DIR"/}" @@ -616,6 +625,7 @@ diff_objects() { cmd=("$SRC/tools/objtool/objtool") cmd+=("klp") cmd+=("diff") + (( ${#opts[@]} > 0 )) && cmd+=("${opts[@]}") cmd+=("$orig_file") cmd+=("$patched_file") cmd+=("$out_file") -- cgit v1.2.3 From 78be9facfb5e711e5284ef1856401ea909eceeb2 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 17 Sep 2025 09:04:10 -0700 Subject: livepatch/klp-build: Add --show-first-changed option to show function divergence Add a --show-first-changed option to identify where changed functions begin to diverge: - Parse 'objtool klp diff' output to find changed functions. - Run objtool again on each object with --debug-checksum=. - Diff the per-instruction checksum debug output to locate the first differing instruction. This can be useful for quickly determining where and why a function changed. Acked-by: Petr Mladek Tested-by: Joe Lawrence Signed-off-by: Josh Poimboeuf --- scripts/livepatch/klp-build | 82 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index 28ee259ce5f6..881e052e7fae 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -20,7 +20,7 @@ set -o nounset # This helps keep execution in pipes so pipefail+errexit can catch errors. shopt -s lastpipe -unset DEBUG_CLONE SKIP_CLEANUP XTRACE +unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP XTRACE REPLACE=1 SHORT_CIRCUIT=0 @@ -114,6 +114,7 @@ Usage: $SCRIPT [OPTIONS] PATCH_FILE(s) Generate a livepatch module. Options: + -f, --show-first-changed Show address of first changed instruction -j, --jobs= Build jobs to run simultaneously [default: $JOBS] -o, --output= Output file [default: livepatch-.ko] --no-replace Disable livepatch atomic replace @@ -141,8 +142,8 @@ process_args() { local long local args - short="hj:o:vdS:T" - long="help,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp" + short="hfj:o:vdS:T" + long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp" args=$(getopt --options "$short" --longoptions "$long" -- "$@") || { echo; usage; exit @@ -155,6 +156,10 @@ process_args() { usage exit 0 ;; + -f | --show-first-changed) + DIFF_CHECKSUM=1 + shift + ;; -j | --jobs) JOBS="$2" shift 2 @@ -618,6 +623,7 @@ diff_objects() { local orig_file="$rel_file" local patched_file="$PATCHED_DIR/$rel_file" local out_file="$DIFF_DIR/$rel_file" + local filter=() local cmd=() mkdir -p "$(dirname "$out_file")" @@ -630,16 +636,80 @@ diff_objects() { cmd+=("$patched_file") cmd+=("$out_file") + if [[ -v DIFF_CHECKSUM ]]; then + filter=("grep0") + filter+=("-Ev") + filter+=("DEBUG: .*checksum: ") + else + filter=("cat") + fi + ( cd "$ORIG_DIR" "${cmd[@]}" \ 1> >(tee -a "$log") \ - 2> >(tee -a "$log" >&2) || \ + 2> >(tee -a "$log" | "${filter[@]}" >&2) || \ die "objtool klp diff failed" ) done } +# For each changed object, run objtool with --debug-checksum to get the +# per-instruction checksums, and then diff those to find the first changed +# instruction for each function. +diff_checksums() { + local orig_log="$ORIG_DIR/checksum.log" + local patched_log="$PATCHED_DIR/checksum.log" + local -A funcs + local cmd=() + local line + local file + local func + + gawk '/\.o: changed function: / { + sub(/:$/, "", $1) + print $1, $NF + }' "$KLP_DIFF_LOG" | mapfile -t lines + + for line in "${lines[@]}"; do + read -r file func <<< "$line" + if [[ ! -v funcs["$file"] ]]; then + funcs["$file"]="$func" + else + funcs["$file"]+=" $func" + fi + done + + cmd=("$SRC/tools/objtool/objtool") + cmd+=("--checksum") + cmd+=("--link") + cmd+=("--dry-run") + + for file in "${!funcs[@]}"; do + local opt="--debug-checksum=${funcs[$file]// /,}" + + ( + cd "$ORIG_DIR" + "${cmd[@]}" "$opt" "$file" &> "$orig_log" || \ + ( cat "$orig_log" >&2; die "objtool --debug-checksum failed" ) + + cd "$PATCHED_DIR" + "${cmd[@]}" "$opt" "$file" &> "$patched_log" || \ + ( cat "$patched_log" >&2; die "objtool --debug-checksum failed" ) + ) + + for func in ${funcs[$file]}; do + diff <( grep0 -E "^DEBUG: .*checksum: $func " "$orig_log" | sed "s|$ORIG_DIR/||") \ + <( grep0 -E "^DEBUG: .*checksum: $func " "$patched_log" | sed "s|$PATCHED_DIR/||") \ + | gawk '/^< DEBUG: / { + gsub(/:/, "") + printf "%s: %s: %s\n", $3, $5, $6 + exit + }' || true + done + done +} + # Build and post-process livepatch module in $KMOD_DIR build_patch_module() { local makefile="$KMOD_DIR/Kbuild" @@ -743,6 +813,10 @@ fi if (( SHORT_CIRCUIT <= 3 )); then status "Diffing objects" diff_objects + if [[ -v DIFF_CHECKSUM ]]; then + status "Finding first changed instructions" + diff_checksums + fi fi if (( SHORT_CIRCUIT <= 4 )); then -- cgit v1.2.3 From 5ff90d427ef841fa48608d0c19a81c48d6126d46 Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Thu, 16 Oct 2025 10:14:17 +0100 Subject: kbuild: install-extmod-build: Fix when given dir outside the build dir Commit b5e395653546 ("kbuild: install-extmod-build: Fix build when specifying KBUILD_OUTPUT") tried to address the "build" variable expecting a relative path by using `realpath --relative-base=.`, but this only works when the given directory is below the current directory. `realpath --relative-to=.` will return a relative path in all cases. Fixes: b5e395653546 ("kbuild: install-extmod-build: Fix build when specifying KBUILD_OUTPUT") Signed-off-by: James Le Cuirot Reviewed-by: Nicolas Schier Link: https://patch.msgid.link/20251016091417.9985-1-chewi@gentoo.org Signed-off-by: Nathan Chancellor --- scripts/package/install-extmod-build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/package/install-extmod-build b/scripts/package/install-extmod-build index b96538787f3d..054fdf45cc37 100755 --- a/scripts/package/install-extmod-build +++ b/scripts/package/install-extmod-build @@ -63,7 +63,7 @@ if [ "${CC}" != "${HOSTCC}" ]; then # Clear VPATH and srcroot because the source files reside in the output # directory. # shellcheck disable=SC2016 # $(MAKE) and $(build) will be expanded by Make - "${MAKE}" run-command KBUILD_RUN_COMMAND='+$(MAKE) HOSTCC='"${CC}"' VPATH= srcroot=. $(build)='"$(realpath --relative-base=. "${destdir}")"/scripts + "${MAKE}" run-command KBUILD_RUN_COMMAND='+$(MAKE) HOSTCC='"${CC}"' VPATH= srcroot=. $(build)='"$(realpath --relative-to=. "${destdir}")"/scripts rm -f "${destdir}/scripts/Kbuild" fi -- cgit v1.2.3 From 3927c4a1084c48ef97f11281a0a43ecb2cb4d6f1 Mon Sep 17 00:00:00 2001 From: Jakub Horký Date: Tue, 14 Oct 2025 17:49:32 +0200 Subject: kconfig/mconf: Initialize the default locale at startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix bug where make menuconfig doesn't initialize the default locale, which causes ncurses menu borders to be displayed incorrectly (lqqqqk) in UTF-8 terminals that don't support VT100 ACS by default, such as PuTTY. Signed-off-by: Jakub Horký Link: https://patch.msgid.link/20251014154933.3990990-1-jakub.git@horky.net [nathan: Alphabetize locale.h include] Signed-off-by: Nathan Chancellor --- scripts/kconfig/mconf.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'scripts') diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index 84ea9215c0a7..b8b7bba84a65 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -931,6 +932,8 @@ int main(int ac, char **av) signal(SIGINT, sig_handler); + setlocale(LC_ALL, ""); + if (ac > 1 && strcmp(av[1], "-s") == 0) { silent = 1; /* Silence conf_read() until the real callback is set up */ -- cgit v1.2.3 From 43c2931a95e6b295bfe9e3b90dbe0f7596933e91 Mon Sep 17 00:00:00 2001 From: Jakub Horký Date: Tue, 14 Oct 2025 16:44:06 +0200 Subject: kconfig/nconf: Initialize the default locale at startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix bug where make nconfig doesn't initialize the default locale, which causes ncurses menu borders to be displayed incorrectly (lqqqqk) in UTF-8 terminals that don't support VT100 ACS by default, such as PuTTY. Signed-off-by: Jakub Horký Link: https://patch.msgid.link/20251014144405.3975275-2-jakub.git@horky.net [nathan: Alphabetize locale.h include] Signed-off-by: Nathan Chancellor --- scripts/kconfig/nconf.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'scripts') diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index ae1fe5f60327..521700ed7152 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -7,6 +7,7 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif +#include #include #include #include @@ -1478,6 +1479,8 @@ int main(int ac, char **av) int lines, columns; char *mode; + setlocale(LC_ALL, ""); + if (ac > 1 && strcmp(av[1], "-s") == 0) { /* Silence conf_read() until the real callback is set up */ conf_set_message_callback(NULL); -- cgit v1.2.3 From a26a6c93edfeee82cb73f55e87d995eea59ddfe8 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 5 Nov 2025 15:30:27 -0700 Subject: kbuild: Strip trailing padding bytes from modules.builtin.modinfo After commit d50f21091358 ("kbuild: align modinfo section for Secureboot Authenticode EDK2 compat"), running modules_install with certain versions of kmod (such as 29.1 in Ubuntu Jammy) in certain configurations may fail with: depmod: ERROR: kmod_builtin_iter_next: unexpected string without modname prefix The additional padding bytes to ensure .modinfo is aligned within vmlinux.unstripped are unexpected by kmod, as this section has always just been null-terminated strings. Strip the trailing padding bytes from modules.builtin.modinfo after it has been extracted from vmlinux.unstripped to restore the format that kmod expects while keeping .modinfo aligned within vmlinux.unstripped to avoid regressing the Authenticode calculation fix for EDK2. Cc: stable@vger.kernel.org Fixes: d50f21091358 ("kbuild: align modinfo section for Secureboot Authenticode EDK2 compat") Reported-by: Omar Sandoval Reported-by: Samir M Reported-by: Venkat Rao Bagalkote Closes: https://lore.kernel.org/7fef7507-ad64-4e51-9bb8-c9fb6532e51e@linux.ibm.com/ Tested-by: Omar Sandoval Tested-by: Samir M Tested-by: Venkat Rao Bagalkote Reviewed-by: Nicolas Schier Link: https://patch.msgid.link/20251105-kbuild-fix-builtin-modinfo-for-kmod-v1-1-b419d8ad4606@kernel.org Signed-off-by: Nathan Chancellor --- scripts/Makefile.vmlinux | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux index ced4379550d7..cd788cac9d91 100644 --- a/scripts/Makefile.vmlinux +++ b/scripts/Makefile.vmlinux @@ -102,11 +102,24 @@ vmlinux: vmlinux.unstripped FORCE # modules.builtin.modinfo # --------------------------------------------------------------------------- +# .modinfo in vmlinux.unstripped is aligned to 8 bytes for compatibility with +# tools that expect vmlinux to have sufficiently aligned sections but the +# additional bytes used for padding .modinfo to satisfy this requirement break +# certain versions of kmod with +# +# depmod: ERROR: kmod_builtin_iter_next: unexpected string without modname prefix +# +# Strip the trailing padding bytes after extracting .modinfo to comply with +# what kmod expects to parse. +quiet_cmd_modules_builtin_modinfo = GEN $@ + cmd_modules_builtin_modinfo = $(cmd_objcopy); \ + sed -i 's/\x00\+$$/\x00/g' $@ + OBJCOPYFLAGS_modules.builtin.modinfo := -j .modinfo -O binary targets += modules.builtin.modinfo modules.builtin.modinfo: vmlinux.unstripped FORCE - $(call if_changed,objcopy) + $(call if_changed,modules_builtin_modinfo) # modules.builtin # --------------------------------------------------------------------------- -- cgit v1.2.3 From 002621a4df3c166fab1427e8e502bc15acc26b13 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 7 Nov 2025 19:29:33 +0100 Subject: kbuild: Let kernel-doc.py use PYTHON3 override It is possible to force a specific version of python to be used when building the kernel by passing PYTHON3= on the make command line. However kernel-doc.py is currently called with python3 hard-coded and thus ignores this setting. Use $(PYTHON3) to run $(KERNELDOC) so that the desired version of python is used. Signed-off-by: Jean Delvare Reviewed-by: Nicolas Schier Reviewed-by: Mauro Carvalho Chehab Link: https://patch.msgid.link/20251107192933.2bfe9e57@endymion Signed-off-by: Nathan Chancellor --- drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/i915/Makefile | 2 +- include/drm/Makefile | 2 +- scripts/Makefile.build | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4b2f7d794275..da2565e6de71 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -245,7 +245,7 @@ always-$(CONFIG_DRM_HEADER_TEST) += \ quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@) cmd_hdrtest = \ $(CC) $(c_flags) -fsyntax-only -x c /dev/null -include $< -include $<; \ - PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \ + PYTHONDONTWRITEBYTECODE=1 $(PYTHON3) $(KERNELDOC) -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \ touch $@ $(obj)/%.hdrtest: $(src)/%.h FORCE diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index e58c0c158b3a..b91575380708 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -413,7 +413,7 @@ obj-$(CONFIG_DRM_I915_GVT_KVMGT) += kvmgt.o # # Enable locally for CONFIG_DRM_I915_WERROR=y. See also scripts/Makefile.build ifdef CONFIG_DRM_I915_WERROR - cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none -Werror $< + cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(PYTHON3) $(KERNELDOC) -none -Werror $< endif # header test diff --git a/include/drm/Makefile b/include/drm/Makefile index 1df6962556ef..48fae3f167c7 100644 --- a/include/drm/Makefile +++ b/include/drm/Makefile @@ -11,7 +11,7 @@ always-$(CONFIG_DRM_HEADER_TEST) += \ quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@) cmd_hdrtest = \ $(CC) $(c_flags) -fsyntax-only -x c /dev/null -include $< -include $<; \ - PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \ + PYTHONDONTWRITEBYTECODE=1 $(PYTHON3) $(KERNELDOC) -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \ touch $@ $(obj)/%.hdrtest: $(src)/%.h FORCE diff --git a/scripts/Makefile.build b/scripts/Makefile.build index d0ee33a487be..52c08c4eb0b9 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -167,7 +167,7 @@ else ifeq ($(KBUILD_CHECKSRC),2) endif ifneq ($(KBUILD_EXTRA_WARN),) - cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none $(KDOCFLAGS) \ + cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(PYTHON3) $(KERNELDOC) -none $(KDOCFLAGS) \ $(if $(findstring 2, $(KBUILD_EXTRA_WARN)), -Wall) \ $< endif -- cgit v1.2.3 From 2092007aa32f8dd968c38751bd1b7cac9b1f738d Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 12 Nov 2025 15:32:34 -0800 Subject: objtool/klp: Only enable --checksum when needed With CONFIG_KLP_BUILD enabled, checksums are only needed during a klp-build run. There's no need to enable them for normal kernel builds. This also has the benefit of softening the xxhash dependency. Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Tested-by: Michael Kelley Link: https://patch.msgid.link/edbb1ca215e4926e02edb493b68b9d6d063e902f.1762990139.git.jpoimboe@kernel.org --- arch/x86/boot/startup/Makefile | 2 +- scripts/Makefile.lib | 1 - scripts/livepatch/klp-build | 4 ++++ 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/arch/x86/boot/startup/Makefile b/arch/x86/boot/startup/Makefile index e8fdf020b422..5e499cfb29b5 100644 --- a/arch/x86/boot/startup/Makefile +++ b/arch/x86/boot/startup/Makefile @@ -36,7 +36,7 @@ $(patsubst %.o,$(obj)/%.o,$(lib-y)): OBJECT_FILES_NON_STANDARD := y # relocations, even if other objtool actions are being deferred. # $(pi-objs): objtool-enabled = 1 -$(pi-objs): objtool-args = $(if $(delay-objtool),,$(objtool-args-y)) --noabs +$(pi-objs): objtool-args = $(if $(delay-objtool),--dry-run,$(objtool-args-y)) --noabs # # Confine the startup code by prefixing all symbols with __pi_ (for position diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index f4b33919ec37..28a1c08e3b22 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -173,7 +173,6 @@ ifdef CONFIG_OBJTOOL objtool := $(objtree)/tools/objtool/objtool -objtool-args-$(CONFIG_KLP_BUILD) += --checksum objtool-args-$(CONFIG_HAVE_JUMP_LABEL_HACK) += --hacks=jump_label objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) += --hacks=noinstr objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) += --hacks=skylake diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index 881e052e7fae..882272120c9e 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -489,8 +489,11 @@ clean_kernel() { build_kernel() { local log="$TMP_DIR/build.log" + local objtool_args=() local cmd=() + objtool_args=("--checksum") + cmd=("make") # When a patch to a kernel module references a newly created unexported @@ -513,6 +516,7 @@ build_kernel() { cmd+=("$VERBOSE") cmd+=("-j$JOBS") cmd+=("KCFLAGS=-ffunction-sections -fdata-sections") + cmd+=("OBJTOOL_ARGS=${objtool_args[*]}") cmd+=("vmlinux") cmd+=("modules") -- cgit v1.2.3 From 93863f3f859a626347ce2ec18947b11357b4ca14 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 20 Nov 2025 12:14:20 -0800 Subject: kbuild: Check for functions with ambiguous -ffunction-sections section names Commit 9c7dc1dd897a ("objtool: Warn on functions with ambiguous -ffunction-sections section names") only works for drivers which are compiled on architectures supported by objtool. Make a script to perform the same check for all architectures. Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Acked-by: Greg Kroah-Hartman Link: https://patch.msgid.link/a6a49644a34964f7e02f3a8ce43af03e72817180.1763669451.git.jpoimboe@kernel.org --- include/asm-generic/vmlinux.lds.h | 2 +- scripts/Makefile.vmlinux_o | 4 ++++ scripts/check-function-names.sh | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100755 scripts/check-function-names.sh (limited to 'scripts') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 5efe1de2209b..0cdae6f809b5 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -110,7 +110,7 @@ * .text.startup could be __attribute__((constructor)) code in a *non* * ffunction-sections object, which should be placed in .init.text; or it could * be an actual function named startup() in an ffunction-sections object, which - * should be placed in .text. Objtool will detect and complain about any such + * should be placed in .text. The build will detect and complain about any such * ambiguously named functions. */ #define TEXT_MAIN \ diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o index 20533cc0b1ee..527352c222ff 100644 --- a/scripts/Makefile.vmlinux_o +++ b/scripts/Makefile.vmlinux_o @@ -63,11 +63,15 @@ quiet_cmd_ld_vmlinux.o = LD $@ --start-group $(KBUILD_VMLINUX_LIBS) --end-group \ $(cmd_objtool) +cmd_check_function_names = $(srctree)/scripts/check-function-names.sh $@ + define rule_ld_vmlinux.o $(call cmd_and_savecmd,ld_vmlinux.o) $(call cmd,gen_objtooldep) + $(call cmd,check_function_names) endef + vmlinux.o: $(initcalls-lds) vmlinux.a $(KBUILD_VMLINUX_LIBS) FORCE $(call if_changed_rule,ld_vmlinux.o) diff --git a/scripts/check-function-names.sh b/scripts/check-function-names.sh new file mode 100755 index 000000000000..410042591cfc --- /dev/null +++ b/scripts/check-function-names.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Certain function names are disallowed due to section name ambiguities +# introduced by -ffunction-sections. +# +# See the comment above TEXT_MAIN in include/asm-generic/vmlinux.lds.h. + +objfile="$1" + +if [ ! -f "$objfile" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +bad_symbols=$(nm "$objfile" | awk '$2 ~ /^[TtWw]$/ {print $3}' | grep -E '^(startup|exit|split|unlikely|hot|unknown)(\.|$)') + +if [ -n "$bad_symbols" ]; then + echo "$bad_symbols" | while read -r sym; do + echo "$objfile: error: $sym() function name creates ambiguity with -ffunction-sections" >&2 + done + exit 1 +fi + +exit 0 -- cgit v1.2.3