From patchwork Tue Aug 18 06:30:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95591 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1A44DC5DF86 for ; Tue, 18 Aug 2026 06:31:44 +0000 (UTC) Received: from mta-64-227.siemens.flowmailer.net (mta-64-227.siemens.flowmailer.net [185.136.64.227]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.3141.1787034698731700073 for ; Mon, 17 Aug 2026 23:31:40 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=SAAnbinz; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.227, mailfrom: fm-1329275-202608180631368d027decd400020756-9tyko2@rts-flowmailer.siemens.com) Received: by mta-64-227.siemens.flowmailer.net with ESMTPSA id 202608180631368d027decd400020756 for ; Tue, 18 Aug 2026 08:31:36 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=dueRr0yadYy37iV0XVbp5ftoJlnMu0N9p1HBbYDc9mQ=; b=SAAnbinzzTdhRK23WVPxBYo57ZbbPUlx+GUTang8Z20KM813HnRMSPnErbtcrllQ1mT/Ho T8KMNTSfoPNm+9XiIuCYDUXg/FM+SiQwn/iw/KZZquUY76wZm0nuU8sqQCYd1EYbVGq7dRYZ tPBm7x6YiWJqdtbReODxjibC+kPB9l1DKRk0Jgdya1MEGiI3R54SB1ByO9rF25/1FdQ3DjOy pKW2oj+bKghLI71ZajReZM9ql9Vtjp5gXXNixWLGGHQBpV8LhZZkc65FFqk8o0zscwMHBPpm 3HKWJTifjK+gOGlh5EnKSX+GMYCAc4s2dk8dkwAnM2KYLP4M34p6ApeA==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Alex Kiernan , Mathieu Dubois-Briand , Richard Purdie Subject: [PATCH v2 01/14] wpa-supplicant: Use wpa_supplicant.conf from ${S} Date: Tue, 18 Aug 2026 08:30:40 +0200 Message-ID: <20260818063120.73042-2-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:44 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243657 From: Alex Kiernan Rather than ship an example wpa_supplicant.conf from 2013, install the upstream all options example config. This is only the version which documents all options the minimal version installed into /etc stays the same. Signed-off-by: Alex Kiernan Signed-off-by: Mathieu Dubois-Briand Signed-off-by: Richard Purdie --- .../wpa-supplicant/wpa_supplicant.conf | 690 ------------------ .../wpa-supplicant/wpa-supplicant_2.12.bb | 3 +- 2 files changed, 1 insertion(+), 692 deletions(-) delete mode 100644 meta/recipes-connectivity/wpa-supplicant/wpa-supplicant/wpa_supplicant.conf diff --git a/meta/recipes-connectivity/wpa-supplicant/wpa-supplicant/wpa_supplicant.conf b/meta/recipes-connectivity/wpa-supplicant/wpa-supplicant/wpa_supplicant.conf deleted file mode 100644 index 68258f5ee2..0000000000 --- a/meta/recipes-connectivity/wpa-supplicant/wpa-supplicant/wpa_supplicant.conf +++ /dev/null @@ -1,690 +0,0 @@ -##### Example wpa_supplicant configuration file ############################### -# -# This file describes configuration file format and lists all available option. -# Please also take a look at simpler configuration examples in 'examples' -# subdirectory. -# -# Empty lines and lines starting with # are ignored - -# NOTE! This file may contain password information and should probably be made -# readable only by root user on multiuser systems. - -# Note: All file paths in this configuration file should use full (absolute, -# not relative to working directory) path in order to allow working directory -# to be changed. This can happen if wpa_supplicant is run in the background. - -# Whether to allow wpa_supplicant to update (overwrite) configuration -# -# This option can be used to allow wpa_supplicant to overwrite configuration -# file whenever configuration is changed (e.g., new network block is added with -# wpa_cli or wpa_gui, or a password is changed). This is required for -# wpa_cli/wpa_gui to be able to store the configuration changes permanently. -# Please note that overwriting configuration file will remove the comments from -# it. -#update_config=1 - -# global configuration (shared by all network blocks) -# -# Parameters for the control interface. If this is specified, wpa_supplicant -# will open a control interface that is available for external programs to -# manage wpa_supplicant. The meaning of this string depends on which control -# interface mechanism is used. For all cases, the existence of this parameter -# in configuration is used to determine whether the control interface is -# enabled. -# -# For UNIX domain sockets (default on Linux and BSD): This is a directory that -# will be created for UNIX domain sockets for listening to requests from -# external programs (CLI/GUI, etc.) for status information and configuration. -# The socket file will be named based on the interface name, so multiple -# wpa_supplicant processes can be run at the same time if more than one -# interface is used. -# /var/run/wpa_supplicant is the recommended directory for sockets and by -# default, wpa_cli will use it when trying to connect with wpa_supplicant. -# -# Access control for the control interface can be configured by setting the -# directory to allow only members of a group to use sockets. This way, it is -# possible to run wpa_supplicant as root (since it needs to change network -# configuration and open raw sockets) and still allow GUI/CLI components to be -# run as non-root users. However, since the control interface can be used to -# change the network configuration, this access needs to be protected in many -# cases. By default, wpa_supplicant is configured to use gid 0 (root). If you -# want to allow non-root users to use the control interface, add a new group -# and change this value to match with that group. Add users that should have -# control interface access to this group. If this variable is commented out or -# not included in the configuration file, group will not be changed from the -# value it got by default when the directory or socket was created. -# -# When configuring both the directory and group, use following format: -# DIR=/var/run/wpa_supplicant GROUP=wheel -# DIR=/var/run/wpa_supplicant GROUP=0 -# (group can be either group name or gid) -# -# For UDP connections (default on Windows): The value will be ignored. This -# variable is just used to select that the control interface is to be created. -# The value can be set to, e.g., udp (ctrl_interface=udp) -# -# For Windows Named Pipe: This value can be used to set the security descriptor -# for controlling access to the control interface. Security descriptor can be -# set using Security Descriptor String Format (see http://msdn.microsoft.com/ -# library/default.asp?url=/library/en-us/secauthz/security/ -# security_descriptor_string_format.asp). The descriptor string needs to be -# prefixed with SDDL=. For example, ctrl_interface=SDDL=D: would set an empty -# DACL (which will reject all connections). See README-Windows.txt for more -# information about SDDL string format. -# -ctrl_interface=/var/run/wpa_supplicant - -# IEEE 802.1X/EAPOL version -# wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which defines -# EAPOL version 2. However, there are many APs that do not handle the new -# version number correctly (they seem to drop the frames completely). In order -# to make wpa_supplicant interoperate with these APs, the version number is set -# to 1 by default. This configuration value can be used to set it to the new -# version (2). -eapol_version=1 - -# AP scanning/selection -# By default, wpa_supplicant requests driver to perform AP scanning and then -# uses the scan results to select a suitable AP. Another alternative is to -# allow the driver to take care of AP scanning and selection and use -# wpa_supplicant just to process EAPOL frames based on IEEE 802.11 association -# information from the driver. -# 1: wpa_supplicant initiates scanning and AP selection -# 0: driver takes care of scanning, AP selection, and IEEE 802.11 association -# parameters (e.g., WPA IE generation); this mode can also be used with -# non-WPA drivers when using IEEE 802.1X mode; do not try to associate with -# APs (i.e., external program needs to control association). This mode must -# also be used when using wired Ethernet drivers. -# 2: like 0, but associate with APs using security policy and SSID (but not -# BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to -# enable operation with hidden SSIDs and optimized roaming; in this mode, -# the network blocks in the configuration file are tried one by one until -# the driver reports successful association; each network block should have -# explicit security policy (i.e., only one option in the lists) for -# key_mgmt, pairwise, group, proto variables -ap_scan=1 - -# EAP fast re-authentication -# By default, fast re-authentication is enabled for all EAP methods that -# support it. This variable can be used to disable fast re-authentication. -# Normally, there is no need to disable this. -fast_reauth=1 - -# OpenSSL Engine support -# These options can be used to load OpenSSL engines. -# The two engines that are supported currently are shown below: -# They are both from the opensc project (http://www.opensc.org/) -# By default no engines are loaded. -# make the opensc engine available -#opensc_engine_path=/usr/lib/opensc/engine_opensc.so -# make the pkcs11 engine available -#pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so -# configure the path to the pkcs11 module required by the pkcs11 engine -#pkcs11_module_path=/usr/lib/pkcs11/opensc-pkcs11.so - -# Dynamic EAP methods -# If EAP methods were built dynamically as shared object files, they need to be -# loaded here before being used in the network blocks. By default, EAP methods -# are included statically in the build, so these lines are not needed -#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_tls.so -#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_md5.so - -# Driver interface parameters -# This field can be used to configure arbitrary driver interace parameters. The -# format is specific to the selected driver interface. This field is not used -# in most cases. -#driver_param="field=value" - -# Maximum lifetime for PMKSA in seconds; default 43200 -#dot11RSNAConfigPMKLifetime=43200 -# Threshold for reauthentication (percentage of PMK lifetime); default 70 -#dot11RSNAConfigPMKReauthThreshold=70 -# Timeout for security association negotiation in seconds; default 60 -#dot11RSNAConfigSATimeout=60 - -# network block -# -# Each network (usually AP's sharing the same SSID) is configured as a separate -# block in this configuration file. The network blocks are in preference order -# (the first match is used). -# -# network block fields: -# -# disabled: -# 0 = this network can be used (default) -# 1 = this network block is disabled (can be enabled through ctrl_iface, -# e.g., with wpa_cli or wpa_gui) -# -# id_str: Network identifier string for external scripts. This value is passed -# to external action script through wpa_cli as WPA_ID_STR environment -# variable to make it easier to do network specific configuration. -# -# ssid: SSID (mandatory); either as an ASCII string with double quotation or -# as hex string; network name -# -# scan_ssid: -# 0 = do not scan this SSID with specific Probe Request frames (default) -# 1 = scan with SSID-specific Probe Request frames (this can be used to -# find APs that do not accept broadcast SSID or use multiple SSIDs; -# this will add latency to scanning, so enable this only when needed) -# -# bssid: BSSID (optional); if set, this network block is used only when -# associating with the AP using the configured BSSID -# -# priority: priority group (integer) -# By default, all networks will get same priority group (0). If some of the -# networks are more desirable, this field can be used to change the order in -# which wpa_supplicant goes through the networks when selecting a BSS. The -# priority groups will be iterated in decreasing priority (i.e., the larger the -# priority value, the sooner the network is matched against the scan results). -# Within each priority group, networks will be selected based on security -# policy, signal strength, etc. -# Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are not -# using this priority to select the order for scanning. Instead, they try the -# networks in the order that used in the configuration file. -# -# mode: IEEE 802.11 operation mode -# 0 = infrastructure (Managed) mode, i.e., associate with an AP (default) -# 1 = IBSS (ad-hoc, peer-to-peer) -# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP) -# and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In addition, ap_scan has -# to be set to 2 for IBSS. WPA-None requires following network block options: -# proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or CCMP, but not -# both), and psk must also be set. -# -# proto: list of accepted protocols -# WPA = WPA/IEEE 802.11i/D3.0 -# RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN) -# If not set, this defaults to: WPA RSN -# -# key_mgmt: list of accepted authenticated key management protocols -# WPA-PSK = WPA pre-shared key (this requires 'psk' field) -# WPA-EAP = WPA using EAP authentication (this can use an external -# program, e.g., Xsupplicant, for IEEE 802.1X EAP Authentication -# IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically -# generated WEP keys -# NONE = WPA is not used; plaintext or static WEP could be used -# If not set, this defaults to: WPA-PSK WPA-EAP -# -# auth_alg: list of allowed IEEE 802.11 authentication algorithms -# OPEN = Open System authentication (required for WPA/WPA2) -# SHARED = Shared Key authentication (requires static WEP keys) -# LEAP = LEAP/Network EAP (only used with LEAP) -# If not set, automatic selection is used (Open System with LEAP enabled if -# LEAP is allowed as one of the EAP methods). -# -# pairwise: list of accepted pairwise (unicast) ciphers for WPA -# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] -# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] -# NONE = Use only Group Keys (deprecated, should not be included if APs support -# pairwise keys) -# If not set, this defaults to: CCMP TKIP -# -# group: list of accepted group (broadcast/multicast) ciphers for WPA -# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] -# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] -# WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key -# WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11] -# If not set, this defaults to: CCMP TKIP WEP104 WEP40 -# -# psk: WPA preshared key; 256-bit pre-shared key -# The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e., -# 32 bytes or as an ASCII passphrase (in which case, the real PSK will be -# generated using the passphrase and SSID). ASCII passphrase must be between -# 8 and 63 characters (inclusive). -# This field is not needed, if WPA-EAP is used. -# Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys -# from ASCII passphrase. This process uses lot of CPU and wpa_supplicant -# startup and reconfiguration time can be optimized by generating the PSK only -# only when the passphrase or SSID has actually changed. -# -# eapol_flags: IEEE 802.1X/EAPOL options (bit field) -# Dynamic WEP key required for non-WPA mode -# bit0 (1): require dynamically generated unicast WEP key -# bit1 (2): require dynamically generated broadcast WEP key -# (3 = require both keys; default) -# Note: When using wired authentication, eapol_flags must be set to 0 for the -# authentication to be completed successfully. -# -# proactive_key_caching: -# Enable/disable opportunistic PMKSA caching for WPA2. -# 0 = disabled (default) -# 1 = enabled -# -# wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or -# hex without quotation, e.g., 0102030405) -# wep_tx_keyidx: Default WEP key index (TX) (0..3) -# -# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e DLS) is -# allowed. This is only used with RSN/WPA2. -# 0 = disabled (default) -# 1 = enabled -#peerkey=1 -# -# Following fields are only used with internal EAP implementation. -# eap: space-separated list of accepted EAP methods -# MD5 = EAP-MD5 (unsecure and does not generate keying material -> -# cannot be used with WPA; to be used as a Phase 2 method -# with EAP-PEAP or EAP-TTLS) -# MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used -# as a Phase 2 method with EAP-PEAP or EAP-TTLS) -# OTP = EAP-OTP (cannot be used separately with WPA; to be used -# as a Phase 2 method with EAP-PEAP or EAP-TTLS) -# GTC = EAP-GTC (cannot be used separately with WPA; to be used -# as a Phase 2 method with EAP-PEAP or EAP-TTLS) -# TLS = EAP-TLS (client and server certificate) -# PEAP = EAP-PEAP (with tunnelled EAP authentication) -# TTLS = EAP-TTLS (with tunnelled EAP or PAP/CHAP/MSCHAP/MSCHAPV2 -# authentication) -# If not set, all compiled in methods are allowed. -# -# identity: Identity string for EAP -# anonymous_identity: Anonymous identity string for EAP (to be used as the -# unencrypted identity with EAP types that support different tunnelled -# identity, e.g., EAP-TTLS) -# password: Password string for EAP -# ca_cert: File path to CA certificate file (PEM/DER). This file can have one -# or more trusted CA certificates. If ca_cert and ca_path are not -# included, server certificate will not be verified. This is insecure and -# a trusted CA certificate should always be configured when using -# EAP-TLS/TTLS/PEAP. Full path should be used since working directory may -# change when wpa_supplicant is run in the background. -# On Windows, trusted CA certificates can be loaded from the system -# certificate store by setting this to cert_store://, e.g., -# ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT". -# Note that when running wpa_supplicant as an application, the user -# certificate store (My user account) is used, whereas computer store -# (Computer account) is used when running wpasvc as a service. -# ca_path: Directory path for CA certificate files (PEM). This path may -# contain multiple CA certificates in OpenSSL format. Common use for this -# is to point to system trusted CA list which is often installed into -# directory like /etc/ssl/certs. If configured, these certificates are -# added to the list of trusted CAs. ca_cert may also be included in that -# case, but it is not required. -# client_cert: File path to client certificate file (PEM/DER) -# Full path should be used since working directory may change when -# wpa_supplicant is run in the background. -# Alternatively, a named configuration blob can be used by setting this -# to blob://. -# private_key: File path to client private key file (PEM/DER/PFX) -# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be -# commented out. Both the private key and certificate will be read from -# the PKCS#12 file in this case. Full path should be used since working -# directory may change when wpa_supplicant is run in the background. -# Windows certificate store can be used by leaving client_cert out and -# configuring private_key in one of the following formats: -# cert://substring_to_match -# hash://certificate_thumbprint_in_hex -# for example: private_key="hash://63093aa9c47f56ae88334c7b65a4" -# Note that when running wpa_supplicant as an application, the user -# certificate store (My user account) is used, whereas computer store -# (Computer account) is used when running wpasvc as a service. -# Alternatively, a named configuration blob can be used by setting this -# to blob://. -# private_key_passwd: Password for private key file (if left out, this will be -# asked through control interface) -# dh_file: File path to DH/DSA parameters file (in PEM format) -# This is an optional configuration file for setting parameters for an -# ephemeral DH key exchange. In most cases, the default RSA -# authentication does not use this configuration. However, it is possible -# setup RSA to use ephemeral DH key exchange. In addition, ciphers with -# DSA keys always use ephemeral DH keys. This can be used to achieve -# forward secrecy. If the file is in DSA parameters format, it will be -# automatically converted into DH params. -# subject_match: Substring to be matched against the subject of the -# authentication server certificate. If this string is set, the server -# sertificate is only accepted if it contains this string in the subject. -# The subject string is in following format: -# /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com -# altsubject_match: Semicolon separated string of entries to be matched against -# the alternative subject name of the authentication server certificate. -# If this string is set, the server sertificate is only accepted if it -# contains one of the entries in an alternative subject name extension. -# altSubjectName string is in following format: TYPE:VALUE -# Example: EMAIL:server@example.com -# Example: DNS:server.example.com;DNS:server2.example.com -# Following types are supported: EMAIL, DNS, URI -# phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters -# (string with field-value pairs, e.g., "peapver=0" or -# "peapver=1 peaplabel=1") -# 'peapver' can be used to force which PEAP version (0 or 1) is used. -# 'peaplabel=1' can be used to force new label, "client PEAP encryption", -# to be used during key derivation when PEAPv1 or newer. Most existing -# PEAPv1 implementation seem to be using the old label, "client EAP -# encryption", and wpa_supplicant is now using that as the default value. -# Some servers, e.g., Radiator, may require peaplabel=1 configuration to -# interoperate with PEAPv1; see eap_testing.txt for more details. -# 'peap_outer_success=0' can be used to terminate PEAP authentication on -# tunneled EAP-Success. This is required with some RADIUS servers that -# implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g., -# Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode) -# include_tls_length=1 can be used to force wpa_supplicant to include -# TLS Message Length field in all TLS messages even if they are not -# fragmented. -# sim_min_num_chal=3 can be used to configure EAP-SIM to require three -# challenges (by default, it accepts 2 or 3) -# phase2: Phase2 (inner authentication with TLS tunnel) parameters -# (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or -# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS) -# Following certificate/private key fields are used in inner Phase2 -# authentication when using EAP-TTLS or EAP-PEAP. -# ca_cert2: File path to CA certificate file. This file can have one or more -# trusted CA certificates. If ca_cert2 and ca_path2 are not included, -# server certificate will not be verified. This is insecure and a trusted -# CA certificate should always be configured. -# ca_path2: Directory path for CA certificate files (PEM) -# client_cert2: File path to client certificate file -# private_key2: File path to client private key file -# private_key2_passwd: Password for private key file -# dh_file2: File path to DH/DSA parameters file (in PEM format) -# subject_match2: Substring to be matched against the subject of the -# authentication server certificate. -# altsubject_match2: Substring to be matched against the alternative subject -# name of the authentication server certificate. -# -# fragment_size: Maximum EAP fragment size in bytes (default 1398). -# This value limits the fragment size for EAP methods that support -# fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set -# small enough to make the EAP messages fit in MTU of the network -# interface used for EAPOL. The default value is suitable for most -# cases. -# -# EAP-PSK variables: -# eappsk: 16-byte (128-bit, 32 hex digits) pre-shared key in hex format -# nai: user NAI -# -# EAP-PAX variables: -# eappsk: 16-byte (128-bit, 32 hex digits) pre-shared key in hex format -# -# EAP-SAKE variables: -# eappsk: 32-byte (256-bit, 64 hex digits) pre-shared key in hex format -# (this is concatenation of Root-Secret-A and Root-Secret-B) -# nai: user NAI (PEERID) -# -# EAP-GPSK variables: -# eappsk: Pre-shared key in hex format (at least 128 bits, i.e., 32 hex digits) -# nai: user NAI (ID_Client) -# -# EAP-FAST variables: -# pac_file: File path for the PAC entries. wpa_supplicant will need to be able -# to create this file and write updates to it when PAC is being -# provisioned or refreshed. Full path to the file should be used since -# working directory may change when wpa_supplicant is run in the -# background. Alternatively, a named configuration blob can be used by -# setting this to blob:// -# phase1: fast_provisioning=1 option enables in-line provisioning of EAP-FAST -# credentials (PAC) -# -# wpa_supplicant supports number of "EAP workarounds" to work around -# interoperability issues with incorrectly behaving authentication servers. -# These are enabled by default because some of the issues are present in large -# number of authentication servers. Strict EAP conformance mode can be -# configured by disabling workarounds with eap_workaround=0. - -# Example blocks: - -# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers -network={ - ssid="simple" - psk="very secret passphrase" - priority=5 -} - -# Same as previous, but request SSID-specific scanning (for APs that reject -# broadcast SSID) -network={ - ssid="second ssid" - scan_ssid=1 - psk="very secret passphrase" - priority=2 -} - -# Only WPA-PSK is used. Any valid cipher combination is accepted. -network={ - ssid="example" - proto=WPA - key_mgmt=WPA-PSK - pairwise=CCMP TKIP - group=CCMP TKIP WEP104 WEP40 - psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb - priority=2 -} - -# Only WPA-EAP is used. Both CCMP and TKIP is accepted. An AP that used WEP104 -# or WEP40 as the group cipher will not be accepted. -network={ - ssid="example" - proto=RSN - key_mgmt=WPA-EAP - pairwise=CCMP TKIP - group=CCMP TKIP - eap=TLS - identity="user@example.com" - ca_cert="/etc/cert/ca.pem" - client_cert="/etc/cert/user.pem" - private_key="/etc/cert/user.prv" - private_key_passwd="password" - priority=1 -} - -# EAP-PEAP/MSCHAPv2 configuration for RADIUS servers that use the new peaplabel -# (e.g., Radiator) -network={ - ssid="example" - key_mgmt=WPA-EAP - eap=PEAP - identity="user@example.com" - password="foobar" - ca_cert="/etc/cert/ca.pem" - phase1="peaplabel=1" - phase2="auth=MSCHAPV2" - priority=10 -} - -# EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the -# unencrypted use. Real identity is sent only within an encrypted TLS tunnel. -network={ - ssid="example" - key_mgmt=WPA-EAP - eap=TTLS - identity="user@example.com" - anonymous_identity="anonymous@example.com" - password="foobar" - ca_cert="/etc/cert/ca.pem" - priority=2 -} - -# EAP-TTLS/MSCHAPv2 configuration with anonymous identity for the unencrypted -# use. Real identity is sent only within an encrypted TLS tunnel. -network={ - ssid="example" - key_mgmt=WPA-EAP - eap=TTLS - identity="user@example.com" - anonymous_identity="anonymous@example.com" - password="foobar" - ca_cert="/etc/cert/ca.pem" - phase2="auth=MSCHAPV2" -} - -# WPA-EAP, EAP-TTLS with different CA certificate used for outer and inner -# authentication. -network={ - ssid="example" - key_mgmt=WPA-EAP - eap=TTLS - # Phase1 / outer authentication - anonymous_identity="anonymous@example.com" - ca_cert="/etc/cert/ca.pem" - # Phase 2 / inner authentication - phase2="autheap=TLS" - ca_cert2="/etc/cert/ca2.pem" - client_cert2="/etc/cer/user.pem" - private_key2="/etc/cer/user.prv" - private_key2_passwd="password" - priority=2 -} - -# Both WPA-PSK and WPA-EAP is accepted. Only CCMP is accepted as pairwise and -# group cipher. -network={ - ssid="example" - bssid=00:11:22:33:44:55 - proto=WPA RSN - key_mgmt=WPA-PSK WPA-EAP - pairwise=CCMP - group=CCMP - psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb -} - -# Special characters in SSID, so use hex string. Default to WPA-PSK, WPA-EAP -# and all valid ciphers. -network={ - ssid=00010203 - psk=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f -} - - -# IEEE 802.1X/EAPOL with dynamically generated WEP keys (i.e., no WPA) using -# EAP-TLS for authentication and key generation; require both unicast and -# broadcast WEP keys. -network={ - ssid="1x-test" - key_mgmt=IEEE8021X - eap=TLS - identity="user@example.com" - ca_cert="/etc/cert/ca.pem" - client_cert="/etc/cert/user.pem" - private_key="/etc/cert/user.prv" - private_key_passwd="password" - eapol_flags=3 -} - - -# LEAP with dynamic WEP keys -network={ - ssid="leap-example" - key_mgmt=IEEE8021X - eap=LEAP - identity="user" - password="foobar" -} - -# Plaintext connection (no WPA, no IEEE 802.1X) -network={ - ssid="plaintext-test" - key_mgmt=NONE -} - - -# Shared WEP key connection (no WPA, no IEEE 802.1X) -network={ - ssid="static-wep-test" - key_mgmt=NONE - wep_key0="abcde" - wep_key1=0102030405 - wep_key2="1234567890123" - wep_tx_keyidx=0 - priority=5 -} - - -# Shared WEP key connection (no WPA, no IEEE 802.1X) using Shared Key -# IEEE 802.11 authentication -network={ - ssid="static-wep-test2" - key_mgmt=NONE - wep_key0="abcde" - wep_key1=0102030405 - wep_key2="1234567890123" - wep_tx_keyidx=0 - priority=5 - auth_alg=SHARED -} - - -# IBSS/ad-hoc network with WPA-None/TKIP. -network={ - ssid="test adhoc" - mode=1 - proto=WPA - key_mgmt=WPA-NONE - pairwise=NONE - group=TKIP - psk="secret passphrase" -} - - -# Catch all example that allows more or less all configuration modes -network={ - ssid="example" - scan_ssid=1 - key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE - pairwise=CCMP TKIP - group=CCMP TKIP WEP104 WEP40 - psk="very secret passphrase" - eap=TTLS PEAP TLS - identity="user@example.com" - password="foobar" - ca_cert="/etc/cert/ca.pem" - client_cert="/etc/cert/user.pem" - private_key="/etc/cert/user.prv" - private_key_passwd="password" - phase1="peaplabel=0" -} - -# Example of EAP-TLS with smartcard (openssl engine) -network={ - ssid="example" - key_mgmt=WPA-EAP - eap=TLS - proto=RSN - pairwise=CCMP TKIP - group=CCMP TKIP - identity="user@example.com" - ca_cert="/etc/cert/ca.pem" - client_cert="/etc/cert/user.pem" - - engine=1 - - # The engine configured here must be available. Look at - # OpenSSL engine support in the global section. - # The key available through the engine must be the private key - # matching the client certificate configured above. - - # use the opensc engine - #engine_id="opensc" - #key_id="45" - - # use the pkcs11 engine - engine_id="pkcs11" - key_id="id_45" - - # Optional PIN configuration; this can be left out and PIN will be - # asked through the control interface - pin="1234" -} - -# Example configuration showing how to use an inlined blob as a CA certificate -# data instead of using external file -network={ - ssid="example" - key_mgmt=WPA-EAP - eap=TTLS - identity="user@example.com" - anonymous_identity="anonymous@example.com" - password="foobar" - ca_cert="blob://exampleblob" - priority=20 -} - -blob-base64-exampleblob={ -SGVsbG8gV29ybGQhCg== -} - - -# Wildcard match for SSID (plaintext APs only). This example select any -# open AP regardless of its SSID. -network={ - key_mgmt=NONE -} diff --git a/meta/recipes-connectivity/wpa-supplicant/wpa-supplicant_2.12.bb b/meta/recipes-connectivity/wpa-supplicant/wpa-supplicant_2.12.bb index 9c2d716c03..df0585f1f3 100644 --- a/meta/recipes-connectivity/wpa-supplicant/wpa-supplicant_2.12.bb +++ b/meta/recipes-connectivity/wpa-supplicant/wpa-supplicant_2.12.bb @@ -12,7 +12,6 @@ DEPENDS = "dbus libnl" SRC_URI = "http://w1.fi/releases/wpa_supplicant-${PV}.tar.gz \ file://wpa-supplicant.sh \ - file://wpa_supplicant.conf \ file://wpa_supplicant.conf-sane \ file://99_wpa_supplicant \ " @@ -77,7 +76,7 @@ do_install () { oe_runmake -C wpa_supplicant DESTDIR="${D}" install install -d ${D}${docdir}/wpa_supplicant - install -m 644 wpa_supplicant/README ${UNPACKDIR}/wpa_supplicant.conf ${D}${docdir}/wpa_supplicant + install -m 644 wpa_supplicant/README ${S}/wpa_supplicant/wpa_supplicant.conf ${D}${docdir}/wpa_supplicant install -d ${D}${sysconfdir} install -m 600 ${UNPACKDIR}/wpa_supplicant.conf-sane ${D}${sysconfdir}/wpa_supplicant.conf From patchwork Tue Aug 18 06:30:41 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95585 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8FE81C5DF8B for ; Tue, 18 Aug 2026 06:31:44 +0000 (UTC) Received: from mta-65-226.siemens.flowmailer.net (mta-65-226.siemens.flowmailer.net [185.136.65.226]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3326.1787034698953510541 for ; Mon, 17 Aug 2026 23:31:40 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=ae58XCrI; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.226, mailfrom: fm-1329275-202608180631360b7346dd7700020755-oufiyo@rts-flowmailer.siemens.com) Received: by mta-65-226.siemens.flowmailer.net with ESMTPSA id 202608180631360b7346dd7700020755 for ; Tue, 18 Aug 2026 08:31:36 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=5VQ2D8P3tTzFlOGYHLE+KG4twPBVxqhSmR48g+AKgM4=; b=ae58XCrIaW4R9h7gevvprImsTw6cErRoCrZkTd9Wct2WuhqQ+x9P0klY9OSketVJAuf6ty qN871z3N60APD9xskWnZlR6n6eo6vAy+E/gDFjpXi1ELWb/qK0XkDJ6A0iSTiM1w8fx172a0 OTU/puuYMKp0D0OKiDaFByGPPInCxqT7BOuGZvC1YN1AGD63rsu8b4hH4eUFFGHnc5AUxM3I 6JAMoXNJZi2fFyqnSr3NsmtqgagK8CnO6L9UHZYotR/TpYU+b3a6J797kj3olblT96mmirqM vd9cBjKZMYLZoGidAZokHD7pOlykDg/gfFmjTFPWN/bFaPyDf3bKheIQ==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 02/14] devtool: deploy-target: pass tar -m to avoid future timestamp warnings Date: Tue, 18 Aug 2026 08:30:41 +0200 Message-ID: <20260818063120.73042-3-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:44 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243654 From: Adrian Freihofer devtool deploy-target tars up the build output and pipes it through ssh to the target, where it's extracted with "tar xv". If the target's clock is behind the host, tar prints a "time stamp ... is ... in the future" warning per file, since the archived mtimes come from the host. Add -m (--touch) so tar sets mtimes to the current time on extraction instead of restoring them from the archive, avoiding the warning. Supported identically by GNU tar and BusyBox tar. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/deploy.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/lib/devtool/deploy.py b/scripts/lib/devtool/deploy.py index 8228d9914f..35ec0b1b4d 100644 --- a/scripts/lib/devtool/deploy.py +++ b/scripts/lib/devtool/deploy.py @@ -108,9 +108,10 @@ def _prepare_remote_script(deploy, destdir='/', verbose=False, dryrun=False, und lines.append('mkdir -p `dirname $manifest`') lines.append('mkdir -p $2') if verbose: - lines.append(' tar xv -C $2 -f - | tee $manifest') + # -m avoids "time stamp is in the future" warnings if the target's clock is behind + lines.append(' tar xvm -C $2 -f - | tee $manifest') else: - lines.append(' tar xv -C $2 -f - > $manifest') + lines.append(' tar xvm -C $2 -f - > $manifest') lines.append('sed -i "s!^./!$2!" $manifest') elif not dryrun: # Put any preserved files back From patchwork Tue Aug 18 06:30:42 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95583 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 140C0C5DF84 for ; Tue, 18 Aug 2026 06:31:44 +0000 (UTC) Received: from mta-65-225.siemens.flowmailer.net (mta-65-225.siemens.flowmailer.net [185.136.65.225]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3327.1787034699017815985 for ; Mon, 17 Aug 2026 23:31:40 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=bdTtOup/; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.225, mailfrom: fm-1329275-2026081806313727c745743a000207a6-5rprft@rts-flowmailer.siemens.com) Received: by mta-65-225.siemens.flowmailer.net with ESMTPSA id 2026081806313727c745743a000207a6 for ; Tue, 18 Aug 2026 08:31:37 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=0LME96prFOIM/uXU+8jGo4MT5gpK2xxLXqqh7BMRT2Y=; b=bdTtOup/qSKWX9IQqtDRFX6Q+pJy0fynubibS3cUcph8msLW61WuGkULzsxmC21dV13h9k 9ENQLu+U5jAJWV/A1cTp/3P2e4MPsNNpE0lgIsRYm8qQ4LM/cqZ6JQJ6m42frcYdDJZ7xSlp csCYcZuGcrp9GvjHsJJTLFxl9Rl1+9zbet9QEJokYOgnqJAuSLYRIdX+OvEaM6/vfk8SIX0t vuYDV8l6qmVDcn5X8Bufgt2Gp4cKldN2BahZPtc8qaIuXihc7eM8IZfstz3Ch6pfGZrQTyFD yPly6JzjVEcUSz/TK5L9mQ6HXZFtm7BJ/M8KAhtTPwiLTNIqsRZlsGkg==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 03/14] oe-selftest: devtool deploy: use QEMU target commands Date: Tue, 18 Aug 2026 08:30:42 +0200 Message-ID: <20260818063120.73042-4-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:44 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243655 From: Adrian Freihofer Use the QEMU target API for target-side verification in devtool deploy selftests instead of invoking SSH directly. Copy the generated file list through the target API before comparing deployed file metadata. There are more such instances in the devtool ide-sdk selftests that could be converted to use the QEMU target API, but this will be done by follow-up patches with more changes than just this refactoring. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index df0910aaa6..b0c7835134 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -2070,8 +2070,8 @@ class DevtoolDeployTargetTests(DevtoolBase): self.logger.debug(deploy_cmd) result = runCmd(deploy_cmd) # Run a test command to see if it was installed properly - sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' - result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand)) + status, _ = qemu.run(testcommand) + self.assertEqual(status, 0) # Check if it deployed all of the files with the right ownership/perms # First look on the host - need to do this under pseudo to get the correct ownership/perms bb_vars = get_bb_vars(['D', 'FAKEROOTENV', 'FAKEROOTCMD', 'PATH'], testrecipe) @@ -2090,15 +2090,21 @@ class DevtoolDeployTargetTests(DevtoolBase): for line in filelist1: splitline = line.split() f.write(splitline[-1] + '\n') - result = runCmd('cat %s | ssh -q %s root@%s \'xargs ls -l\'' % (tmpfilelist, sshargs, qemu.ip)) - filelist2 = self._process_ls_output(result.output) + remotefilelist = '/tmp/%s' % os.path.basename(tmpfilelist) + status, _ = qemu.copy_to(tmpfilelist, remotefilelist) + self.assertEqual(status, 0) + status, output = qemu.run( + 'xargs ls -l < %s; status=$?; rm -f %s; exit $status' % ( + remotefilelist, remotefilelist)) + self.assertEqual(status, 0) + filelist2 = self._process_ls_output(output) filelist1.sort(key=lambda item: item.split()[-1]) filelist2.sort(key=lambda item: item.split()[-1]) self.assertEqual(filelist1, filelist2) # Test undeploy-target result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, qemu.ip)) - result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True) - self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have') + status, _ = qemu.run(testcommand) + self.assertNotEqual(status, 0, 'undeploy-target did not remove command as it should have') class DevtoolBuildImageTests(DevtoolBase): From patchwork Tue Aug 18 06:30:43 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95587 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7AC4EC5DF89 for ; Tue, 18 Aug 2026 06:31:44 +0000 (UTC) Received: from mta-65-227.siemens.flowmailer.net (mta-65-227.siemens.flowmailer.net [185.136.65.227]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3328.1787034699921256297 for ; Mon, 17 Aug 2026 23:31:40 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=gbxnCrhH; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.227, mailfrom: fm-1329275-202608180631374c84bb59580002072f-ehjrs8@rts-flowmailer.siemens.com) Received: by mta-65-227.siemens.flowmailer.net with ESMTPSA id 202608180631374c84bb59580002072f for ; Tue, 18 Aug 2026 08:31:37 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=UNgIJ1oVQJ0La/+N24u6MB56rijVPnMWsiNDrcz02BY=; b=gbxnCrhH4ySn3p4+5kJjVZov9QjYoW4srAuBbs15Xg+y2ww79SvG0gALcNbPI5SrkuBg25 K+8y7AHSFwMBnJlrKaY5jpm0MhtHkpECEZmjmwAeYreWqLUDlYPoDWqV35ekcZD9MUh0sffV OsQ3Z1Bche0fp3ha4TnPaDiecnqhzpVAK4Lm7KAg6sAy8V89yxK7Txwe0ta5uFTT2H6+qnrU 7FtKuhDuZYlozAuNv18S7z2rHySLcE+jYQ3Lq3njnmmDTM4ucJSKKogiHgCBdAxu3ZDdrBFw TdGDAyhlzZPnpnocdBuA/fvPCGEWLDnYmLalFjclW/7vfvkU6f/uHEYg==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 04/14] oe-selftest: devtool ide-sdk: Add new test classes for ide-sdk tests Date: Tue, 18 Aug 2026 08:30:43 +0200 Message-ID: <20260818063120.73042-5-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:44 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243653 From: Adrian Freihofer This allows to run the ide-sdk tests with different toolchains (gcc, clang) and build systems (cmake, meson) in parallel. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 211 +++++++++++++----------- 1 file changed, 111 insertions(+), 100 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index b0c7835134..051be713b0 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3063,6 +3063,105 @@ class DevtoolIdeSdkTests(DevtoolBase): exe_break_line=63 + LINE_SHIFT, exe_list_line=55 + LINE_SHIFT, hpp_break_line=21 + LINE_SHIFT, lib_break_line=31 + LINE_SHIFT) + def _verify_cmake_preset(self, tempdir): + """Verify the generated cmake preset works as expected + + Check if compiling works + Check if unit tests can be executed in qemu (not qemu-system) + """ + with open(os.path.join(tempdir, 'CMakeUserPresets.json')) as cmake_preset_j: + cmake_preset_d = json.load(cmake_preset_j) + config_presets = cmake_preset_d["configurePresets"] + self.assertEqual(len(config_presets), 1) + cmake_exe = config_presets[0]["cmakeExecutable"] + preset_name = config_presets[0]["name"] + compile_cmd = '%s --build --preset %s' % (cmake_exe, preset_name) + + # Verify the wrapper for cmake native is available + self.assertExists(cmake_exe) + + # Verify the cmake preset generated by devtool ide-sdk is available + result = runCmd('%s --list-presets' % cmake_exe, cwd=tempdir, output_log=self._cmd_logger) + self.assertIn(preset_name, result.output) + + # Verify cmake re-uses the o files compiled by bitbake + result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) + self.assertIn("ninja: no work to do.", result.output) + + # Verify the unit tests work (in Qemu user mode) + result = runCmd('%s --target test' % compile_cmd, cwd=tempdir, output_log=self._cmd_logger) + self.assertIn("100% tests passed", result.output) + + # Verify re-building and testing works again + result = runCmd('%s --target clean' % compile_cmd, cwd=tempdir, output_log=self._cmd_logger) + self.assertIn("Cleaning", result.output) + result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) + self.assertIn("Building", result.output) + self.assertIn("Linking", result.output) + result = runCmd('%s --target test' % compile_cmd, cwd=tempdir, output_log=self._cmd_logger) + self.assertIn("Running tests...", result.output) + self.assertIn("100% tests passed", result.output) + + return compile_cmd + + def _verify_meson_build(self, tempdir, recipe_name): + """Verify meson works as expected + + Check if compiling works + Check if unit tests can be executed in qemu (not qemu-system) + """ + meson_exe = os.path.join(self._workspace_scripts_dir(recipe_name), "meson") + self.assertExists(meson_exe) + build_dir = os.path.join(self._sources_workdir_dir(tempdir), recipe_name + "-1.0") + compile_cmd = '%s compile -C %s' % (meson_exe, build_dir) + + # Verify meson re-uses the o files compiled by bitbake + result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) + self.assertIn("ninja: no work to do.", result.output) + + # Verify the unit tests work (in Qemu user mode) + result = runCmd('%s test -C %s' % (meson_exe, build_dir), + cwd=tempdir, output_log=self._cmd_logger) + self.assertEqual(result.status, 0) + self.assertRegex(result.output, r"Fail:\s+0") + + # Verify re-building and testing works again + result = runCmd('%s compile -C %s --clean' % (meson_exe, build_dir), + cwd=tempdir, output_log=self._cmd_logger) + self.assertIn("Cleaning...", result.output) + result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) + self.assertIn("Linking target", result.output) + result = runCmd('%s test -C %s' % (meson_exe, build_dir), + cwd=tempdir, output_log=self._cmd_logger) + self.assertEqual(result.status, 0) + self.assertRegex(result.output, r"Fail:\s+0") + + return compile_cmd + + def _verify_service_running(self, qemu, service_name): + """Helper to verify a service is running in Qemu""" + # Use anchored regex (^name$) instead of pgrep -x because the target + # may have busybox pgrep which does not support the -x flag. + status, output = qemu.run("pgrep '^%s$'" % service_name) + self.assertEqual(status, 0, msg="%s service not running: %s" % + (service_name, output)) + self.assertTrue(output.strip().isdigit(), + f"pgrep output should be a PID integer, got: {output.strip()}") + + def _verify_conf_file(self, qemu, conf_file, owner, group): + """Helper to verify a configuration file is owned by the proper user and group""" + stat_cmd = "stat -c '%%U %%G' %s" % conf_file + status, output = qemu.run(stat_cmd) + self.assertEqual(status, 0, msg="Failed to stat %s: %s" % (conf_file, output)) + actual_owner, actual_group = output.strip().split() + self.assertEqual(actual_owner, owner, + msg="%s not owned by user %s: got %s" % (conf_file, owner, actual_owner)) + self.assertEqual(actual_group, group, + msg="%s not owned by group %s: got %s" % (conf_file, group, actual_group)) + + +class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): + def _gdb_cross(self): """Verify gdb-cross is provided by devtool ide-sdk""" target_arch = self.td["TARGET_ARCH"] @@ -3207,102 +3306,6 @@ class DevtoolIdeSdkTests(DevtoolBase): self.assertEqual(r.status, 0) self.assertNotIn("gdbserver ", r.output) - def _verify_cmake_preset(self, tempdir): - """Verify the generated cmake preset works as expected - - Check if compiling works - Check if unit tests can be executed in qemu (not qemu-system) - """ - with open(os.path.join(tempdir, 'CMakeUserPresets.json')) as cmake_preset_j: - cmake_preset_d = json.load(cmake_preset_j) - config_presets = cmake_preset_d["configurePresets"] - self.assertEqual(len(config_presets), 1) - cmake_exe = config_presets[0]["cmakeExecutable"] - preset_name = config_presets[0]["name"] - compile_cmd = '%s --build --preset %s' % (cmake_exe, preset_name) - - # Verify the wrapper for cmake native is available - self.assertExists(cmake_exe) - - # Verify the cmake preset generated by devtool ide-sdk is available - result = runCmd('%s --list-presets' % cmake_exe, cwd=tempdir, output_log=self._cmd_logger) - self.assertIn(preset_name, result.output) - - # Verify cmake re-uses the o files compiled by bitbake - result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) - self.assertIn("ninja: no work to do.", result.output) - - # Verify the unit tests work (in Qemu user mode) - result = runCmd('%s --target test' % compile_cmd, cwd=tempdir, output_log=self._cmd_logger) - self.assertIn("100% tests passed", result.output) - - # Verify re-building and testing works again - result = runCmd('%s --target clean' % compile_cmd, cwd=tempdir, output_log=self._cmd_logger) - self.assertIn("Cleaning", result.output) - result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) - self.assertIn("Building", result.output) - self.assertIn("Linking", result.output) - result = runCmd('%s --target test' % compile_cmd, cwd=tempdir, output_log=self._cmd_logger) - self.assertIn("Running tests...", result.output) - self.assertIn("100% tests passed", result.output) - - return compile_cmd - - def _verify_meson_build(self, tempdir, recipe_name): - """Verify meson works as expected - - Check if compiling works - Check if unit tests can be executed in qemu (not qemu-system) - """ - meson_exe = os.path.join(self._workspace_scripts_dir(recipe_name), "meson") - self.assertExists(meson_exe) - build_dir = os.path.join(self._sources_workdir_dir(tempdir), recipe_name + "-1.0") - compile_cmd = '%s compile -C %s' % (meson_exe, build_dir) - - # Verify meson re-uses the o files compiled by bitbake - result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) - self.assertIn("ninja: no work to do.", result.output) - - # Verify the unit tests work (in Qemu user mode) - result = runCmd('%s test -C %s' % (meson_exe, build_dir), - cwd=tempdir, output_log=self._cmd_logger) - self.assertEqual(result.status, 0) - self.assertRegex(result.output, r"Fail:\s+0") - - # Verify re-building and testing works again - result = runCmd('%s compile -C %s --clean' % (meson_exe, build_dir), - cwd=tempdir, output_log=self._cmd_logger) - self.assertIn("Cleaning...", result.output) - result = runCmd(compile_cmd, cwd=tempdir, output_log=self._cmd_logger) - self.assertIn("Linking target", result.output) - result = runCmd('%s test -C %s' % (meson_exe, build_dir), - cwd=tempdir, output_log=self._cmd_logger) - self.assertEqual(result.status, 0) - self.assertRegex(result.output, r"Fail:\s+0") - - return compile_cmd - - def _verify_service_running(self, qemu, service_name): - """Helper to verify a service is running in Qemu""" - # Use anchored regex (^name$) instead of pgrep -x because the target - # may have busybox pgrep which does not support the -x flag. - status, output = qemu.run("pgrep '^%s$'" % service_name) - self.assertEqual(status, 0, msg="%s service not running: %s" % - (service_name, output)) - self.assertTrue(output.strip().isdigit(), - f"pgrep output should be a PID integer, got: {output.strip()}") - - def _verify_conf_file(self, qemu, conf_file, owner, group): - """Helper to verify a configuration file is owned by the proper user and group""" - stat_cmd = "stat -c '%%U %%G' %s" % conf_file - status, output = qemu.run(stat_cmd) - self.assertEqual(status, 0, msg="Failed to stat %s: %s" % (conf_file, output)) - actual_owner, actual_group = output.strip().split() - self.assertEqual(actual_owner, owner, - msg="%s not owned by user %s: got %s" % (conf_file, owner, actual_owner)) - self.assertEqual(actual_group, group, - msg="%s not owned by group %s: got %s" % (conf_file, group, actual_group)) - @OETestTag("runqemu") def test_devtool_ide_sdk_none_qemu(self): """Start qemu-system and run tests for multiple recipes. ide=none is used.""" @@ -3701,6 +3704,8 @@ class DevtoolIdeSdkTests(DevtoolBase): # Verify deployment and remote debugging works self._verify_launch_json_debugging(tempdir, qemu, example_exe) +class DevtoolIdeSdkKernelTests(DevtoolIdeSdkTests): + @OETestTag("runqemu") def test_devtool_ide_sdk_code_kernel_module(self): """Verify a kernel module recipe works with ide=code mode @@ -3916,6 +3921,8 @@ class DevtoolIdeSdkTests(DevtoolBase): self.assertIn(MAGIC_STRING_NEW, output, 'New magic string not found in sysfs after rebuild: %s' % output) +class DevtoolIdeSdkSharedTests(DevtoolIdeSdkTests): + def test_devtool_ide_sdk_shared_sysroots(self): """Verify the shared sysroot SDK""" @@ -3977,6 +3984,8 @@ class DevtoolIdeSdkTests(DevtoolBase): runCmdEnv('meson setup %s' % tempdir_meson, cwd=cpp_example_src, output_log=self._cmd_logger) runCmdEnv('meson compile', cwd=tempdir_meson, output_log=self._cmd_logger) +class DevtoolIdeSdkClangTests(DevtoolIdeSdkTests): + def _verify_launch_json_lldb(self, tempdir): """Verify the launch.json file contains valid CodeLLDB (type: lldb) configurations.""" launch_json_path = os.path.join(tempdir, '.vscode', 'launch.json') @@ -4320,7 +4329,7 @@ class DevtoolIdeSdkTests(DevtoolBase): self._lldb_debug_cpp_example_check(r.output, magic_string) @OETestTag("runqemu") - def test_devtool_ide_sdk_code_cmake_clang(self): + def test_devtool_ide_sdk_code_cmake(self): """Verify a cmake recipe built with clang works with ide=code (CodeLLDB debugging). This test uses the cmake-example-clang recipe which is a cmake-example variant @@ -4375,7 +4384,7 @@ class DevtoolIdeSdkTests(DevtoolBase): tempdir, qemu, magic_string)) @OETestTag("runqemu") - def test_devtool_ide_sdk_code_meson_clang(self): + def test_devtool_ide_sdk_code_meson(self): """Verify a meson recipe built with clang works with ide=code (CodeLLDB debugging). This is the meson/ninja counterpart of test_devtool_ide_sdk_code_cmake_clang. @@ -4498,7 +4507,7 @@ class DevtoolIdeSdkTests(DevtoolBase): self.assertNotIn('lldb-server', r.output) @OETestTag("runqemu") - def test_devtool_ide_sdk_none_cmake_clang(self): + def test_devtool_ide_sdk_none_cmake(self): """Verify ide=none generates correct LLDB scripts for a clang cmake recipe. Uses cmake-example-clang (TOOLCHAIN = "clang") which is built with the @@ -4561,7 +4570,7 @@ class DevtoolIdeSdkTests(DevtoolBase): tempdir, qemu, recipe_name, example_exe, magic_string)) @OETestTag("runqemu") - def test_devtool_ide_sdk_none_meson_clang(self): + def test_devtool_ide_sdk_none_meson(self): """Verify ide=none generates correct LLDB scripts for a clang meson recipe. This is the meson/ninja counterpart of test_devtool_ide_sdk_none_cmake_clang. @@ -4629,6 +4638,8 @@ class DevtoolIdeSdkTests(DevtoolBase): lambda magic_string: self._lldb_none_debugging_multi( tempdir, qemu, recipe_name, example_exe, magic_string)) +class DevtoolIdeSdkMiscTests(DevtoolIdeSdkTests): + def test_devtool_ide_sdk_plugins(self): """Test that devtool ide-sdk can use plugins from other layers.""" From patchwork Tue Aug 18 06:30:44 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95579 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 845F4C5DF7E for ; Tue, 18 Aug 2026 06:31:42 +0000 (UTC) Received: from mta-65-225.siemens.flowmailer.net (mta-65-225.siemens.flowmailer.net [185.136.65.225]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3329.1787034699924261415 for ; Mon, 17 Aug 2026 23:31:40 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=g1vFPgkr; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.225, mailfrom: fm-1329275-202608180631383cf0bccf8c000207be-onk5di@rts-flowmailer.siemens.com) Received: by mta-65-225.siemens.flowmailer.net with ESMTPSA id 202608180631383cf0bccf8c000207be for ; Tue, 18 Aug 2026 08:31:38 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=G7LVVwYWMZiWTFPbGoHBnWd++oOlopoo8Yx8tzuzJps=; b=g1vFPgkrhBHB7sv7Afj3qkYBJAIZbzeFEtaWIviTt0XbQoBZHIYk3F+dk84xVmHOaPfHd5 9rLvjBSh18JP2UWFCzveAkvaiGEwHGx6RTUoXwoNaZkDbnLVh7kN6iCquO9p4aFKErXqeV4I e6ATmoZAkyqWNzxyECGKvvFlJWgctzePlEfZwEdwi9Ft3yw6SgnxcvQ7aqIt1iKKDbg/oK7t fdKU5PyHt3EWOrpdwYzxk/kPZqSr35RQgplho4OztzaHem2QGdME0H08NL0uE8EFcTD34qsg ayH3Jprxgq7u8ROo9s8ge+kvA8XWGAmwr2+VQNiFgmOwViMfvCYqCxpw==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 05/14] devtool: ide-sdk: fix meson IntelliSense for gcc toolchain Date: Tue, 18 Aug 2026 08:30:44 +0200 Message-ID: <20260818063120.73042-6-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:42 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243652 From: Adrian Freihofer The configurationProvider = "mesonbuild.mesonbuild" approach in c_cpp_properties.json worked for meson recipes built with clang (the __gen_meson_absolute_cross_file workaround absolutized the clang binary path in an extra cross-file so the mesonbuild extension could find it), but failed for the gcc toolchain: the extension could not resolve the bare compiler name in the meson.cross file and silently fell back to the host compiler for IntelliSense. The sysroot was also absent, so built-in headers were resolved against the host system. Replace the extension-based approach with explicit settings that work for both gcc and clang toolchains: - compilerPath: absolute path to the cross CXX binary in staging_bindir_toolchain, so cpptools finds the right compiler - compilerArgs: ["--sysroot="], so built-in includes are resolved against the target sysroot headers - compileCommands: path to compile_commands.json in the meson build directory B, which meson/ninja generates automatically with the exact per-file flags (include paths, defines, -march= etc.) - intelliSenseMode: derived from TARGET_ARCH and TOOLCHAIN (linux-gcc-arm64 / linux-clang-x64, etc.) With compileCommands pointing at the real compilation database, the mesonbuild extension is no longer needed for IntelliSense, so the __gen_meson_absolute_cross_file workaround is also removed. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/ide_plugins/ide_code.py | 66 +++++++-------------- 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py index 9faba3f2d1..d190858d44 100644 --- a/scripts/lib/devtool/ide_plugins/ide_code.py +++ b/scripts/lib/devtool/ide_plugins/ide_code.py @@ -147,45 +147,6 @@ class IdeVSCode(IdeBase): def dot_code_dir(self, modified_recipe): return os.path.join(modified_recipe.srctree, '.vscode') - def __gen_meson_absolute_cross_file(self, modified_recipe): - """Generate an extra cross file overriding c/cpp binaries with absolute paths. - - The recipe's generated meson.cross references the toolchain binaries - (e.g. "aarch64-poky-linux-clang++") by bare name, relying on PATH - being set up by the meson wrapper script at build time. Meson stores - that command exactly as configured (it never resolves it to an - absolute path itself), so it ends up unresolved in - compile_commands.json and meson-info/intro-compilers.json. cpptools - (via the mesonbuild extension) resolves compilerPath using its own - process PATH, which does not include the toolchain directory, and - silently falls back to a host compiler for IntelliSense. - - Real builds are unaffected since the wrapper script sets up PATH, so - the recipe's meson.cross is left untouched. Instead, an extra - --cross-file is layered on top with only the c/cpp [binaries] - entries absolutized, reusing the exact same flags as CC/CXX to avoid - any behavior drift. Meson merges multiple machine files, with later - files overriding matching keys from earlier ones. - """ - def absolutize(cmd): - args = cmd.split() - args[0] = os.path.join( - modified_recipe.staging_bindir_toolchain, args[0]) - return repr(args) - - lines = ["[binaries]"] - if modified_recipe.cc: - lines.append("c = %s" % absolutize(modified_recipe.cc)) - if modified_recipe.cxx: - lines.append("cpp = %s" % absolutize(modified_recipe.cxx)) - - os.makedirs(modified_recipe.ide_sdk_dir, exist_ok=True) - cross_file = os.path.join( - modified_recipe.ide_sdk_dir, 'meson-absolute-toolchain.cross') - with open(cross_file, 'w') as f: - f.write(os.linesep.join(lines) + os.linesep) - return ['--cross-file', cross_file] - def __vscode_settings_meson(self, settings_dict, modified_recipe): if modified_recipe.build_tool is not BuildTool.MESON: return @@ -193,11 +154,16 @@ class IdeVSCode(IdeBase): confopts = modified_recipe.mesonopts.split() confopts += modified_recipe.meson_cross_file.split() - if modified_recipe.meson_cross_file: - confopts += self.__gen_meson_absolute_cross_file(modified_recipe) confopts += modified_recipe.extra_oemeson.split() settings_dict["mesonbuild.configureOptions"] = confopts settings_dict["mesonbuild.buildFolder"] = modified_recipe.b + # Prevent the extension from writing C_Cpp.default.configurationProvider + # for itself once meson-info.json appears, which would take precedence + # over the compileCommands set in c_cpp_properties.json. + settings_dict["mesonbuild.modifySettings"] = False + # Clear any such value a previous run (or extension version without + # modifySettings support) already wrote to this workspace's settings. + settings_dict["C_Cpp.default.configurationProvider"] = "" def __vscode_settings_cmake(self, settings_dict, modified_recipe): """Add cmake specific settings to settings.json. @@ -328,8 +294,22 @@ class IdeVSCode(IdeBase): if modified_recipe.build_tool is BuildTool.CMAKE: properties_dict["configurationProvider"] = "ms-vscode.cmake-tools" elif modified_recipe.build_tool is BuildTool.MESON: - properties_dict["configurationProvider"] = "mesonbuild.mesonbuild" - properties_dict["compilerPath"] = os.path.join(modified_recipe.staging_bindir_toolchain, modified_recipe.cxx.split()[0]) + # configurationProvider = "mesonbuild.mesonbuild" does not work because + # the cross-compiler is not in PATH and omits the sysroot by default. + compiler_type = 'clang' if modified_recipe.toolchain == 'clang' else 'gcc' + arch_map = { + 'aarch64': 'arm64', 'x86_64': 'x64', 'arm': 'arm', + 'i686': 'x86', 'i586': 'x86', 'riscv64': 'x64', 'riscv32': 'x86', + } + intelli_arch = arch_map.get(modified_recipe.target_arch, 'x64') + properties_dict["compilerPath"] = os.path.join( + modified_recipe.staging_bindir_toolchain, + modified_recipe.cxx.split()[0]) + properties_dict["compilerArgs"] = [ + "--sysroot=" + modified_recipe.recipe_sysroot] + properties_dict["compileCommands"] = os.path.join( + modified_recipe.b, 'compile_commands.json') + properties_dict["intelliSenseMode"] = "linux-%s-%s" % (compiler_type, intelli_arch) elif modified_recipe.build_tool is BuildTool.KERNEL_MODULE: # Using e.g. configurationProvider = "ms-vscode.makefile-tools" was not successful properties_dict["compilerPath"] = os.path.join(modified_recipe.staging_bindir_toolchain, modified_recipe.kernel_cc.split()[0]) From patchwork Tue Aug 18 06:30:45 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95584 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 618CBC5DF87 for ; Tue, 18 Aug 2026 06:31:44 +0000 (UTC) Received: from mta-64-226.siemens.flowmailer.net (mta-64-226.siemens.flowmailer.net [185.136.64.226]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.3143.1787034700534743416 for ; Mon, 17 Aug 2026 23:31:40 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=XS15n8fK; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.226, mailfrom: fm-1329275-202608180631386c6b7cbb610002077a-qnop2t@rts-flowmailer.siemens.com) Received: by mta-64-226.siemens.flowmailer.net with ESMTPSA id 202608180631386c6b7cbb610002077a for ; Tue, 18 Aug 2026 08:31:38 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=0d7wl/WvSmWGtaa/PKeuZVaOtcmBCwvhFH5fBtTFSbQ=; b=XS15n8fKF6NclFOtiUsyJBPzikTt3NpHyD6opWV1KNI3l8M/in5p/FKDCIf7fr6eSWx0/q 1AWC+Oy6Rk5p3pWQBlHKF5E6yryiwBgOPh1RgG5hb71vnzVmgiLfdkr/xRWI0kdCqNHsR6kO Y7P5i7Mnv+o5EA2dwsEMYGAhvFWCBTaNWz69cb2zB1Ed1U71KpXdCopKvWvW5ctapsgztrBA wThTlphFNJjWJhpGxwk6dQIQ9QuVZ5g4ZRsyGBNoC5fj9fdaOdNZ/DQOtaifka20yRf9pFQ/ EgDT3ug8+NVz+xRkbQLau8d5oW3O8Owzvh6MdSYYt3Niq5mIyVHuvD3Q==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 06/14] ccp-example: fix PID file handling Date: Tue, 18 Aug 2026 08:30:45 +0200 Message-ID: <20260818063120.73042-7-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:44 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243658 From: Adrian Freihofer Remove the PID file if the process is not running anymore. This can happen when a debugger kills the process it debugs. Signed-off-by: Adrian Freihofer --- meta-selftest/recipes-test/cpp/files/cpp-example.init | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/meta-selftest/recipes-test/cpp/files/cpp-example.init b/meta-selftest/recipes-test/cpp/files/cpp-example.init index c154fd1126..30b8486eeb 100644 --- a/meta-selftest/recipes-test/cpp/files/cpp-example.init +++ b/meta-selftest/recipes-test/cpp/files/cpp-example.init @@ -14,6 +14,11 @@ PIDFILE="/var/run/$DAEMON.pid" LOCK_FILE="/var/lock/subsys/$DAEMON" start() { + # A debugger which kills the process it debugs leaves the PID file behind + if [ -f $PIDFILE ] && ! kill -0 $(cat $PIDFILE) 2>/dev/null; then + rm -f $PIDFILE $LOCK_FILE + fi + if [ -f $PIDFILE ]; then echo "$DAEMON is already running." return 1 From patchwork Tue Aug 18 06:30:46 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95580 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id E4F12C5DF83 for ; Tue, 18 Aug 2026 06:31:43 +0000 (UTC) Received: from mta-65-225.siemens.flowmailer.net (mta-65-225.siemens.flowmailer.net [185.136.65.225]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3329.1787034699924261415 for ; Mon, 17 Aug 2026 23:31:41 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=Qqwog9Kb; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.225, mailfrom: fm-1329275-202608180631383064e21d2200020776-ai_agc@rts-flowmailer.siemens.com) Received: by mta-65-225.siemens.flowmailer.net with ESMTPSA id 202608180631383064e21d2200020776 for ; Tue, 18 Aug 2026 08:31:39 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=tF9kmAJxWcRoTEXgu2vR8LvSN5T/1sdatjBT61iZ390=; b=Qqwog9KbkhUlgmvgceW2nQDcvaQ4o0Urw00EpMsMsjibUTKzZyTpLspBGdJKBclbbPph6T kPThGSqbBQcILHjrnKuG40HxdxUIyhkCHF9fsoeNSPAkpntd8HTn3nxvWjpvTthVu3aPr997 u1zwkXC2LVsSf46P1ZyewmANbDxV5AiGbVe7ttBDLDkm5pr1e+d3TRzrRKjvCg8f2qR06BYi bRaTa2mE/Cef2Nm+eiLPU1xQlhxr/ijS9PmoFSpA/Co7w+gglIxhvhRNj0F6Kn30tnjH5PAK FTV/3UU5NP8MqfKPxyehFEVABWdjbg4nDR+9b1IXxngTpumSo5urAYog==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 07/14] devtool: ide-sdk: default to all modified recipes Date: Tue, 18 Aug 2026 08:30:46 +0200 Message-ID: <20260818063120.73042-8-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:43 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243659 From: Adrian Freihofer Simplify ide-sdk usage in modified mode: keep the image recipe explicit, but when no modified recipe is passed, take all modified recipes from /appends/*.bbappend. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/ide_sdk.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/scripts/lib/devtool/ide_sdk.py b/scripts/lib/devtool/ide_sdk.py index 42b6e381c5..ab2bba5d39 100755 --- a/scripts/lib/devtool/ide_sdk.py +++ b/scripts/lib/devtool/ide_sdk.py @@ -1258,6 +1258,15 @@ def ide_setup(args, config, basepath, workspace): recipes_modified_names)) invalid_params = True if args.mode == DevtoolIdeMode.modified: + if not recipes_modified_names: + appends_dir = os.path.join(config.workspace_path, 'appends') + recipes_modified_names = sorted( + os.path.splitext(os.path.basename(path))[0] + for path in glob.glob(os.path.join(appends_dir, '*.bbappend'))) + if recipes_modified_names: + logger.info( + "No modified recipes specified, using workspace bbappends from %s: %s", + appends_dir, ', '.join(recipes_modified_names)) if len(recipes_other_names): logger.error("Only in shared sysroots mode not modified recipes %s can be handled." % str( recipes_other_names)) @@ -1266,6 +1275,11 @@ def ide_setup(args, config, basepath, workspace): logger.error( "One image recipe is required as the rootfs for the remote development.") invalid_params = True + if not recipes_modified_names: + logger.error( + "At least one modified recipe is required or must be guessable from %s." % + os.path.join(config.workspace_path, 'appends')) + invalid_params = True for modified_recipe_name in recipes_modified_names: if modified_recipe_name.startswith('nativesdk-') or modified_recipe_name.endswith('-native'): logger.error( @@ -1422,7 +1436,9 @@ def register_commands(subparsers, context): help='Setup the SDK and configure the IDE') parser_ide_sdk.add_argument( 'recipenames', nargs='+', help='Generate an IDE configuration suitable to work on the given recipes.\n' - 'Depending on the --mode parameter different types of SDKs and IDE configurations are generated.') + 'Depending on the --mode parameter different types of SDKs and IDE configurations are generated.\n' + 'In modified mode at least the image recipe is required; if no modified recipe is passed, ' + 'all modified recipes are taken from /appends/*.bbappend.') parser_ide_sdk.add_argument( '-m', '--mode', type=DevtoolIdeMode, default=DevtoolIdeMode.modified, help='Different SDK types are supported:\n' From patchwork Tue Aug 18 06:30:47 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95578 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 93BCBC5DF80 for ; Tue, 18 Aug 2026 06:31:42 +0000 (UTC) Received: from mta-65-226.siemens.flowmailer.net (mta-65-226.siemens.flowmailer.net [185.136.65.226]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.3144.1787034701094931138 for ; Mon, 17 Aug 2026 23:31:41 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=Gl/9ws09; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.226, mailfrom: fm-1329275-20260818063139b9747118130002077f-ukvzi3@rts-flowmailer.siemens.com) Received: by mta-65-226.siemens.flowmailer.net with ESMTPSA id 20260818063139b9747118130002077f for ; Tue, 18 Aug 2026 08:31:39 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=UB7eVCqupIConBytoa/cucXR0nlNYoUrSRPxeIrccyk=; b=Gl/9ws09CENsh9apVNO/dtGrc9mvfj/551pM4Y9oPfqGBHF783lIYUhoo0IOAzczf8y9Zg qw7kl8VDPqgTf7FrlddI+eObqBVSxP3QYHq+H+KZx3D+lsKKoVHnWCdFOfU3eziqdtRW1kzo u68GPpUe7qKSe8UMh6ngVLzo2WVnyPpdmBvRsaPu4XbxexqnKuSx10uPFrMgi/CJ451kOgG5 JmP4NHdllNzZ2Euo1V7jrM1SSU2jn2q+U6owVNo5am4fsqQB5TsSk3PJyhaEXzMBy6mG6m37 FG1nZ1zHhxJJv4Kn0KJs5iFsHRg9jGc/y6E+/9skvGtVx49H8k+74KGw==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 08/14] devtool: ide-sdk: let gdbserver reuse the shared debug server wait helper Date: Tue, 18 Aug 2026 08:30:47 +0200 Message-ID: <20260818063120.73042-9-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:42 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243661 From: Adrian Freihofer GdbCrossConfig open coded its own copy of the loop which waits for the debug server to listen on its port, while LldbServerConfig already used the _target_wait_for_tcp_port_cmd() helper of the common base class. Use the helper for gdbserver too, which requires capturing the gdbserver pid in a shell variable so the helper can refer to it, and defining DEBUG_SERVER_NAME for GdbCrossConfig. Beside removing the duplication this also gives gdbserver the two things the helper already did for lldb-server: the server is killed instead of left behind when it never starts listening, and the reason is printed rather than exiting silently. Signed-off-by: Adrian Freihofer --- scripts/lib/devtool/ide_plugins/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py index 63e9682e73..5211df5806 100644 --- a/scripts/lib/devtool/ide_plugins/__init__.py +++ b/scripts/lib/devtool/ide_plugins/__init__.py @@ -139,6 +139,7 @@ class GdbCrossConfig(DebuggerCrossConfig): gdbinit / gdb wrapper scripts used by ide=none as well as the target-side tmp/pid/log paths consumed by the gdbserver start command. """ + DEBUG_SERVER_NAME = "gdbserver" def __init__(self, image_recipe, modified_recipe, binary, default_mode=DebuggerServerModes.MULTI): @@ -193,14 +194,13 @@ class GdbCrossConfig(DebuggerCrossConfig): else: raise DevtoolError("Cannot use gdbserver attach mode for binary %s. No PID found." % self.binary.binary_path) elif server_mode == DebuggerServerModes.MULTI: - hex_port = "%04X" % self.debug_server_port - gdbserver_cmd_start = "grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null && exit 0; " % hex_port + gdbserver_cmd_start = self._target_tcp_port_check_cmd() + " && exit 0; " gdbserver_cmd_start += "mkdir -p %s; " % self._gdbserver_tmp_dir(server_mode) - gdbserver_cmd_start += "%s --multi :%s > %s 2>&1 & " % ( - self.debugger_cross.debug_server_path, self.debug_server_port, self._gdbserver_log_file(server_mode)) - gdbserver_cmd_start += "echo \\$! > %s; " % self._gdbserver_pid_file(server_mode) - gdbserver_cmd_start += "_w=0; while ! grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null; " % hex_port - gdbserver_cmd_start += "do _w=\\$((_w+1)); [ \\$_w -lt 100 ] || exit 1; sleep 0.1; done;" + gdbserver_cmd_start += "%s --multi :%s > %s 2>&1 & _gdbserver_pid=\\$!; " % ( + self.debugger_cross.debug_server_path, self.debug_server_port, + self._gdbserver_log_file(server_mode)) + gdbserver_cmd_start += "echo \\$_gdbserver_pid > %s; " % self._gdbserver_pid_file(server_mode) + gdbserver_cmd_start += self._target_wait_for_tcp_port_cmd("gdbserver_pid") else: raise DevtoolError("Unsupported gdbserver mode: %s" % server_mode) return "\"/bin/sh -c '" + gdbserver_cmd_start + "'\"" From patchwork Tue Aug 18 06:30:48 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95589 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id B2DB4C5DF67 for ; Tue, 18 Aug 2026 06:31:44 +0000 (UTC) Received: from mta-64-227.siemens.flowmailer.net (mta-64-227.siemens.flowmailer.net [185.136.64.227]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.3145.1787034701837570118 for ; Mon, 17 Aug 2026 23:31:42 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=Bfry60wG; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.64.227, mailfrom: fm-1329275-202608180631398770fc4169000207df-tdiezw@rts-flowmailer.siemens.com) Received: by mta-64-227.siemens.flowmailer.net with ESMTPSA id 202608180631398770fc4169000207df for ; Tue, 18 Aug 2026 08:31:40 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=QW3KRaFj9rp9zsOiIYwZFBHMfJ+1Fz1xy7506IUrunI=; b=Bfry60wGxIAwb/i6P/5FlIUtITTVJ3kyW2+DwuCBaWeceHFfHEZQ5LXw8/t22TUI2tWNUP 3pSUqEXLL1JEvja9Xk9cA4M072vBqUVQ1j6Zz4MlFeouJoY3H/Hv13+vn3f/bE4o3Rr83YDX nrphgbLhSNqcNknawPZ4Kc/ixLoFmJGKcMzD/gd7qwhE8xpuOjxLLWMbY8sp2a1ZGTOhfO4/ f1I8HbEz3cib786xVihDrPaess1XqNSwHmJK0kaibRIyUt4m+2kgUcOuIIB/cA3Meiznyfpv 6Lmwma3tJ16WmefzFP4aJ7mjqBTrqNGsG/NumtMLV2UJPk6CMw+Zmaxw==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 09/14] oe-selftest: devtool ide-sdk: support $ in gdbserver task command Date: Tue, 18 Aug 2026 08:30:48 +0200 Message-ID: <20260818063120.73042-10-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:44 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243663 From: Adrian Freihofer This is a preparation commit for the next commit that will add a new a variable to the tasks.json file to support gdbserver commands. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 051be713b0..0791422461 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3576,10 +3576,11 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): # Start gdbserver on target using the task command (keep the ssh connection open while debugging) ssh_gdbserver_cmd = [task_command] + task_args - # Fix shell command escaping - remove extra quotes from the last argument - # The task_args likely contains a quoted shell command that needs to be unquoted + # The tasks.json argument is formatted for an intermediate shell. Strip + # its quotes and restore dollar expansions before passing it directly to + # SSH via subprocess. if len(ssh_gdbserver_cmd) > 0 and ssh_gdbserver_cmd[-1].startswith('"') and ssh_gdbserver_cmd[-1].endswith('"'): - ssh_gdbserver_cmd[-1] = ssh_gdbserver_cmd[-1][1:-1] # Remove surrounding quotes + ssh_gdbserver_cmd[-1] = ssh_gdbserver_cmd[-1][1:-1].replace('\\$', '$') # Remove surrounding quotes self.logger.debug(f"Starting gdbserver with command: {' '.join(ssh_gdbserver_cmd)}") with RunCmdBackground(ssh_gdbserver_cmd, output_log=self._cmd_logger): # Give gdbserver a moment to start From patchwork Tue Aug 18 06:30:49 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95582 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id E85E4C5DF85 for ; Tue, 18 Aug 2026 06:31:43 +0000 (UTC) Received: from mta-65-225.siemens.flowmailer.net (mta-65-225.siemens.flowmailer.net [185.136.65.225]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3327.1787034699017815985 for ; Mon, 17 Aug 2026 23:31:41 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=co2ooz7M; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.225, mailfrom: fm-1329275-20260818063140c17ea36fd900020727-broxzf@rts-flowmailer.siemens.com) Received: by mta-65-225.siemens.flowmailer.net with ESMTPSA id 20260818063140c17ea36fd900020727 for ; Tue, 18 Aug 2026 08:31:40 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=lM/lKO0pYKgS5Yio1xLUnwwwxSNS9Oyu/UwEIjSxetc=; b=co2ooz7MW20qvXiiVINGc4JlvBhQ2k2ht9zdONbGA42V1oQtymN3QFPeJ+SFCGEQTIEEur +22HtKDC6E4PZX2Oy8rDGQJx7pMv60MJs0zEt2ZMmL1Ou4rZMqHtov78J6v+OLNoBocoBY3R oa+RV2pxSyM5OWK8MZE0tpLzLDNbyggz1GvJ9q8Q/9lX31iRym9x0w/4R6unKXDnxopyYHpv EtFGIKTJPRRYevlw10DWTLgT+NVtlHsoYmlAk+rkhRRJQZSFJE8cy15hwdfE4XTc2tymRQ6g 8fB7nNoJnySXH9HdD/EP2FaNi8rzRRMlFJ24SmQZUzsBJJU6p1sNXgVQ==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 10/14] devtool: ide-sdk: attach via extended-remote Date: Tue, 18 Aug 2026 08:30:49 +0200 Message-ID: <20260818063120.73042-11-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:43 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243660 From: Adrian Freihofer The old ATTACH mode started gdbserver with "--attach : $(pidof )" and used a plain "request": "launch" config. This is the classic cpptools attach setup, which is fragile and has several long-standing, unfixed bugs, e.g. https://github.com/microsoft/vscode-cpptools/issues/4166. { "name": "Attach with GDB", "type": "cppdbg", "request": "launch", "program": "", "stopAtEntry": true, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "miDebuggerPath": "/usr/bin/gdb", "miDebuggerServerAddress": ":", "postDebugTask": "kill_gdbserver_..." } cppdbg also supports attaching over the extended-remote protocol: { "name": "Attach with GDB", "type": "cppdbg", "request": "attach", "program": "", "MIMode": "gdb", "miDebuggerPath": "/usr/bin/gdb", "miDebuggerServerAddress": ":", "useExtendedRemote": true } Switch to this instead: merge ATTACH into the same persistent "gdbserver --multi" server already used for MULTI mode, since both are now extended-remote sessions that only differ on the client side, and stop it by PID instead of pgrep/killall. ONCE mode gets the same readiness synchronization MULTI already had, since gdbserver now always writes a PID file that the stop commands wait on. Allocate a separate debug server port for each mode. This prevents the persistent attach endpoint from colliding with a once session for the same binary. Until https://github.com/microsoft/vscode-cpptools/pull/14684 is merged, this comes with one downside (which was the blocker for using extended-remote attach mode until now): the user is prompted to pick the process to attach to, instead of it being selected automatically. But this is a temporary limitation, and the new setup is more robust and fixes several long-standing issues with the old ATTACH mode. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 23 ++++- scripts/lib/devtool/ide_plugins/__init__.py | 103 +++++++++++++------- scripts/lib/devtool/ide_plugins/ide_code.py | 94 ++++++++++-------- scripts/lib/devtool/ide_plugins/ide_none.py | 44 +++++---- 4 files changed, 166 insertions(+), 98 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 0791422461..35abc6a48f 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3397,21 +3397,31 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): # Track configurations found once_configs = [] attach_configs = [] + server_addresses = [] for config in configurations: # Verify required fields exist - required_fields = ["name", "type", "request", "program", "cwd", "MIMode", + required_fields = ["name", "type", "request", "program", "MIMode", "miDebuggerPath", "miDebuggerServerAddress"] for field in required_fields: self.assertIn(field, config, f"Configuration '{config.get('name', 'Unknown')}' missing required field: {field}") # Verify common configuration values self.assertEqual(config["type"], "cppdbg", f"Configuration '{config['name']}' should use cppdbg type") - self.assertEqual(config["request"], "launch", f"Configuration '{config['name']}' should be launch type") - self.assertEqual(config["cwd"], "${workspaceFolder}", f"Configuration '{config['name']}' should use workspaceFolder as cwd") self.assertEqual(config["MIMode"], "gdb", f"Configuration '{config['name']}' should use gdb MIMode") - self.assertEqual(config.get("externalConsole", False), False, f"Configuration '{config['name']}' should not use external console") - self.assertEqual(config.get("stopAtEntry", True), True, f"Configuration '{config['name']}' should stop at entry") + + if config["request"] == "launch": + self.assertEqual(config["cwd"], "${workspaceFolder}", f"Configuration '{config['name']}' should use workspaceFolder as cwd") + self.assertEqual(config.get("externalConsole", False), False, f"Configuration '{config['name']}' should not use external console") + self.assertEqual(config.get("stopAtEntry", True), True, f"Configuration '{config['name']}' should stop at entry") + elif config["request"] == "attach": + # Attaching to a process running on the target requires the + # extended-remote protocol. Stopping the session then detaches + # from the process instead of killing it. + self.assertTrue(config.get("useExtendedRemote"), f"Configuration '{config['name']}' should use useExtendedRemote") + self.assertNotIn("cwd", config, f"Configuration '{config['name']}' should not set cwd in attach mode") + else: + self.fail(f"Configuration '{config['name']}' has unexpected request type: {config['request']}") # Verify program path is absolute and exists conceptually program = config["program"] @@ -3426,6 +3436,7 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): # Verify server address format server_addr = config["miDebuggerServerAddress"] self.assertRegex(server_addr, r"^\d+\.\d+\.\d+\.\d+:\d+$", f"Configuration '{config['name']}' server address should be IP:PORT format: {server_addr}") + server_addresses.append(server_addr) # Verify additional SO lib search path exists and contains debug paths so_paths = config.get("additionalSOLibSearchPath", []) @@ -3466,6 +3477,8 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): # Verify we have expected configuration types self.assertEqual(len(once_configs), 2, f"Should have two once configuration, found: {once_configs}") self.assertEqual(len(attach_configs), 1, f"Should have one attach configuration, found: {attach_configs}") + self.assertEqual(len(server_addresses), len(set(server_addresses)), + "Each debug configuration should use a distinct server address") def _verify_launch_json_debugging(self, tempdir, qemu, example_exe): """Verify remote debugging and deployment works using launch.json configurations diff --git a/scripts/lib/devtool/ide_plugins/__init__.py b/scripts/lib/devtool/ide_plugins/__init__.py index 5211df5806..0209155dce 100644 --- a/scripts/lib/devtool/ide_plugins/__init__.py +++ b/scripts/lib/devtool/ide_plugins/__init__.py @@ -59,8 +59,11 @@ class DebuggerCrossConfig: self.binary = binary self.default_mode = default_mode self.binary_pretty = self.binary.binary_path.replace(os.sep, '-').lstrip('-') - self.debug_server_port = DebuggerCrossConfig._port_next - DebuggerCrossConfig._port_next += 1 + self.debug_server_ports = {} + for mode in self.server_modes(): + self.debug_server_ports[mode] = DebuggerCrossConfig._port_next + DebuggerCrossConfig._port_next += 1 + self.debug_server_port = self.debug_server_ports[self.default_mode] self.id_pretty = "%d_%s" % (self.debug_server_port, self.binary_pretty) if self.id_pretty in DebuggerCrossConfig._configs: @@ -68,8 +71,14 @@ class DebuggerCrossConfig: "debugger config for binary %s is already generated" % binary) DebuggerCrossConfig._configs[self.id_pretty] = self + def port(self, mode=None): + """Return the debug server port allocated for a server mode.""" + if mode is None: + mode = self.default_mode + return self.debug_server_ports[mode] + def id_pretty_mode(self, mode): - return "%s_%s" % (self.id_pretty, mode.name.lower()) + return "%d_%s_%s" % (self.port(mode), self.binary_pretty, mode.name.lower()) # Host-side script paths @property @@ -100,15 +109,22 @@ class DebuggerCrossConfig: # Re-tries in 0.1s Example: 300 * 0.1s, i.e. ~30s. TARGET_START_RETRIES = 300 - def _target_tcp_port_check_cmd(self): - hex_port = "%04X" % self.debug_server_port + def _target_tcp_port_check_cmd(self, mode=None): + hex_port = "%04X" % self.port(mode) return "grep -q :%s /proc/net/tcp /proc/net/tcp6 2>/dev/null" % hex_port - def _target_wait_for_tcp_port_cmd(self, pid_var=None, log_file=None): + def get_debug_server_ready_marker(self, port): + return "%s ready on port %s" % (self.DEBUG_SERVER_NAME, port) + + def get_debug_server_ready_marker_pattern(self): + return "^%s$" % self.get_debug_server_ready_marker("[0-9]+") + + def _target_wait_for_tcp_port_cmd(self, pid_var=None, log_file=None, mode=None): """Shell fragment waiting until the debug server listens on its port. The server log is dumped to stderr when giving up. """ + port = self.port(mode) dump_log = "cat %s >&2; " % log_file if log_file else "" cleanup = "" if pid_var: @@ -117,8 +133,15 @@ class DebuggerCrossConfig: "_w=0; while ! %s; do _w=\\$((_w+1)); [ \\$_w -lt %d ] || { " "%secho %s did not start on port %s after \\$_w retries >&2; %sexit 1; }; " "sleep 0.1; done;" - % (self._target_tcp_port_check_cmd(), self.TARGET_START_RETRIES, - cleanup, self.DEBUG_SERVER_NAME, self.debug_server_port, dump_log)) + % (self._target_tcp_port_check_cmd(mode), self.TARGET_START_RETRIES, + cleanup, self.DEBUG_SERVER_NAME, port, dump_log)) + + def _target_wait_for_process_exit_cmd(self, pid_var): + return ( + "_w=0; while kill -0 \\$_%s 2>/dev/null; do _w=\\$((_w+1)); " + "[ \\$_w -lt 100 ] || { echo %s did not stop >&2; exit 1; }; " + "sleep 0.1; done;" + % (pid_var, self.DEBUG_SERVER_NAME)) def initialize(self): """Called after construction to generate any required config files.""" @@ -128,7 +151,7 @@ class DebuggerCrossConfig: def _target_start_cmd(self, mode): raise NotImplementedError - def _target_kill_cmd(self): + def _target_stop_cmd(self, mode): raise NotImplementedError @@ -181,33 +204,48 @@ class GdbCrossConfig(DebuggerCrossConfig): Returns something like: "\"/bin/sh -c '/usr/bin/gdbserver --once :1234 /usr/bin/cmake-example'\"" """ + port = self.port(server_mode) if server_mode == DebuggerServerModes.ONCE: - gdbserver_cmd_start = "%s --once :%s %s" % ( - self.debugger_cross.debug_server_path, self.debug_server_port, self.binary.binary_path) - elif server_mode == DebuggerServerModes.ATTACH: - pid_command = self.binary.pid_command - if pid_command: - gdbserver_cmd_start = "%s --attach :%s \\$(%s)" % ( - self.debugger_cross.debug_server_path, - self.debug_server_port, - pid_command) - else: - raise DevtoolError("Cannot use gdbserver attach mode for binary %s. No PID found." % self.binary.binary_path) - elif server_mode == DebuggerServerModes.MULTI: - gdbserver_cmd_start = self._target_tcp_port_check_cmd() + " && exit 0; " + gdbserver_cmd_start = "mkdir -p %s; " % self._gdbserver_tmp_dir(server_mode) + gdbserver_cmd_start += "%s --once :%s %s & " % ( + self.debugger_cross.debug_server_path, port, self.binary.binary_path) + gdbserver_cmd_start += "_gdbserver_pid=\\$!; " + gdbserver_cmd_start += "echo \\$_gdbserver_pid > %s; " % self._gdbserver_pid_file(server_mode) + gdbserver_cmd_start += self._target_wait_for_tcp_port_cmd( + "gdbserver_pid", mode=server_mode) + " " + gdbserver_cmd_start += "echo %s; wait \\$_gdbserver_pid" % ( + self.get_debug_server_ready_marker(port)) + elif server_mode in (DebuggerServerModes.ATTACH, DebuggerServerModes.MULTI): + # Both modes run a persistent server speaking the extended-remote + # protocol. They differ on the client side only: ATTACH attaches to + # a process that is already running on the target. + gdbserver_cmd_start = self._target_tcp_port_check_cmd(server_mode) + " && exit 0; " gdbserver_cmd_start += "mkdir -p %s; " % self._gdbserver_tmp_dir(server_mode) gdbserver_cmd_start += "%s --multi :%s > %s 2>&1 & _gdbserver_pid=\\$!; " % ( - self.debugger_cross.debug_server_path, self.debug_server_port, + self.debugger_cross.debug_server_path, port, self._gdbserver_log_file(server_mode)) gdbserver_cmd_start += "echo \\$_gdbserver_pid > %s; " % self._gdbserver_pid_file(server_mode) - gdbserver_cmd_start += self._target_wait_for_tcp_port_cmd("gdbserver_pid") + gdbserver_cmd_start += self._target_wait_for_tcp_port_cmd( + "gdbserver_pid", mode=server_mode) else: raise DevtoolError("Unsupported gdbserver mode: %s" % server_mode) return "\"/bin/sh -c '" + gdbserver_cmd_start + "'\"" - def _target_kill_cmd(self): - """SSH command to kill gdbserver on the target device.""" - return "\"kill \\$(pgrep -o -f 'gdbserver --attach :%s') 2>/dev/null || true\"" % self.debug_server_port + def _target_stop_cmd(self, server_mode): + """SSH command to stop gdbserver on the target device. + + Stopping is based on the PID file written by the start command. Other + debug sessions run their own gdbserver on the target, so anything + matching by process name would hit them as well. + """ + pid_file = self._gdbserver_pid_file(server_mode) + gdbserver_cmd_stop = "if test -f %s; then _gdbserver_pid=\\$(cat %s); " % ( + pid_file, pid_file) + gdbserver_cmd_stop += "kill \\$_gdbserver_pid 2>/dev/null; " + gdbserver_cmd_stop += self._target_wait_for_process_exit_cmd( + "gdbserver_pid") + gdbserver_cmd_stop += " fi; rm -rf %s" % self._gdbserver_tmp_dir(server_mode) + return "\"/bin/sh -c '" + gdbserver_cmd_stop + "'\"" class LldbServerConfig(DebuggerCrossConfig): @@ -244,10 +282,7 @@ class LldbServerConfig(DebuggerCrossConfig): # lldb-server 21.x and the remote lldb client connects from the host. # Start from /tmp because lldb-server creates temp files in its cwd and # the SSH default cwd (/home/root) may not exist on a minimal image. - if mode == DebuggerServerModes.ONCE: - cmd = "cd /tmp && %s platform --one-shot --server --listen *:%s" % ( - lldb_server, self.debug_server_port) - elif mode == DebuggerServerModes.MULTI: + if mode == DebuggerServerModes.MULTI: pid_file = self._lldb_server_pid_file(mode) tmp_dir = self._lldb_server_tmp_dir(mode) log_file = self._lldb_server_log_file(mode) @@ -261,11 +296,11 @@ class LldbServerConfig(DebuggerCrossConfig): "lldb_server_pid", log_file) else: raise DevtoolError( - "lldb-server does not support mode %s " - "(ATTACH is handled client-side with 'process attach')" % mode) + "lldb-server only supports MULTI mode; " + "ATTACH is handled client-side with 'process attach': %s" % mode) return "\"/bin/sh -c '" + cmd + "'\"" - def _target_kill_cmd(self): + def _target_stop_cmd(self, server_mode): """SSH command to stop a MULTI-mode lldb-server on the target.""" pid_file = self._lldb_server_pid_file(DebuggerServerModes.MULTI) tmp_dir = self._lldb_server_tmp_dir(DebuggerServerModes.MULTI) diff --git a/scripts/lib/devtool/ide_plugins/ide_code.py b/scripts/lib/devtool/ide_plugins/ide_code.py index d190858d44..8ccc611c98 100644 --- a/scripts/lib/devtool/ide_plugins/ide_code.py +++ b/scripts/lib/devtool/ide_plugins/ide_code.py @@ -32,14 +32,12 @@ class GdbCrossConfigVSCode(GdbCrossConfig): self._target_start_cmd(mode) ] - def target_ssh_gdbserver_kill_args(self): - """Get the ssh command arguments to kill gdbserver on the target device - - returns something like: - ['-p', '2222', 'root@target', '"kill $(pgrep -o -f \'gdbserver --attach :1234\') 2>/dev/null || true"'] - """ + def target_ssh_gdbserver_stop_args(self, mode=None): + """Get the ssh command arguments to stop gdbserver on the target device""" + if mode is None: + mode = self.default_mode return self._target_ssh_args() + [ - self._target_kill_cmd() + self._target_stop_cmd(mode) ] @@ -59,10 +57,12 @@ class LldbServerConfigVSCode(LldbServerConfig): self._target_start_cmd(mode) ] - def target_ssh_gdbserver_kill_args(self): + def target_ssh_gdbserver_stop_args(self, mode=None): """SSH argument list to stop a running MULTI-mode lldb-server""" + if mode is None: + mode = self.default_mode return self._target_ssh_args() + [ - self._target_kill_cmd() + self._target_stop_cmd(mode) ] class IdeVSCode(IdeBase): @@ -338,25 +338,43 @@ class IdeVSCode(IdeBase): return self._vscode_launch_bin_dbg_lldb(cross_debug_config, server_mode) return self._vscode_launch_bin_dbg_gdb(cross_debug_config, server_mode) + @staticmethod + def _stop_task_label(cross_debug_config, server_mode): + return "stop_%s_%s" % (cross_debug_config.DEBUG_SERVER_NAME, + cross_debug_config.id_pretty_mode(server_mode)) + def _vscode_launch_bin_dbg_gdb(self, cross_debug_config, server_mode): """Generate a cppdbg (GDB) launch configuration entry for launch.json.""" modified_recipe = cross_debug_config.modified_recipe + is_attach = server_mode == DebuggerServerModes.ATTACH + launch_config = { "name": cross_debug_config.id_pretty_mode(server_mode), "type": "cppdbg", - "request": "launch", + "request": "attach" if is_attach else "launch", "program": cross_debug_config.binary.binary_host_path, - "stopAtEntry": True, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": False, "MIMode": "gdb", "preLaunchTask": cross_debug_config.id_pretty_mode(server_mode), "miDebuggerPath": modified_recipe.debugger_cross.gdb, - "miDebuggerServerAddress": "%s:%d" % (modified_recipe.debugger_cross.host, cross_debug_config.debug_server_port) + "miDebuggerServerAddress": "%s:%d" % (modified_recipe.debugger_cross.host, cross_debug_config.port(server_mode)) } + if is_attach: + # Without useExtendedRemote, cppdbg rejects attaching to a remote + # target. It also makes cppdbg offer a picker listing the processes + # running on the target, so the PID does not have to be known when + # this configuration is generated. Stopping the session detaches + # from the process instead of killing it. + launch_config["useExtendedRemote"] = True + else: + # cwd, environment and externalConsole configure the process the + # debugger starts, they are not part of the attach schema. + launch_config["cwd"] = "${workspaceFolder}" + launch_config["environment"] = [] + launch_config["externalConsole"] = False + launch_config["stopAtEntry"] = True + # Search for header files in recipe-sysroot. src_file_map = { "/usr/include": os.path.join(modified_recipe.recipe_sysroot, "usr", "include") @@ -415,10 +433,10 @@ class IdeVSCode(IdeBase): launch_config['sourceFileMap'] = src_file_map launch_config['setupCommands'] = setup_commands - # Add postDebugTask for attach mode to clean up gdbserver - if server_mode == DebuggerServerModes.ATTACH: - kill_task_label = "kill_gdbserver_" + cross_debug_config.id_pretty_mode(server_mode) - launch_config["postDebugTask"] = kill_task_label + if is_attach: + # The extended-remote server outlives the debug session + launch_config["postDebugTask"] = self._stop_task_label( + cross_debug_config, server_mode) return launch_config @@ -574,8 +592,9 @@ class IdeVSCode(IdeBase): if cross_debug_config.modified_recipe is not modified_recipe: continue for server_mode in cross_debug_config.server_modes(): - if server_mode == DebuggerServerModes.MULTI: - # MULTI mode: the SSH command blocks until the port is ready + if server_mode in (DebuggerServerModes.MULTI, + DebuggerServerModes.ATTACH): + # The SSH command blocks until the port is ready # (wait loop in _target_start_cmd), so VSCode treats this as # a regular non-background task. new_task = { @@ -586,7 +605,7 @@ class IdeVSCode(IdeBase): "problemMatcher": [] } else: - # ONCE / ATTACH: gdbserver runs in the foreground for the + # ONCE: gdbserver runs in the foreground for the # whole session, so VSCode needs isBackground + a pattern # matcher to avoid waiting for the task to exit. new_task = { @@ -608,7 +627,7 @@ class IdeVSCode(IdeBase): "background": { "activeOnStart": True, "beginsPattern": ".", - "endsPattern": ".", + "endsPattern": cross_debug_config.get_debug_server_ready_marker_pattern(), } } ] @@ -621,28 +640,20 @@ class IdeVSCode(IdeBase): tasks_dict['tasks'].append(new_task) - # For attach mode, add a kill task to stop a previously running gdbserver - # This is a known issue with gdbserver --attach that it does not terminate - # after detaching. With this helper task, it is possible to: - # 1. Start debugging in attach mode - # 2. Add breakpoints, step, continue, etc. - # 3. Press the Continue button - # 4. Press the Stop button which detaches gdbserver from the debugged process - # 5. Start debugging again in attach mode - # Without this kill task, step 5 would fail because gdbserver is still running + # The extended-remote server used by attach mode keeps running + # after the debug session, launch.json refers to this task as + # postDebugTask. if server_mode == DebuggerServerModes.ATTACH: - new_task_kill_label = "kill_gdbserver_"+ cross_debug_config.id_pretty_mode(server_mode) - new_task_kill = { - "label": new_task_kill_label, + tasks_dict['tasks'].append({ + "label": self._stop_task_label(cross_debug_config, server_mode), "type": "shell", "command": cross_debug_config.debugger_cross.target_device.ssh_sshexec, - "args": cross_debug_config.target_ssh_gdbserver_kill_args(), + "args": cross_debug_config.target_ssh_gdbserver_stop_args(server_mode), "presentation": { "close": True }, "problemMatcher": [] - } - tasks_dict['tasks'].append(new_task_kill) + }) tasks_file = 'tasks.json' IdeBase.update_json_file( @@ -804,8 +815,9 @@ class IdeVSCode(IdeBase): if cross_debug_config.modified_recipe is not modified_recipe: continue for server_mode in cross_debug_config.server_modes(): - if server_mode == DebuggerServerModes.MULTI: - # MULTI mode: SSH command blocks until port is ready, treat as + if server_mode in (DebuggerServerModes.MULTI, + DebuggerServerModes.ATTACH): + # SSH command blocks until port is ready, treat as # a regular non-background task (same as vscode_tasks_cpp). new_task = { "label": cross_debug_config.id_pretty_mode(server_mode), @@ -815,7 +827,7 @@ class IdeVSCode(IdeBase): "problemMatcher": [] } else: - # ONCE / ATTACH: server runs for the whole session, needs + # ONCE: server runs for the whole session, needs # isBackground so VSCode does not wait for the task to exit. new_task = { "label": cross_debug_config.id_pretty_mode(server_mode), diff --git a/scripts/lib/devtool/ide_plugins/ide_none.py b/scripts/lib/devtool/ide_plugins/ide_none.py index a8ddc3f39f..959140cedb 100644 --- a/scripts/lib/devtool/ide_plugins/ide_none.py +++ b/scripts/lib/devtool/ide_plugins/ide_none.py @@ -21,16 +21,19 @@ class GdbCrossConfigNone(GdbCrossConfig): default_mode) def _target_gdbserver_stop_cmd(self, server_mode): - """Kill a gdbserver process""" - # This is the usual behavior: gdbserver is stopped on demand - if server_mode == DebuggerServerModes.MULTI: - gdbserver_cmd_stop = "test -f %s && kill \\$(cat %s);" % ( - self._gdbserver_pid_file(server_mode), self._gdbserver_pid_file(server_mode)) - gdbserver_cmd_stop += " rm -rf %s" % self._gdbserver_tmp_dir(server_mode) - # This is unexpected since gdbserver should terminate after each debug session - # Just kill all gdbserver instances to keep it simple - else: - gdbserver_cmd_stop = "killall gdbserver" + """Kill a gdbserver process + + Stopping is based on the PID file written by the start command. Other + debug sessions run their own gdbserver on the target, so anything + matching by process name would hit them as well. + """ + pid_file = self._gdbserver_pid_file(server_mode) + gdbserver_cmd_stop = "if test -f %s; then _gdbserver_pid=\\$(cat %s); " % ( + pid_file, pid_file) + gdbserver_cmd_stop += "kill \\$_gdbserver_pid 2>/dev/null; " + gdbserver_cmd_stop += self._target_wait_for_process_exit_cmd( + "gdbserver_pid") + gdbserver_cmd_stop += " fi; rm -rf %s" % self._gdbserver_tmp_dir(server_mode) return "\"/bin/sh -c '" + gdbserver_cmd_stop + "'\"" def _gen_gdbserver_start_script(self, server_mode=None): @@ -165,14 +168,19 @@ class LldbServerConfigNone(LldbServerConfig): return os.path.join(self.script_dir, 'lldb_' + self.id_pretty) def _target_lldb_server_stop_cmd(self, server_mode): - """SSH command to stop lldb-server on the target.""" - if server_mode == DebuggerServerModes.MULTI: - pid_file = self._lldb_server_pid_file(server_mode) - tmp_dir = self._lldb_server_tmp_dir(server_mode) - cmd = ("test -f %(pf)s && kill \\$(cat %(pf)s) 2>/dev/null; rm -rf %(td)s" - % {'pf': pid_file, 'td': tmp_dir}) - else: - cmd = "killall lldb-server 2>/dev/null || true" + """SSH command to stop lldb-server on the target. + + Stopping is based on the PID file written by the start command. Other + debug sessions run their own lldb-server on the target, so anything + matching by process name would hit them as well. + """ + pid_file = self._lldb_server_pid_file(server_mode) + tmp_dir = self._lldb_server_tmp_dir(server_mode) + cmd = "if test -f %s; then _lldb_server_pid=\\$(cat %s); " % ( + pid_file, pid_file) + cmd += "kill \\$_lldb_server_pid 2>/dev/null; " + cmd += self._target_wait_for_process_exit_cmd("lldb_server_pid") + cmd += " fi; rm -rf %s" % tmp_dir return "\"/bin/sh -c '" + cmd + "'\"" def _gen_lldb_server_start_script(self, server_mode=None): From patchwork Tue Aug 18 06:30:50 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95581 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 735CEC5DF70 for ; Tue, 18 Aug 2026 06:31:42 +0000 (UTC) Received: from mta-65-226.siemens.flowmailer.net (mta-65-226.siemens.flowmailer.net [185.136.65.226]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3326.1787034698953510541 for ; Mon, 17 Aug 2026 23:31:41 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=LQYB8Xv3; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.226, mailfrom: fm-1329275-20260818063141670c8628e100020711-xbkeel@rts-flowmailer.siemens.com) Received: by mta-65-226.siemens.flowmailer.net with ESMTPSA id 20260818063141670c8628e100020711 for ; Tue, 18 Aug 2026 08:31:41 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=XnPM01sJcrcqbV/8Mrj4GFC0YJzTxFPBrhmYW1Wrn0U=; b=LQYB8Xv3X1+/RnQL26+oCZClg7plwEyODumfQjwM1ENwlKydc4FHKF/4il4yc+7bGkLnZt Kj5/Tm/vWWQ135tbmazBJFR6HhyHc08APl6oScivHcmgRyEwueQup+X6UtjxtOiZtOsNITj0 ACLWduaplsfWMOI9ImuTS0Uu10vwyr0b9ikKEukYi0s3WgGTQSHxwtj1rSIKAkRBuf/ZY3Hk u17aQD2NFQepkIBcRhzffYqkGt9GCNOvYSRybNLlFgsQkIe9fCVrcozurw0lmMpExCuaahfZ Jifu55gsycT4voidqyi16xs6by/jdmErafTdsUQbTTOEw/+JXVO0UVOQ==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 11/14] oe-selftest: devtool ide-sdk: wait for gdbserver readiness Date: Tue, 18 Aug 2026 08:30:50 +0200 Message-ID: <20260818063120.73042-12-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:42 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243662 From: Adrian Freihofer Replace the fixed delay and process-list probe for GDB background tasks with a wait for the readiness marker generated in tasks.json. This verifies the same background-task contract VS Code uses before starting the debugger session. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 36 ++++++++++++++++++------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 35abc6a48f..461ebf5900 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -2811,6 +2811,22 @@ class RunCmdBackground: def __enter__(self): self.cmd.run() + return self + + def output(self): + return b"".join(self.cmd._output_chunks).decode( + "utf-8", errors="replace") + + def wait_for_output(self, pattern, timeout): + pattern = re.compile(pattern, re.MULTILINE) + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + if pattern.search(self.output()): + return True + if self.cmd.process.poll() is not None: + break + time.sleep(0.1) + return False def __exit__(self, exc_type, exc_val, exc_tb): self.cmd.stop() @@ -3595,16 +3611,16 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): if len(ssh_gdbserver_cmd) > 0 and ssh_gdbserver_cmd[-1].startswith('"') and ssh_gdbserver_cmd[-1].endswith('"'): ssh_gdbserver_cmd[-1] = ssh_gdbserver_cmd[-1][1:-1].replace('\\$', '$') # Remove surrounding quotes self.logger.debug(f"Starting gdbserver with command: {' '.join(ssh_gdbserver_cmd)}") - with RunCmdBackground(ssh_gdbserver_cmd, output_log=self._cmd_logger): - # Give gdbserver a moment to start - time.sleep(1) - - # Verify gdbserver is running on target and listening on expected port - result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'), output_log=self._cmd_logger) - self.assertEqual(result.status, 0, "Failed to check processes on target") - self.assertIn("gdbserver", result.output, "gdbserver should be running on target") - _, server_port = server_addr.split(':') - self.assertIn(server_port, result.output, f"gdbserver should be listening on port {server_port}") + _, server_port = server_addr.split(':') + with RunCmdBackground(ssh_gdbserver_cmd, output_log=self._cmd_logger) as gdbserver: + ready_pattern = prelaunch_task["problemMatcher"][0]["background"]["endsPattern"] + # Must exceed the target side budget (TARGET_START_RETRIES * 0.1s), + # otherwise this gives up while the target is still waiting and its + # diagnostics never make it into the failure. + self.assertTrue( + gdbserver.wait_for_output(ready_pattern, timeout=60), + "gdbserver did not report readiness on port %s:\n%s" % + (server_port, gdbserver.output())) if debug_func and debug_check_func: # Do a gdb remote session using the once configuration From patchwork Tue Aug 18 06:30:51 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95590 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id DE953C5DF8A for ; Tue, 18 Aug 2026 06:31:44 +0000 (UTC) Received: from mta-65-226.siemens.flowmailer.net (mta-65-226.siemens.flowmailer.net [185.136.65.226]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.3144.1787034701094931138 for ; Mon, 17 Aug 2026 23:31:42 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=GRtYKlER; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.226, mailfrom: fm-1329275-20260818063141b53eae15e200020757-u1hohh@rts-flowmailer.siemens.com) Received: by mta-65-226.siemens.flowmailer.net with ESMTPSA id 20260818063141b53eae15e200020757 for ; Tue, 18 Aug 2026 08:31:41 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=Db671FKAgCWzhIAjHlts1ex6ZRtEmpd5EGpJ9/oXW88=; b=GRtYKlERFy1ZQaTwOEm+uvVm5Hrjqt4WaEtCZRrpoeRxRAjkl7TTamd9rnMAeEZ+x7ujIr DqpL3Sus/SguJ8vLtuwfKj1dj0udC6Ef9db8OCY9n3J/19T0c/5z7/NSVylhgtZJtiHM0qfa vU01CShPFcjeORk/pXbFEbdPgJfX1wT51EKmbPUi8EaJmFPzmsjORExLIKWkxeZS3TjIZ5DD ZIsZ6FoOSlXGBxFVbyGaFr/EDxl6vtidniNaow8LEQHPnCBt/tKoq5gZeE9WqhJjIGNhmhPu amI9JIZLQ0qfVn7fC4lK76AsHO3DDdo7kg/3h4VJvOeuHiV0DABlg2Mw==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 12/14] oe-selftest: devtool ide-sdk: verify debugger shutdown Date: Tue, 18 Aug 2026 08:30:51 +0200 Message-ID: <20260818063120.73042-13-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:44 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243664 From: Adrian Freihofer Use the QEMU target command interface to inspect the generated ide=none server PID files and process command lines. Verify that the stop helpers remove both the tracked process and its PID file, without relying on broad ps output. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 61 +++++++++++-------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 461ebf5900..3a99a493b5 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3278,7 +3278,6 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): numbers after recompiling, to prove the breakpoints resolve via the freshly rebuilt debug info. """ - sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' gdbserver_script = os.path.join(self._workspace_scripts_dir( recipe_name), 'gdbserver_1234_usr-bin-' + example_exe + '_multi') gdb_script = os.path.join(self._workspace_scripts_dir( @@ -3288,19 +3287,18 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): r = runCmd(gdbserver_script, output_log=self._cmd_logger) self.assertEqual(r.status, 0) - # Check there is a gdbserver running - r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'), output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertIn("gdbserver ", r.output) + pid_file = '/tmp/gdbserver_1234_usr-bin-%s_multi/gdbserver.pid' % example_exe + status, output = qemu.run('cat %s' % pid_file) + self.assertEqual(status, 0) + gdbserver_pid = output.strip() + self.assertRegex(gdbserver_pid, r'^\d+$') - # Check the pid file is correct - test_cmd = "'cat /proc/$(cat /tmp/gdbserver_1234_usr-bin-" + \ - example_exe + "_multi/gdbserver.pid)/cmdline'" - r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, test_cmd), output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertIn("gdbserver", r.output) - self.assertIn("--multi", r.output) - self.assertIn("1234", r.output) + # Check the pid file identifies the expected gdbserver process + status, output = qemu.run('cat /proc/%s/cmdline' % gdbserver_pid) + self.assertEqual(status, 0) + self.assertIn("gdbserver", output) + self.assertIn("--multi", output) + self.assertIn("1234", output) # Test remote debugging works gdb_batch_cmd = " --batch " + self._gdb_debug_cpp_example( @@ -3317,10 +3315,10 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): r = runCmd(gdbserver_script + ' stop', output_log=self._cmd_logger) self.assertEqual(r.status, 0) - # Check there is no gdbserver running - r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'ps'), output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertNotIn("gdbserver ", r.output) + # The stop script waits for its recorded PID before it succeeds. + status, _ = qemu.run('test ! -d /proc/%s && test ! -e %s' % ( + gdbserver_pid, pid_file)) + self.assertEqual(status, 0) @OETestTag("runqemu") def test_devtool_ide_sdk_none_qemu(self): @@ -4489,7 +4487,6 @@ class DevtoolIdeSdkClangTests(DevtoolIdeSdkTests): lldbinit source map / debug-file-search-paths setup for the library's own debug info specifically. """ - sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' scripts_dir = self._workspace_scripts_dir(recipe_name) binary_pretty = 'usr-bin-' + example_exe lldb_server_script = os.path.join( @@ -4503,19 +4500,16 @@ class DevtoolIdeSdkClangTests(DevtoolIdeSdkTests): r = runCmd(lldb_server_script, output_log=self._cmd_logger) self.assertEqual(r.status, 0) - # Verify lldb-server is running on the target - r = runCmd('ssh %s root@%s ps' % (sshargs, qemu.ip), - output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertIn('lldb-server', r.output) + pid_file = '/tmp/lldb_server_1234_%s_multi/lldb_server.pid' % binary_pretty + status, output = qemu.run('cat %s' % pid_file) + self.assertEqual(status, 0) + lldb_server_pid = output.strip() + self.assertRegex(lldb_server_pid, r'^\d+$') # Verify the pid file points at the running lldb-server process - pid_file = '/tmp/lldb_server_1234_%s_multi/lldb_server.pid' % binary_pretty - test_cmd = "'cat /proc/$(cat %s)/cmdline'" % pid_file - r = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, test_cmd), - output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertIn('lldb-server', r.output) + status, output = qemu.run('cat /proc/%s/cmdline' % lldb_server_pid) + self.assertEqual(status, 0) + self.assertIn('lldb-server', output) # Run an lldb batch session covering the executable, library and # header breakpoints, then continue to completion @@ -4530,11 +4524,10 @@ class DevtoolIdeSdkClangTests(DevtoolIdeSdkTests): r = runCmd(lldb_server_script + ' stop', output_log=self._cmd_logger) self.assertEqual(r.status, 0) - # Verify lldb-server is no longer running - r = runCmd('ssh %s root@%s ps' % (sshargs, qemu.ip), - output_log=self._cmd_logger) - self.assertEqual(r.status, 0) - self.assertNotIn('lldb-server', r.output) + # The stop script waits for its recorded PID before it succeeds. + status, _ = qemu.run('test ! -d /proc/%s && test ! -e %s' % ( + lldb_server_pid, pid_file)) + self.assertEqual(status, 0) @OETestTag("runqemu") def test_devtool_ide_sdk_none_cmake(self): From patchwork Tue Aug 18 06:30:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95586 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id F25F2C5DF8F for ; Tue, 18 Aug 2026 06:31:44 +0000 (UTC) Received: from mta-65-225.siemens.flowmailer.net (mta-65-225.siemens.flowmailer.net [185.136.65.225]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3327.1787034699017815985 for ; Mon, 17 Aug 2026 23:31:43 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=iCRFqYfY; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.225, mailfrom: fm-1329275-2026081806314222fed1677f000207db-tnc3u6@rts-flowmailer.siemens.com) Received: by mta-65-225.siemens.flowmailer.net with ESMTPSA id 2026081806314222fed1677f000207db for ; Tue, 18 Aug 2026 08:31:42 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=u9wQBP+OXdbtkXle/jhnAZocO808uBTHSDuTBbEAOqA=; b=iCRFqYfYT9To0DYfJ9Dy7AGHEslcrZDrqvJiLPzY4Fw+tuqsE63tuhVzXK8DMK5Indfezv 1/6bkv17TLZ3XCQ90SSjgLxYlMVJxYiudUqsXkQLMeaiMDTUWR3MYXARFChat7RAorAyfJpq ZW3rQoHWH4Wb2UyPfjH4gJC7oHu+wtCUurBmMD2qCv7JWOpTPKI0jD6AjYd6wiSSi/LZrpjx hAYvqCN0S2h8/dfmuchsRGvuqRzqFtiJjpAS0ZJCMDsxjtyULoCQ7zL71EOsZBBm56qLWtyn pN4kflKng9ayiCLCKb6D1ECADE9oaYZ6aZ3iBaNDPfaD5zYM3M2RPjCQ==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 13/14] oe-selftest: devtool ide-sdk: use qemu.run for target checks Date: Tue, 18 Aug 2026 08:30:52 +0200 Message-ID: <20260818063120.73042-14-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:44 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243665 From: Adrian Freihofer Use QEMU target commands instead of spawning a separate ssh process for simple target-side executable checks in the ide=code selftest helper. This keeps the test consistent with the rest of the qemu-based cleanup and avoids duplicating SSH option handling in the test itself. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index 3a99a493b5..ab846b8dc3 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3593,13 +3593,10 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): self.assertEqual(task_command, "ssh", f"Task '{prelaunch_task_name}' should use ssh command") self.assertTrue(len(task_args) >= 2, f"Task '{prelaunch_task_name}' should have at least 2 args (ssh options and remote command)") - sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' - result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'test -x /usr/bin/gdbserver'), - output_log=self._cmd_logger) - self.assertEqual(result.status, 0, "gdbserver should be installed on target") - result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, 'test -x ' + os.path.join('/usr/bin', example_exe)), - output_log=self._cmd_logger) - self.assertEqual(result.status, 0, "Example binary should be installed on target") + status, _ = qemu.run('test -x /usr/bin/gdbserver') + self.assertEqual(status, 0, "gdbserver should be installed on target") + status, _ = qemu.run('test -x ' + os.path.join('/usr/bin', example_exe)) + self.assertEqual(status, 0, "Example binary should be installed on target") # Start gdbserver on target using the task command (keep the ssh connection open while debugging) ssh_gdbserver_cmd = [task_command] + task_args From patchwork Tue Aug 18 06:30:53 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AdrianF X-Patchwork-Id: 95588 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id CBDBCC5DF8C for ; Tue, 18 Aug 2026 06:31:44 +0000 (UTC) Received: from mta-65-226.siemens.flowmailer.net (mta-65-226.siemens.flowmailer.net [185.136.65.226]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.3326.1787034698953510541 for ; Mon, 17 Aug 2026 23:31:43 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=adrian.freihofer@siemens.com header.s=fm2 header.b=c5M3HrR5; spf=pass (domain: rts-flowmailer.siemens.com, ip: 185.136.65.226, mailfrom: fm-1329275-202608180631424f892a4af3000207c9-h3ydxz@rts-flowmailer.siemens.com) Received: by mta-65-226.siemens.flowmailer.net with ESMTPSA id 202608180631424f892a4af3000207c9 for ; Tue, 18 Aug 2026 08:31:42 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=fm2; d=siemens.com; i=adrian.freihofer@siemens.com; h=Date:From:Subject:To:Message-ID:MIME-Version:Content-Type:Content-Transfer-Encoding:Cc:References:In-Reply-To; bh=Dfo9o014X+KpMqKCSfHrN1I4hTVkrP6APLZXbRBwNms=; b=c5M3HrR5rYBVn+dOwrBvhM3LWsYK9tvG8C42TJY4KlHWiWO0IPqlXdfhIrcL5NOO7xqbC9 qVVXmZSzhCFjOn3UP3+xGGFDED70RK3LLIXC/4oslaDB73ZCJ9V8hWKfLZ5g+1vNizOgGyC8 FmlDVRo061DyoV7G7EBSD2+Fqu1erxdqSsUpIIU18bGrYQ0Y/x+E3+Q+hCSkutedI2IHww7P IW7A1akQ61v3SiqyHNrMdxZfGohCyLaHWntNaR9DxMD6pk5xFMzbbYP2KfnEeNQ5fm/q5CIr datCXkarWfgZi8pBwT9XwXGjFN2V9EeBfkmMsAKd5Bb2HxBVAglGpWzw==; From: AdrianF To: openembedded-core@lists.openembedded.org Cc: Adrian Freihofer Subject: [PATCH v2 14/14] oe-selftest: devtool ide-sdk: verify attach debug workflow Date: Tue, 18 Aug 2026 08:30:53 +0200 Message-ID: <20260818063120.73042-15-adrian.freihofer@siemens.com> In-Reply-To: <20260818063120.73042-1-adrian.freihofer@siemens.com> References: <20260818063120.73042-1-adrian.freihofer@siemens.com> MIME-Version: 1.0 X-Flowmailer-Platform: Siemens Feedback-ID: 519:519-1329275:519-21489:flowmailer List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 18 Aug 2026 06:31:44 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/243666 From: Adrian Freihofer Add attach-mode debug verification in ide=code selftests and document the intended execution flow: - Verify attach postDebugTask labels against tasks.json. - Run attach checks before once checks to avoid Build-ID mismatch from once/deploy side effects on the still-running service process. - In attach mode, set a temporary breakpoint on sleep(), continue once to hit it, then detach immediately. This verifies debugger control flow against the already-running loop without fragile inferior expression evaluation. Signed-off-by: Adrian Freihofer --- meta/lib/oeqa/selftest/cases/devtool.py | 152 +++++++++++++++++++----- 1 file changed, 119 insertions(+), 33 deletions(-) diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index ab846b8dc3..ef9ebf0f30 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py @@ -3399,10 +3399,19 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): def _verify_launch_json(self, tempdir): """Verify the launch.json file created is valid and contains proper debug configurations""" launch_json_path = os.path.join(tempdir, '.vscode', 'launch.json') + tasks_json_path = os.path.join(tempdir, '.vscode', 'tasks.json') self.assertTrue(os.path.exists(launch_json_path), "launch.json file should exist") + self.assertTrue(os.path.exists(tasks_json_path), "tasks.json file should exist") with open(launch_json_path) as launch_j: launch_d = json.load(launch_j) + with open(tasks_json_path) as tasks_j: + tasks_d = json.load(tasks_j) + task_labels = { + task.get("label") + for task in tasks_d.get("tasks", []) + if task.get("label") + } self.assertIn("configurations", launch_d) configurations = launch_d["configurations"] @@ -3480,6 +3489,8 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): self.assertIn("_attach", task, f"attach configuration '{config_name}' should have attach preLaunchTask") # Verify postDebugTask exists for attach configurations (kill gdbserver) self.assertIn("postDebugTask", config, f"attach configuration '{config_name}' should have postDebugTask") + self.assertIn(config["postDebugTask"], task_labels, + f"attach configuration '{config_name}' postDebugTask should exist in tasks.json") # Categorize configurations config_name = config["name"] @@ -3495,34 +3506,11 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): "Each debug configuration should use a distinct server address") def _verify_launch_json_debugging(self, tempdir, qemu, example_exe): - """Verify remote debugging and deployment works using launch.json configurations + """Verify launch.json debugging configs for one target executable. - This method tests the VSCode debug configurations by: - 1. Starting gdbserver on target using tasks.json commands - 2. Running gdb debugging session with batch commands - $BUILDDIR/tmp/work/x86_64-linux/gdb-cross-x86_64/16.3/recipe-sysroot-native/usr/bin/x86_64-poky-linux/x86_64-poky-linux-gdb --batch \ - -ex 'set sysroot $BUILDDIR/tmp/work/x86-64-v3-poky-linux/cmake-example/1.0/image' \ - -ex 'set substitute-path /usr/include $BUILDDIR/tmp/work/x86-64-v3-poky-linux/cmake-example/1.0/recipe-sysroot/usr/include' \ - -ex 'set substitute-path /usr/src/debug/cmake-example/1.0 $BUILDDIR/workspace/sources/cmake-example' \ - -ex 'set substitute-path /usr/src/debug $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/usr/src/debug' \ - -ex 'set solib-search-path $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/lib/.debug:\ - $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/usr/lib/.debug:\ - $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/usr/lib/debug:\ - $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/lib:\ - $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs-dbg/usr/lib:\ - $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs/lib:\ - $BUILDDIR/tmp/work/qemux86_64-poky-linux/oe-selftest-image/1.0/rootfs/usr/lib' \ - -ex 'file $BUILDDIR/tmp/work/x86-64-v3-poky-linux/cmake-example/1.0/image/usr/bin/cmake-example' \ - -ex 'target remote 192.168.7.2:1234' \ - -ex 'break main' \ - -ex 'continue' \ - -ex 'break CppExample::print_json()' \ - -ex 'continue' \ - -ex 'print CppExample::test_string.compare("cpp-example-lib Magic: 123456789")' \ - -ex 'print CppExample::test_string.compare("cpp-example-lib Magic: 123456789aaa")' \ - -ex 'list cpp-example-lib.hpp:15,15' \ - -ex 'continue' - 3. Verifying debug output and stopping gdbserver + Runs attach-mode first against the already-running service process, + then validates the once configuration. This ordering avoids Build-ID + mismatch from once/deploy side effects before attach is tested. """ with open(os.path.join(tempdir, '.vscode', 'launch.json')) as launch_j: launch_d = json.load(launch_j) @@ -3538,18 +3526,116 @@ class DevtoolIdeSdkGccTests(DevtoolIdeSdkTests): # as the recipe name (e.g. meson-example installs a binary named # "mesonex"). once_config_count = 0 + attach_config_count = 0 + + # Run attach first. The attach test targets the already-running + # service process in the image; running a once configuration first can + # deploy/update binaries and trigger Build-ID mismatch warnings against + # that still-running process. + for config in configurations: + if f"usr-bin-{example_exe}_attach" in config["name"]: + attach_config_count += 1 + self._verify_launch_config_attach(tempdir, config, qemu, example_exe) + for config in configurations: if f"usr-bin-{example_exe}_once" in config["name"]: once_config_count += 1 self._verify_launch_config(tempdir, config, tasks, qemu, example_exe, self._gdb_debug_cpp_example, self._gdb_debug_cpp_example_check) - # It works but is not 100% reliable in VSCode - # This one: https://github.com/microsoft/vscode-cpptools/issues/4243 ? - # elif f"usr-bin-{example_exe}_attach" in config["name"] - # self._verify_launch_config(tempdir, config, tasks, qemu, example_exe) - else: - continue self.assertEqual(once_config_count, 1, f"Should have one once configuration, found: {once_config_count}") + self.assertEqual(attach_config_count, 1, f"Should have one attach configuration, found: {attach_config_count}") + + def _verify_launch_config_attach(self, tempdir, launch_config, qemu, example_exe): + """Verify attach-mode debugging by exercising debugger control flow. + + The attached service runs an endless loop, and each loop iteration + executes sleep(). We break on sleep() to prove that attach-mode gdb is + controlling the already-running process at a point that is expected to + be reached repeatedly during normal service execution, then detach. + """ + self.assertEqual(launch_config.get("request"), "attach") + + tasks_path = os.path.join(tempdir, '.vscode', 'tasks.json') + self.assertExists(tasks_path, 'tasks.json not found at %s' % tasks_path) + with open(tasks_path) as tasks_j: + tasks_d = json.load(tasks_j) + task_by_label = { + task.get('label'): task + for task in tasks_d.get('tasks', []) + if task.get('label') + } + + debugger_path = launch_config["miDebuggerPath"] + server_addr = launch_config["miDebuggerServerAddress"] + prelaunch_task_name = launch_config["preLaunchTask"] + post_debug_task_name = launch_config.get("postDebugTask") + program = launch_config["program"] + additional_so_lib_search_path = launch_config["additionalSOLibSearchPath"] + source_file_map = launch_config["sourceFileMap"] + setup_commands = launch_config["setupCommands"] + + self.assertTrue(post_debug_task_name, + "attach configuration should define postDebugTask") + self.assertIn(prelaunch_task_name, task_by_label) + self.assertIn(post_debug_task_name, task_by_label) + + status, _ = qemu.run('test -x /usr/bin/gdbserver') + self.assertEqual(status, 0, "gdbserver should be installed on target") + status, _ = qemu.run('test -x ' + os.path.join('/usr/bin', example_exe)) + self.assertEqual(status, 0, "Example binary should be installed on target") + + # The attach preLaunchTask has no dependsOn (it attaches to an already + # running service instead of building/deploying), so it can be run + # directly without a dependency chain runner. + prelaunch_task = task_by_label[prelaunch_task_name] + prelaunch_cmd = [prelaunch_task["command"]] + [str(arg) + for arg in prelaunch_task.get("args", [])] + # The tasks.json command is shell-quoted and escapes '$' for an + # intermediate shell, so undo that before passing argv to subprocess. + if prelaunch_cmd[-1].startswith('"') and prelaunch_cmd[-1].endswith('"'): + prelaunch_cmd[-1] = prelaunch_cmd[-1][1:-1].replace('\\$', '$') + runCmd(prelaunch_cmd, output_log=self._cmd_logger) + + # Attach to the service process currently running on the target. + status, output = qemu.run("pgrep '^%s$'" % example_exe) + self.assertEqual(status, 0, msg="%s service not running: %s" % + (example_exe, output)) + service_pid = output.strip() + self.assertRegex(service_pid, r'^\d+$') + + gdb_batch_cmd = debugger_path + " --batch" + for setup_command in setup_commands: + setup_cmd = setup_command["text"].strip() + if setup_cmd.startswith("-"): + continue + gdb_batch_cmd += ' -ex ' + shlex.quote(setup_cmd) + for k, v in source_file_map.items(): + gdb_batch_cmd += " -ex 'set substitute-path %s %s'" % (k, v.replace("${workspaceFolder}", tempdir)) + gdb_batch_cmd += " -ex 'set solib-search-path %s'" % additional_so_lib_search_path + gdb_batch_cmd += " -ex 'file %s'" % program + gdb_batch_cmd += " -ex 'target extended-remote %s'" % server_addr + gdb_batch_cmd += " -ex 'attach %s'" % service_pid + gdb_batch_cmd += " -ex 'set breakpoint pending on'" + gdb_batch_cmd += " -ex 'tbreak sleep'" + gdb_batch_cmd += " -ex 'continue'" + gdb_batch_cmd += " -ex 'detach'" + self.logger.debug(f"Starting attach gdb session with command: {gdb_batch_cmd}") + + r = runCmd(gdb_batch_cmd, output_log=self._cmd_logger) + self.assertEqual(r.status, 0) + self.assertIn("sleep", r.output) + + # Stop the persistent attach server and verify the service survives. + post_debug_task = task_by_label[post_debug_task_name] + post_debug_cmd = [post_debug_task["command"]] + [str(arg) + for arg in post_debug_task.get("args", [])] + if post_debug_cmd[-1].startswith('"') and post_debug_cmd[-1].endswith('"'): + post_debug_cmd[-1] = post_debug_cmd[-1][1:-1].replace('\\$', '$') + runCmd(post_debug_cmd, output_log=self._cmd_logger) + + status, output = qemu.run("pgrep '^%s$'" % example_exe) + self.assertEqual(status, 0, msg="%s service should still be running after attach stop: %s" % + (example_exe, output)) def _verify_launch_config(self, tempdir, launch_config, tasks, qemu, example_exe, debug_func=None, debug_check_func=None): self.assertIsNotNone(launch_config, "Should have at least one launch debug configuration")