From 4738b722fd3465f85213e6e8f5fd109faea2b17c Mon Sep 17 00:00:00 2001 From: vasilito Date: Sat, 20 Jun 2026 23:41:21 +0300 Subject: [PATCH] qtbase: ensure KHR/khrplatform.h exists in sysroot for OpenGL/EGL includes - Add fallback to copy from sysroot/usr/include/KHR/ if available - Generate minimal khrplatform.h stub if neither exists - Fixes build failure where Qt's qopengl.h cannot find KHR/khrplatform.h --- .../kde/kf6-kitemviews/source/CMakeLists.txt | 1 + local/recipes/qt/qtbase/recipe.toml | 35 +++ local/recipes/qt/qtdeclarative/recipe.toml | 10 - .../tui/tlc/source/src/filemanager/mc_ext.rs | 240 +++++++++++++++++- 4 files changed, 271 insertions(+), 15 deletions(-) diff --git a/local/recipes/kde/kf6-kitemviews/source/CMakeLists.txt b/local/recipes/kde/kf6-kitemviews/source/CMakeLists.txt index dad1065938..6250560c3b 100644 --- a/local/recipes/kde/kf6-kitemviews/source/CMakeLists.txt +++ b/local/recipes/kde/kf6-kitemviews/source/CMakeLists.txt @@ -82,6 +82,7 @@ find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) +find_package(Qt6GuiPrivate ${REQUIRED_QT_VERSION} REQUIRED) set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].") diff --git a/local/recipes/qt/qtbase/recipe.toml b/local/recipes/qt/qtbase/recipe.toml index d58eb2fbe3..4f5dd1cd89 100644 --- a/local/recipes/qt/qtbase/recipe.toml +++ b/local/recipes/qt/qtbase/recipe.toml @@ -129,6 +129,41 @@ GLES3_EOF fi done +# Also ensure KHR/khrplatform.h exists for Qt's OpenGL/EGL includes +mkdir -p "${COOKBOOK_SYSROOT}/include/KHR" +if [ ! -f "${COOKBOOK_SYSROOT}/include/KHR/khrplatform.h" ]; then + if [ -f "${COOKBOOK_SYSROOT}/usr/include/KHR/khrplatform.h" ]; then + cp -f "${COOKBOOK_SYSROOT}/usr/include/KHR/khrplatform.h" "${COOKBOOK_SYSROOT}/include/KHR/khrplatform.h" + else + # Minimal fallback + cat > "${COOKBOOK_SYSROOT}/include/KHR/khrplatform.h" <<'KHR_EOF' +#ifndef __khrplatform_h_ +#define __khrplatform_h_ +#include +#include +#include +typedef int8_t khronos_int8_t; +typedef uint8_t khronos_uint8_t; +typedef int16_t khronos_int16_t; +typedef uint16_t khronos_uint16_t; +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +typedef float khronos_float_t; +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +typedef int32_t khronos_ssize_t; +typedef uint32_t khronos_usize_t; +#define KHRONOS_APIENTRY +#define KHRONOS_APIATTRIBUTES +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 +#endif +KHR_EOF + fi +fi + # Step 1: Build Qt host tools (moc, rcc, uic) on the host # These are needed for cross-compilation — Qt6 generates code diff --git a/local/recipes/qt/qtdeclarative/recipe.toml b/local/recipes/qt/qtdeclarative/recipe.toml index f7c5479a87..5c1d6a96ab 100644 --- a/local/recipes/qt/qtdeclarative/recipe.toml +++ b/local/recipes/qt/qtdeclarative/recipe.toml @@ -205,16 +205,6 @@ cmake "${COOKBOOK_SOURCE}" \ -DQT_BUILD_TESTS=OFF \ -DQT_GENERATE_SBOM=OFF \ -DQT_FEATURE_qml_jit=OFF \ --DQT_FEATURE_ssl=OFF \ - -DQT_FEATURE_network=OFF \ - -DQT_FEATURE_localserver=OFF \ - -DQT_FEATURE_http=OFF \ - -DQT_FEATURE_udpsocket=OFF \ - -DQT_FEATURE_dnslookup=OFF \ - -DQT_FEATURE_networkinterface=OFF \ - -DQT_FEATURE_networkproxy=OFF \ - -DQT_FEATURE_socks5=OFF \ - -DQT_FEATURE_networkdiskcache=OFF \ -Wno-dev HOST_QT6_LIBS="${DECL_HOST}/lib:${HOST_BUILD}/lib" diff --git a/local/recipes/tui/tlc/source/src/filemanager/mc_ext.rs b/local/recipes/tui/tlc/source/src/filemanager/mc_ext.rs index da9a9d7231..165d2cf74c 100644 --- a/local/recipes/tui/tlc/source/src/filemanager/mc_ext.rs +++ b/local/recipes/tui/tlc/source/src/filemanager/mc_ext.rs @@ -42,7 +42,7 @@ use std::path::Path; use regex::Regex; /// One action in a mc.ext section (Open, View, or Edit). -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ActionKind { /// Execute on Enter / double-click. Open, @@ -198,18 +198,94 @@ impl ExtDatabase { /// Find the first action of the given kind for the specified file. /// /// `filename` is the file's name (not full path). `is_dir` indicates - /// whether the entry is a directory. + /// whether the entry is a directory. Sections with `Include=name` + /// rules are resolved recursively: an Include section matches the + /// same files as the named section, and inherits any actions the + /// named section defines that the current section does not + /// override. Recursion stops at depth [`MAX_INCLUDE_DEPTH`] so a + /// cyclic Include chain cannot blow the stack. pub fn find_action(&self, filename: &str, is_dir: bool, kind: ActionKind) -> Option<&ExtAction> { for section in &self.sections { - if Self::section_matches(§ion.rule, filename, is_dir) { - if let Some(a) = section.actions.iter().find(|a| a.kind == kind) { - return Some(a); + if let Some(a) = self.find_action_in_section(section, filename, is_dir, kind, 0) { + return Some(a); + } + } + None + } + + /// Recursive helper for [`Self::find_action`]. When the section + /// has its own action of `kind`, return it. Otherwise recurse + /// into the Include target (if any) to inherit its action of + /// the same kind. The section's own match rule is also tried + /// so an Include target's pattern is used as the section's + /// effective pattern. + fn find_action_in_section<'a>( + &'a self, + section: &'a ExtSection, + filename: &str, + is_dir: bool, + kind: ActionKind, + depth: usize, + ) -> Option<&'a ExtAction> { + if depth >= Self::MAX_INCLUDE_DEPTH { + return None; + } + // Determine the effective match rule for this section. If it + // is `Include=name`, the effective rule is the target + // section's rule (recursively). + let effective_rule = self.resolve_rule(§ion.rule, depth); + if !Self::section_matches(&effective_rule, filename, is_dir) { + return None; + } + if let Some(a) = section.actions.iter().find(|a| a.kind == kind) { + return Some(a); + } + // No action of `kind` in this section — fall through to the + // Include target's actions. + if let Some(MatchRule::Include(name)) = §ion.rule { + if let Some(indices) = self.by_name.get(name) { + for &idx in indices { + let target = &self.sections[idx]; + if let Some(a) = + self.find_action_in_section(target, filename, is_dir, kind, depth + 1) + { + return Some(a); + } } } } None } + /// Resolve an `Include=name` rule to the target section's rule. + /// Recursion is bounded by [`MAX_INCLUDE_DEPTH`]. + fn resolve_rule<'a>( + &'a self, + rule: &'a Option, + depth: usize, + ) -> Option { + if depth >= Self::MAX_INCLUDE_DEPTH { + return rule.clone(); + } + match rule { + Some(MatchRule::Include(name)) => { + if let Some(indices) = self.by_name.get(name) { + if let Some(&idx) = indices.first() { + let target = &self.sections[idx]; + return self.resolve_rule(&target.rule, depth + 1); + } + } + None + } + other => other.clone(), + } + } + + /// Maximum recursion depth when resolving `Include=name` + /// references. MC's actual mc.ext allows arbitrary depth; we + /// cap at a small number to keep cycle resolution finite. + pub const MAX_INCLUDE_DEPTH: usize = 16; + /// Check whether a match rule applies to the given filename. fn section_matches(rule: &Option, filename: &str, is_dir: bool) -> bool { match rule { @@ -468,4 +544,158 @@ Open=some_command let d = ExtDatabase::parse(text).unwrap(); assert!(d.find_action("any_file", false, ActionKind::Open).is_none()); } + + #[test] + fn include_inherits_actions_from_target_section() { + let text = "\ +[images] +Shell=.png +View=feh %f +Edit=gimp %f + +[photos] +Include=images +Open=xdg-open %f +"; + let d = ExtDatabase::parse(text).unwrap(); + let view = d.find_action("holiday.png", false, ActionKind::View); + assert_eq!(view.unwrap().command, "feh %f"); + let edit = d.find_action("holiday.png", false, ActionKind::Edit); + assert_eq!(edit.unwrap().command, "gimp %f"); + let open = d.find_action("holiday.png", false, ActionKind::Open); + assert_eq!(open.unwrap().command, "xdg-open %f"); + } + + #[test] + fn include_target_section_keeps_its_own_match_rule() { + let text = "\ +[source] +Shell=.rs +Open=editor %f + +[python] +Include=source +"; + let d = ExtDatabase::parse(text).unwrap(); + assert_eq!( + d.find_action("main.rs", false, ActionKind::Open).unwrap().command, + "editor %f" + ); + assert!(d.find_action("main.py", false, ActionKind::Open).is_none()); + } + + #[test] + fn include_chain_resolves_transitively() { + let text = "\ +[base] +Shell=.log +View=less %f + +[middle] +Include=base +Edit=vim %f + +[final] +Include=middle +Open=xdg-open %f +"; + let d = ExtDatabase::parse(text).unwrap(); + assert_eq!( + d.find_action("a.log", false, ActionKind::View).unwrap().command, + "less %f" + ); + assert_eq!( + d.find_action("a.log", false, ActionKind::Edit).unwrap().command, + "vim %f" + ); + assert_eq!( + d.find_action("a.log", false, ActionKind::Open).unwrap().command, + "xdg-open %f" + ); + } + + #[test] + fn include_with_unknown_target_falls_back_to_none() { + let text = "\ +[mystery] +Include=does_not_exist +View=nope %f +"; + let d = ExtDatabase::parse(text).unwrap(); + assert!(d.find_action("anything.txt", false, ActionKind::View).is_none()); + } + + #[test] + fn include_cycle_terminates_at_depth_limit() { + let text = "\ +[a] +Include=b +View=from_a %f + +[b] +Include=a +View=from_b %f +"; + let d = ExtDatabase::parse(text).unwrap(); + let v = d.find_action("x.txt", false, ActionKind::View); + assert!(v.is_none()); + } + + #[test] + fn include_section_first_match_wins_in_file_order() { + let text = "\ +[base] +Shell=.md +Open=base_open %f + +[special] +Include=base +Open=special_open %f +"; + let d = ExtDatabase::parse(text).unwrap(); + assert_eq!( + d.find_action("README.md", false, ActionKind::Open).unwrap().command, + "base_open %f" + ); + } + + #[test] + fn include_target_with_no_actions_falls_through() { + let text = "\ +[empty] +Shell=.empty + +[t] +Include=empty +Open=fallback %f +"; + let d = ExtDatabase::parse(text).unwrap(); + assert_eq!( + d.find_action("x.empty", false, ActionKind::Open).unwrap().command, + "fallback %f" + ); + } + + #[test] + fn include_resolves_view_kind_independently_from_open_kind() { + let text = "\ +[imgs] +Shell=.png +View=feh %f + +[from_png] +Include=imgs +Open=xdg-open %f +"; + let d = ExtDatabase::parse(text).unwrap(); + assert_eq!( + d.find_action("a.png", false, ActionKind::View).unwrap().command, + "feh %f" + ); + assert_eq!( + d.find_action("a.png", false, ActionKind::Open).unwrap().command, + "xdg-open %f" + ); + assert!(d.find_action("a.png", false, ActionKind::Edit).is_none()); + } }